shithub: libgraphics

Download patch

ref: 56e8e3de24e7fea36f165b653a8efc8c38145d4c
parent: d0a7561dd20847504b2449e17fd791f6c2800c84
author: rodri <rgl@antares-labs.eu>
date: Sat Aug 17 08:17:46 EDT 2024

unify drawing routines and add clipped fb drawing support.

now changing the viewport offset will correctly show the
portion of the framebuffer that's visible, including
support for upscaled views.

--- a/camera.c
+++ b/camera.c
@@ -114,6 +114,10 @@
 
 	c = newcamera();
 	c->view = mkviewport(vr);
+	if(c->view == nil){
+		werrstr("mkviewport: %r");
+		return nil;
+	}
 	c->rctl = r;
 	configcamera(c, p, fov, n, f);
 	return c;
--- a/fb.c
+++ b/fb.c
@@ -93,19 +93,8 @@
 //}
 
 static void
-framebufctl_draw(Framebufctl *ctl, Image *dst, Point off)
+upscaledraw(Framebufctl *ctl, Image *dst, Point off, Point scale)
 {
-	Framebuf *fb;
-
-	qlock(ctl);
-	fb = ctl->getfb(ctl);
-	loadimage(dst, rectaddpt(fb->r, addpt(dst->r.min, off)), (uchar*)fb->cb, Dx(fb->r)*Dy(fb->r)*4);
-	qunlock(ctl);
-}
-
-static void
-framebufctl_upscaledraw(Framebufctl *ctl, Image *dst, Point off, Point scale)
-{
 	void (*filter)(ulong*, Framebuf*, Point);
 	Framebuf *fb;
 	Rectangle blkr;
@@ -143,18 +132,37 @@
 }
 
 static void
-framebufctl_memdraw(Framebufctl *ctl, Memimage *dst, Point off)
+framebufctl_draw(Framebufctl *ctl, Image *dst, Point off, Point scale)
 {
 	Framebuf *fb;
+	Rectangle sr, dr;
+	ulong *cb;
+	int y;
 
+	if(scale.x > 1 || scale.y > 1){
+		upscaledraw(ctl, dst, off, scale);
+		return;
+	}
+
 	qlock(ctl);
 	fb = ctl->getfb(ctl);
-	loadmemimage(dst, rectaddpt(fb->r, addpt(dst->r.min, off)), (uchar*)fb->cb, Dx(fb->r)*Dy(fb->r)*4);
+	sr = rectaddpt(fb->r, off);
+	dr = rectsubpt(dst->r, dst->r.min);
+	if(rectinrect(sr, dr))
+		loadimage(dst, rectaddpt(sr, dst->r.min), (uchar*)fb->cb, Dx(fb->r)*Dy(fb->r)*4);
+	else if(rectclip(&sr, dr)){
+		cb = emalloc(Dx(sr)*Dy(sr)*4);
+		/* TODO there must be a cleaner way to do this */
+		for(y = sr.min.y; y < sr.max.y; y++)
+			memmove(&cb[(y - sr.min.y)*Dx(sr)], &fb->cb[max(y - off.y,0)*Dx(fb->r) + max(-off.x,0)], Dx(sr)*4);
+		loadimage(dst, rectaddpt(sr, dst->r.min), (uchar*)cb, Dx(sr)*Dy(sr)*4);
+		free(cb);
+	}
 	qunlock(ctl);
 }
 
 static void
-framebufctl_upscalememdraw(Framebufctl *ctl, Memimage *dst, Point off, Point scale)
+upscalememdraw(Framebufctl *ctl, Memimage *dst, Point off, Point scale)
 {
 	void (*filter)(ulong*, Framebuf*, Point);
 	Framebuf *fb;
@@ -193,6 +201,36 @@
 }
 
 static void
