summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrodri <rgl@antares-labs.eu>2024-09-11 20:48:13 +0000
committerrodri <rgl@antares-labs.eu>2024-09-11 20:48:13 +0000
commit09ffa3cfc72a10e82c5c68f4010d1c518f0c3328 (patch)
treebe398761d4f406071417a0b021803d494aa90c11
parent8806aad87f7be2d2e82c7db2b9f0978246e5a747 (diff)
downloadlibgraphics-09ffa3cfc72a10e82c5c68f4010d1c518f0c3328.tar.gz
libgraphics-09ffa3cfc72a10e82c5c68f4010d1c518f0c3328.tar.bz2
libgraphics-09ffa3cfc72a10e82c5c68f4010d1c518f0c3328.zip
change the raster format to RGBA32. draw to any image format. clean up.
got rid of the bullshit dance between XRGB32 and RGBA32, now all rasters are RGBA32 and premultiply alpha before loading up an image and drawing over the destination. this lets the user compose their own scenes with correct transparency. no more mediocre clearcolor. as a consequence drawing got slower, but if i get the turbo drawing pool working properly that should go away. textures also take alpha pre-multiplied channels into account, dividing them when sampling, so loading transparent textures will show the correct colors.
-rw-r--r--camera.c2
-rw-r--r--color.c40
-rw-r--r--fb.c102
-rw-r--r--graphics.h15
-rw-r--r--render.c26
-rw-r--r--texture.c7
-rw-r--r--util.c42
7 files changed, 181 insertions, 53 deletions
diff --git a/camera.c b/camera.c
index bd6ccb5..2f7daf7 100644
--- a/camera.c
+++ b/camera.c
@@ -241,7 +241,7 @@ shootcamera(Camera *c, Shadertab *s)
job->shaders = s;
job->donec = chancreate(sizeof(void*), 0);
- fbctl->reset(fbctl, c->clearcolor);
+ fbctl->reset(fbctl);
t0 = nanosec();
sendp(c->rctl->jobq, job);
recvp(job->donec);
diff --git a/color.c b/color.c
index fdbd0a0..6a6023d 100644
--- a/color.c
+++ b/color.c
@@ -8,6 +8,39 @@
#include "graphics.h"
#include "internal.h"
+ulong
+col2ul(Color c)
+{
+ uchar cbuf[4];
+
+ cbuf[0] = fclamp(c.a, 0, 1)*0xFF;
+ cbuf[1] = fclamp(c.b, 0, 1)*0xFF;
+ cbuf[2] = fclamp(c.g, 0, 1)*0xFF;
+ cbuf[3] = fclamp(c.r, 0, 1)*0xFF;
+ return cbuf[3]<<24 | cbuf[2]<<16 | cbuf[1]<<8 | cbuf[0];
+}
+
+Color
+ul2col(ulong l)
+{
+ Color c;
+
+ c.a = (l & 0xff)/255.0;
+ c.b = (l>>8 & 0xff)/255.0;
+ c.g = (l>>16 & 0xff)/255.0;
+ c.r = (l>>24 & 0xff)/255.0;
+ return c;
+}
+
+int
+hasalpha(ulong chan)
+{
+ for(; chan; chan >>= 8)
+ if(TYPE(chan) == CAlpha)
+ return 1;
+ return 0;
+}
+
/*
* see also “The Importance of Being Linear”, Gritz and d'Eon, GPU Gems 3, Ch. 24, December 2007
*/
@@ -141,13 +174,6 @@ linear2srgb(Color c)
return c;
}
-ulong
-rgba2xrgb(ulong c)
-{
- return (c & 0xFF)<<24|(c>>24 & 0xFF)<<16|
- (c>>16 & 0xFF)<<8|(c>>8 & 0xFF);
-}
-
/* tone mapping */
diff --git a/fb.c b/fb.c
index ec1c056..7a11571 100644
--- a/fb.c
+++ b/fb.c
@@ -95,6 +95,23 @@ rasterconvF2C(Raster *dst, Raster *src)
}
static void
+premulalpha(Raster *r)
+{
+ Color c;
+ ulong *p, len;
+
+ len = Dx(r->r)*Dy(r->r);
+ p = r->data;
+ while(len--){
+ c = ul2col(*p);
+ c.r *= c.a;
+ c.g *= c.a;
+ c.b *= c.a;
+ *p++ = col2ul(c);
+ }
+}
+
+static void
fb_createraster(Framebuf *fb, char *name, ulong chan)
{
Raster *r;
@@ -135,11 +152,15 @@ upscaledraw(Raster *fb, Image *dst, Point off, Point scale, uint filter)
void (*filterfn)(ulong*, Raster*, Point);
Rectangle blkr;
Point sp, dp;
+ Image *tmp;
ulong *blk;
filterfn = nil;
blk = emalloc(scale.x*scale.y*4);
blkr = Rect(0,0,scale.x,scale.y);
+ tmp = allocimage(display, dst->r, RGBA32, 0, DNofill);
+ if(tmp == nil)
+ sysfatal("allocimage: %r");
switch(filter){
case UFScale2x:
@@ -158,8 +179,10 @@ upscaledraw(Raster *fb, Image *dst, Point off, Point scale, uint filter)
filterfn(blk, fb, sp);
else
memsetl(blk, getpixel(fb, sp), scale.x*scale.y);
- loadimage(dst, rectaddpt(blkr, dp), (uchar*)blk, scale.x*scale.y*4);
+ loadimage(tmp, rectaddpt(blkr, dp), (uchar*)blk, scale.x*scale.y*4);
}
+ draw(dst, dst->r, tmp, nil, tmp->r.min);
+ freeimage(tmp);
free(blk);
}
@@ -186,6 +209,7 @@ framebufctl_draw(Framebufctl *ctl, Image *dst, char *name, Point off, Point scal
Framebuf *fb;
Raster *r, *r2;
Rectangle sr, dr;
+ Image *tmp;
qlock(ctl);
fb = ctl->getfb(ctl);
@@ -203,10 +227,18 @@ framebufctl_draw(Framebufctl *ctl, Image *dst, char *name, Point off, Point scal
r = r2;
}
+ /* this means the raster is a color one, so duplicate it */
+ if(r2 == nil){
+ r2 = allocraster(nil, r->r, COLOR32);
+ memmove(r2->data, r->data, Dx(r->r)*Dy(r->r)*4);
+ r = r2;
+ }
+ premulalpha(r);
+
if(scale.x > 1 || scale.y > 1){
upscaledraw(r, dst, off, scale, ctl->upfilter);
qunlock(ctl);
- if(r2 != nil) freeraster(r2);
+ freeraster(r2);
return;
}
@@ -259,17 +291,30 @@ framebufctl_draw(Framebufctl *ctl, Image *dst, char *name, Point off, Point scal
// }
// procpoolwait(turbodrawingpool);
// free(tasks);
- loadimage(dst, rectaddpt(sr, dst->r.min), (uchar*)r->data, Dx(fb->r)*Dy(r->r)*4);
+
+ tmp = allocimage(display, sr, RGBA32, 0, DNofill);
+ if(tmp == nil)
+ sysfatal("allocimage: %r");
+
+ loadimage(tmp, sr, (uchar*)r->data, Dx(fb->r)*Dy(r->r)*4);
+ draw(dst, rectaddpt(sr, dst->r.min), tmp, nil, ZP);
+ freeimage(tmp);
}else if(rectclip(&sr, dr)){
+ tmp = allocimage(display, sr, RGBA32, 0, DNofill);
+ if(tmp == nil)
+ sysfatal("allocimage: %r");
+
dr = sr;
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(dst, rectaddpt(dr, dst->r.min), rasterbyteaddr(r, sr.min), Dx(dr)*4);
+ loadimage(tmp, rectaddpt(dr, dst->r.min), rasterbyteaddr(r, sr.min), Dx(dr)*4);
+ draw(dst, rectaddpt(tmp->r, dst->r.min), tmp, nil, tmp->r.min);
+ freeimage(tmp);
}
qunlock(ctl);
- if(r2 != nil) freeraster(r2);
+ freeraster(r2);
}
static void
@@ -278,11 +323,15 @@ upscalememdraw(Raster *fb, Memimage *dst, Point off, Point scale, uint filter)
void (*filterfn)(ulong*, Raster*, Point);
Rectangle blkr;
Point sp, dp;
+ Memimage *tmp;
ulong *blk;
filterfn = nil;
blk = emalloc(scale.x*scale.y*4);
blkr = Rect(0,0,scale.x,scale.y);
+ tmp = allocmemimage(dst->r, RGBA32);
+ if(tmp == nil)
+ sysfatal("allocmemimage: %r");
switch(filter){
case UFScale2x:
@@ -301,8 +350,10 @@ upscalememdraw(Raster *fb, Memimage *dst, Point off, Point scale, uint filter)
filterfn(blk, fb, sp);
else
memsetl(blk, getpixel(fb, sp), scale.x*scale.y);
- loadmemimage(dst, rectaddpt(blkr, dp), (uchar*)blk, scale.x*scale.y*4);
+ loadmemimage(tmp, rectaddpt(blkr, dp), (uchar*)blk, scale.x*scale.y*4);
}
+ memimagedraw(dst, dst->r, tmp, tmp->r.min, nil, ZP, S);
+ freememimage(tmp);
free(blk);
}
@@ -312,6 +363,7 @@ framebufctl_memdraw(Framebufctl *ctl, Memimage *dst, char *name, Point off, Poin
Framebuf *fb;
Raster *r, *r2;
Rectangle sr, dr;
+ Memimage *tmp;
qlock(ctl);
fb = ctl->getfb(ctl);
@@ -329,27 +381,47 @@ framebufctl_memdraw(Framebufctl *ctl, Memimage *dst, char *name, Point off, Poin
r = r2;
}
+ /* this means the raster is a color one, so duplicate it */
+ if(r2 == nil){
+ r2 = allocraster(nil, r->r, COLOR32);
+ memmove(r2->data, r->data, Dx(r->r)*Dy(r->r)*4);
+ r = r2;
+ }
+ premulalpha(r);
+
if(scale.x > 1 || scale.y > 1){
upscalememdraw(r, dst, off, scale, ctl->upfilter);
qunlock(ctl);
- if(r2 != nil) freeraster(r2);
+ freeraster(r2);
return;
}
sr = rectaddpt(fb->r, off);
dr = rectsubpt(dst->r, dst->r.min);
- if(rectinrect(sr, dr))
- loadmemimage(dst, rectaddpt(sr, dst->r.min), (uchar*)r->data, Dx(fb->r)*Dy(r->r)*4);
- else if(rectclip(&sr, dr)){
+ if(rectinrect(sr, dr)){
+ tmp = allocmemimage(sr, RGBA32);
+ if(tmp == nil)
+ sysfatal("allocmemimage: %r");
+
+ loadmemimage(tmp, sr, (uchar*)r->data, Dx(fb->r)*Dy(r->r)*4);
+ memimagedraw(dst, rectaddpt(sr, dst->r.min), tmp, ZP, nil, ZP, S);
+ freememimage(tmp);
+ }else if(rectclip(&sr, dr)){
+ tmp = allocmemimage(sr, RGBA32);
+ if(tmp == nil)
+ sysfatal("allocmemimage: %r");
+
dr = sr;
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(dst, rectaddpt(dr, dst->r.min), rasterbyteaddr(r, sr.min), Dx(dr)*4);
+ 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);
+ freememimage(tmp);
}
qunlock(ctl);
- if(r2 != nil) freeraster(r2);
+ freeraster(r2);
}
static void
@@ -371,7 +443,7 @@ resetAbuf(Abuf *buf)
}
static void
-framebufctl_reset(Framebufctl *ctl, ulong clr)
+framebufctl_reset(Framebufctl *ctl)
{
Framebuf *fb;
Raster *r;
@@ -381,7 +453,7 @@ framebufctl_reset(Framebufctl *ctl, ulong clr)
resetAbuf(&fb->abuf);
r = fb->rasters; /* color buffer */
- clearraster(r, rgba2xrgb(clr));
+ clearraster(r, 0);
r = r->next; /* z-buffer */
fclearraster(r, Inf(-1));
while((r = r->next) != nil)
@@ -515,6 +587,8 @@ rastergetfloat(Raster *r, Point p)
void
freeraster(Raster *r)
{
+ if(r == nil)
+ return;
free(r->data);
free(r->name);
free(r);
diff --git a/graphics.h b/graphics.h
index 3cd070f..39af008 100644
--- a/graphics.h
+++ b/graphics.h
@@ -24,8 +24,8 @@ enum {
LightSpot,
/* raster formats */
- COLOR32 = 0,
- FLOAT32,
+ COLOR32 = 0, /* RGBA32 */
+ FLOAT32, /* F32 */
/* texture formats */
RAWTexture = 0, /* unmanaged */
@@ -314,7 +314,7 @@ struct Framebufctl
void (*draw)(Framebufctl*, Image*, char*, Point, Point);
void (*memdraw)(Framebufctl*, Memimage*, char*, Point, Point);
void (*swap)(Framebufctl*);
- void (*reset)(Framebufctl*, ulong);
+ void (*reset)(Framebufctl*);
void (*createraster)(Framebufctl*, char*, ulong);
Raster *(*fetchraster)(Framebufctl*, char*);
Framebuf *(*getfb)(Framebufctl*);
@@ -350,7 +350,6 @@ struct Camera
} clip;
Matrix3 proj; /* VCS to clip space xform */
Projection projtype;
- ulong clearcolor;
int cullmode;
int enableblend;
int enabledepth;
@@ -431,14 +430,20 @@ Color samplecubemap(Cubemap*, Point3, Color(*)(Texture*, Point2));
/* util */
Point2 modulapt2(Point2, Point2);
+Point2 minpt2(Point2, Point2);
+Point2 maxpt2(Point2, Point2);
Point3 modulapt3(Point3, Point3);
+Point3 minpt3(Point3, Point3);
+Point3 maxpt3(Point3, Point3);
Memimage *rgb(ulong);
Memimage *dupmemimage(Memimage*);
/* color */
+ulong col2ul(Color);
+Color ul2col(ulong);
+int hasalpha(ulong);
Color srgb2linear(Color);
Color linear2srgb(Color);
-ulong rgba2xrgb(ulong);
Color aces(Color);
Color aces2(Color);
diff --git a/render.c b/render.c
index c86c3d8..9495f30 100644
--- a/render.c
+++ b/render.c
@@ -11,8 +11,6 @@
Rectangle UR = {0,0,1,1};
//Procpool *turbodrawingpool;
-static ulong col2ul(Color);
-
static Vertexattr *
sparams_getuniform(Shaderparams *sp, char *id)
{
@@ -59,30 +57,6 @@ sparams_toraster(Shaderparams *sp, char *rname, void *v)
}
}
-static ulong
-col2ul(Color c)
-{
- uchar cbuf[4];
-
- cbuf[0] = fclamp(c.b, 0, 1)*0xFF;
- cbuf[1] = fclamp(c.g, 0, 1)*0xFF;
- cbuf[2] = fclamp(c.r, 0, 1)*0xFF;
- cbuf[3] = fclamp(c.a, 0, 1)*0xFF;
- return cbuf[3]<<24 | cbuf[2]<<16 | cbuf[1]<<8 | cbuf[0];
-}
-
-static Color
-ul2col(ulong l)
-{
- Color c;
-
- c.b = (l & 0xff)/255.0;
- c.g = (l>>8 & 0xff)/255.0;
- c.r = (l>>16 & 0xff)/255.0;
- c.a = (l>>24 & 0xff)/255.0;
- return c;
-}
-
static void
pixel(Raster *fb, Point p, Color c, int blend)
{
diff --git a/texture.c b/texture.c
index 5d04b89..df5089a 100644
--- a/texture.c
+++ b/texture.c
@@ -48,6 +48,7 @@ _memreadcolor(Texture *t, Point sp)
uchar cbuf[4];
switch(t->image->chan){
+ default: sysfatal("unsupported texture format");
case RGB24:
unloadmemimage(t->image, rectaddpt(UR, sp), cbuf+1, sizeof cbuf - 1);
cbuf[0] = 0xFF;
@@ -63,6 +64,12 @@ _memreadcolor(Texture *t, Point sp)
}
c = cbuf2col(cbuf);
+ /* remove pre-multiplied alpha */
+ if(hasalpha(t->image->chan) && c.a > 0 && c.a < 1){
+ c.r /= c.a;
+ c.g /= c.a;
+ c.b /= c.a;
+ }
switch(t->type){
case sRGBTexture: c = srgb2linear(c); break;
}
diff --git a/util.c b/util.c
index 7335819..e7a9544 100644
--- a/util.c
+++ b/util.c
@@ -14,12 +14,54 @@ modulapt2(Point2 a, Point2 b)
return (Point2){a.x*b.x, a.y*b.y, a.w*b.w};
}
+Point2
+minpt2(Point2 a, Point2 b)
+{
+ return (Point2){
+ min(a.x, b.x),
+ min(a.y, b.y),
+ min(a.w, b.w)
+ };
+}
+
+Point2
+maxpt2(Point2 a, Point2 b)
+{
+ return (Point2){
+ max(a.x, b.x),
+ max(a.y, b.y),
+ max(a.w, b.w)
+ };
+}
+
Point3
modulapt3(Point3 a, Point3 b)
{
return (Point3){a.x*b.x, a.y*b.y, a.z*b.z, a.w*b.w};
}
+Point3
+minpt3(Point3 a, Point3 b)
+{
+ return (Point3){
+ min(a.x, b.x),
+ min(a.y, b.y),
+ min(a.z, b.z),
+ min(a.w, b.w)
+ };
+}
+
+Point3
+maxpt3(Point3 a, Point3 b)
+{
+ return (Point3){
+ max(a.x, b.x),
+ max(a.y, b.y),
+ max(a.z, b.z),
+ max(a.w, b.w)
+ };
+}
+
void
memsetf(void *dp, float v, usize len)
{