ref: d464ab2c0efc986755f28403e69a75ef878a1230
parent: 76a156b76482bd1335d2838be1c87db85be76246
author: rodri <rgl@antares-labs.eu>
date: Tue Feb 17 11:51:57 EST 2026
fb: implement correct upscale(mem)draw() algorithm
--- a/fb.c
+++ b/fb.c
@@ -132,14 +132,16 @@
upscaledraw(Raster *fb, Image *dst, Point off, Point scale, uint filter)
{void (*filterfn)(ulong*, Raster*, Point, Point, ulong);
- Rectangle blkr;
+ Rectangle blkr, dr;
Point sp, dp;
Image *tmp;
- ulong *blk;
+ ulong *blk, *blkp;
+ int dx, nl;
filterfn = nil;
- blk = _emalloc(scale.x*scale.y*4);
- blkr = Rect(0,0,scale.x,scale.y);
+ dx = Dx(fb->r);
+ blk = _emalloc(scale.x*dx*scale.y*4);
+ blkr = Rect(0,0,scale.x*dx,scale.y);
tmp = allocimage(display, dst->r, RGBA32, 0, 0);
if(tmp == nil)
sysfatal("allocimage: %r");@@ -157,12 +159,32 @@
filterfn = ident_filter;
}
- ;
- for(sp.y = fb->r.min.y, dp.y = dst->r.min.y+off.y; sp.y < fb->r.max.y; sp.y++, dp.y += scale.y)
- for(sp.x = fb->r.min.x, dp.x = dst->r.min.x+off.x; sp.x < fb->r.max.x; sp.x++, dp.x += scale.x){- filterfn(blk, fb, sp, scale, scale.x);
- loadimage(tmp, rectaddpt(blkr, dp), (uchar*)blk, scale.x*scale.y*4);
+ /*
+ * Fill an entire upscaled line (blk) and then clip against
+ * the dst->r to select the data that needs to be sent.
+ *
+ * A better version could also do an initial clip to determine
+ * the scanline segments that need to be sampled in the first
+ * place.
+ */
+ blkr = rectaddpt(blkr, addpt(dst->r.min, off));
+ for(sp.y = fb->r.min.y; sp.y < fb->r.max.y; sp.y++){+ for(sp.x = fb->r.min.x, blkp = blk; sp.x < fb->r.max.x; sp.x++, blkp += scale.x){+ filterfn(blkp, fb, sp, scale, scale.x*dx);
}
+ dr = blkr;
+ if(rectclip(&dr, dst->r)){+ /* get blk local (data) point */
+ dp.x = dr.min.x > blkr.min.x? dr.min.x - blkr.min.x: 0;
+ dp.y = dr.min.y > blkr.min.y? dr.min.y - blkr.min.y: 0;
+ nl = Dy(dr);
+ dr.max.y = dr.min.y+1;
+ for(; nl-- > 0; dp.y++, dr.min.y++, dr.max.y++)
+ loadimage(tmp, dr, (uchar*)(blk + dp.y*scale.x*dx + dp.x), Dx(dr)*4);
+ }
+ blkr.min.y += scale.y;
+ blkr.max.y += scale.y;
+ }
draw(dst, dst->r, tmp, nil, tmp->r.min);
freeimage(tmp);
free(blk);
@@ -203,16 +225,16 @@
sr = rectaddpt(fb->clipr, off);
dr = rectsubpt(dst->r, dst->r.min);
if(rectclip(&sr, dr)){- tmp = allocimage(display, sr, RGBA32, 0, DNofill);
+ tmp = allocimage(display, sr, RGBA32, 0, 0);
if(tmp == nil)
sysfatal("allocimage: %r");- dr = sr;
+ dr = rectaddpt(sr, dst->r.min);
dr.max.y = dr.min.y + 1;
/* remove offset to get the actual rect within the framebuffer */
sr = rectsubpt(sr, off);
for(; sr.min.y < sr.max.y; sr.min.y++, dr.min.y++, dr.max.y++)
- loadimage(tmp, rectaddpt(dr, dst->r.min), _rasterbyteaddr(r, sr.min), Dx(dr)*4);
+ loadimage(tmp, dr, _rasterbyteaddr(r, sr.min), Dx(dr)*4);
draw(dst, rectaddpt(tmp->r, dst->r.min), tmp, nil, tmp->r.min);
freeimage(tmp);
}
@@ -224,17 +246,20 @@
upscalememdraw(Raster *fb, Memimage *dst, Point off, Point scale, uint filter)
{void (*filterfn)(ulong*, Raster*, Point, Point, ulong);
- Rectangle blkr;
+ Rectangle blkr, dr;
Point sp, dp;
Memimage *tmp;
- ulong *blk;
+ ulong *blk, *blkp;
+ int dx, nl;
filterfn = nil;
- blk = _emalloc(scale.x*scale.y*4);
- blkr = Rect(0,0,scale.x,scale.y);
+ dx = Dx(fb->r);
+ blk = _emalloc(scale.x*dx*scale.y*4);
+ blkr = Rect(0,0,scale.x*dx,scale.y);
tmp = allocmemimage(dst->r, RGBA32);
if(tmp == nil)
sysfatal("allocmemimage: %r");+ memfillcolor(tmp, 0);
switch(filter){case UFScale2x:
@@ -249,12 +274,33 @@
filterfn = ident_filter;
}
- for(sp.y = fb->r.min.y, dp.y = dst->r.min.y+off.y; sp.y < fb->r.max.y; sp.y++, dp.y += scale.y)
- for(sp.x = fb->r.min.x, dp.x = dst->r.min.x+off.x; sp.x < fb->r.max.x; sp.x++, dp.x += scale.x){- filterfn(blk, fb, sp, scale, scale.x);
- loadmemimage(tmp, rectaddpt(blkr, dp), (uchar*)blk, scale.x*scale.y*4);
+ /*
+ * Fill an entire upscaled line (blk) and then clip against
+ * the dst->r to select the data that needs to be sent.
+ *
+ * A better version could also do an initial clip to determine
+ * the scanline segments that need to be sampled in the first
+ * place.
+ */
+ blkr = rectaddpt(blkr, addpt(dst->r.min, off));
+ for(sp.y = fb->r.min.y; sp.y < fb->r.max.y; sp.y++){+ for(sp.x = fb->r.min.x, blkp = blk; sp.x < fb->r.max.x; sp.x++, blkp += scale.x){+ filterfn(blkp, fb, sp, scale, scale.x*dx);
}
- memimagedraw(dst, dst->r, tmp, tmp->r.min, nil, ZP, S);
+ dr = blkr;
+ if(rectclip(&dr, dst->r)){+ /* get blk local (data) point */
+ dp.x = dr.min.x > blkr.min.x? dr.min.x - blkr.min.x: 0;
+ dp.y = dr.min.y > blkr.min.y? dr.min.y - blkr.min.y: 0;
+ nl = Dy(dr);
+ dr.max.y = dr.min.y+1;
+ for(; nl-- > 0; dp.y++, dr.min.y++, dr.max.y++)
+ loadmemimage(tmp, dr, (uchar*)(blk + dp.y*scale.x*dx + dp.x), Dx(dr)*4);
+ }
+ blkr.min.y += scale.y;
+ blkr.max.y += scale.y;
+ }
+ memimagedraw(dst, dst->r, tmp, tmp->r.min, nil, ZP, SoverD);
freememimage(tmp);
free(blk);
}
@@ -297,10 +343,11 @@
tmp = allocmemimage(sr, RGBA32);
if(tmp == nil)
sysfatal("allocmemimage: %r");+ memfillcolor(tmp, 0);
bdata0 = tmp->data->bdata;
tmp->data->bdata = (void*)r->data;
- memimagedraw(dst, rectaddpt(sr, dst->r.min), tmp, ZP, nil, ZP, S);
+ memimagedraw(dst, rectaddpt(sr, dst->r.min), tmp, ZP, nil, ZP, SoverD);
tmp->data->bdata = bdata0;
freememimage(tmp);
}else if(rectclip(&sr, dr)){@@ -307,14 +354,15 @@
tmp = allocmemimage(sr, RGBA32);
if(tmp == nil)
sysfatal("allocmemimage: %r");+ memfillcolor(tmp, 0);
- dr = sr;
+ dr = rectaddpt(sr, dst->r.min);
dr.max.y = dr.min.y + 1;
/* remove offset to get the actual rect within the framebuffer */
sr = rectsubpt(sr, off);
for(; sr.min.y < sr.max.y; sr.min.y++, dr.min.y++, dr.max.y++)
- loadmemimage(tmp, rectaddpt(dr, dst->r.min), _rasterbyteaddr(r, sr.min), Dx(dr)*4);
- memimagedraw(dst, rectaddpt(tmp->r, dst->r.min), tmp, tmp->r.min, nil, ZP, S);
+ loadmemimage(tmp, dr, _rasterbyteaddr(r, sr.min), Dx(dr)*4);
+ memimagedraw(dst, rectaddpt(tmp->r, dst->r.min), tmp, tmp->r.min, nil, ZP, SoverD);
freememimage(tmp);
}
qunlock(ctl);
--
⑨