+framebufctl_memdraw(Framebufctl *ctl, Memimage *dst, Point off, Point scale)
+{
+	Framebuf *fb;
+	Rectangle sr, dr;
+	ulong *cb;
+	int y;
+
+	if(scale.x > 1 || scale.y > 1){
+		upscalememdraw(ctl, dst, off, scale);
+		return;
+	}
+
+	qlock(ctl);
+	fb = ctl->getfb(ctl);
+	sr = rectaddpt(fb->r, off);
+	dr = rectsubpt(dst->r, dst->r.min);
+	if(rectinrect(sr, dr))
+		loadmemimage(dst, rectaddpt(sr, dst->r.min), (uchar*)fb->cb, Dx(fb->r)*Dy(fb->r)*4);
+	else if(rectclip(&sr, dr)){
+		cb = emalloc(Dx(sr)*Dy(sr)*4);
+		/* TODO there must be a cleaner way to do this */
+		for(y = sr.min.y; y < sr.max.y; y++)
+			memmove(&cb[(y - sr.min.y)*Dx(sr)], &fb->cb[max(y - off.y,0)*Dx(fb->r) + max(-off.x,0)], Dx(sr)*4);
+		loadmemimage(dst, rectaddpt(sr, dst->r.min), (uchar*)cb, Dx(sr)*Dy(sr)*4);
+		free(cb);
+	}
+	qunlock(ctl);
+}
+
+static void
 framebufctl_drawnormals(Framebufctl *ctl, Image *dst)
 {
 	Framebuf *fb;
@@ -280,9 +318,7 @@
 	fc->fb[0] = mkfb(r);
 	fc->fb[1] = mkfb(r);
 	fc->draw = framebufctl_draw;
-	fc->upscaledraw = framebufctl_upscaledraw;
 	fc->memdraw = framebufctl_memdraw;
-	fc->upscalememdraw = framebufctl_upscalememdraw;
 	fc->drawnormals = framebufctl_drawnormals;
 	fc->swap = framebufctl_swap;
 	fc->reset = framebufctl_reset;
--- a/graphics.h
+++ b/graphics.h
@@ -287,10 +287,8 @@
 	uint idx;		/* front buffer index */
 	uint upfilter;		/* upscaling filter */
 
-	void (*draw)(Framebufctl*, Image*, Point);
-	void (*upscaledraw)(Framebufctl*, Image*, Point, Point);
-	void (*memdraw)(Framebufctl*, Memimage*, Point);
-	void (*upscalememdraw)(Framebufctl*, Memimage*, Point, Point);
+	void (*draw)(Framebufctl*, Image*, Point, Point);
+	void (*memdraw)(Framebufctl*, Memimage*, Point, Point);
 	void (*drawnormals)(Framebufctl*, Image*);
 	void (*swap)(Framebufctl*);
 	void (*reset)(Framebufctl*, ulong);
--- a/viewport.c
+++ b/viewport.c
@@ -15,13 +15,10 @@
 
 	off = Pt(v->p.x, v->p.y);
 	/* no downsampling support yet */
-	scale.x = max(min(v->bx.x, Dx(dst->r)/Dx(v->r)), 1);
-	scale.y = max(min(v->by.y, Dy(dst->r)/Dy(v->r)), 1);
+	scale.x = max(v->bx.x, 1);
+	scale.y = max(v->by.y, 1);
 
-	if(scale.x > 1 || scale.y > 1)
-		v->fbctl->upscaledraw(v->fbctl, dst, off, scale);
-	else
-		v->fbctl->draw(v->fbctl, dst, off);
+	v->fbctl->draw(v->fbctl, dst, off, scale);
 }
 
 static void
@@ -31,13 +28,10 @@
 
 	off = Pt(v->p.x, v->p.y);
 	/* no downsampling support yet */
-	scale.x = max(min(v->bx.x, Dx(dst->r)/Dx(v->r)), 1);
-	scale.y = max(min(v->by.y, Dy(dst->r)/Dy(v->r)), 1);
+	scale.x = max(v->bx.x, 1);
+	scale.y = max(v->by.y, 1);
 
-	if(scale.x > 1 || scale.y > 1)
-		v->fbctl->upscalememdraw(v->fbctl, dst, off, scale);
-	else
-		v->fbctl->memdraw(v->fbctl, dst, off);
+	v->fbctl->memdraw(v->fbctl, dst, off, scale);
 }
 
 static void
@@ -77,6 +71,11 @@
 mkviewport(Rectangle r)
 {
 	Viewport *v;
+
+	if(badrect(r)){
+		werrstr("bad viewport rectangle");
+		return nil;
+	}
 
 	v = emalloc(sizeof *v);
 	v->p = Pt2(0,0,1);