comp.c
#include <math.h>
#include "comp.h"
__attribute__((hot)) void lil_composite(const enum lil_BlendMode cm, lil_Colour* b, const lil_Colour* t){
switch(cm){
case LIL_BM(ALPHA):
for(int i = 0; i < 3; i++)
b->arr[i] = ((b->arr[i] * (255 - t->a)) + t->arr[i] * (t->a)) / 255;
b->a = min(b->a + t->a, 255);
break;
case LIL_BM(REPLACE):
b->c = t->c;
break;
case LIL_BM(ADD):
for(int i = 0; i < 3; i++)
b->arr[i] = min((b->arr[i] + t->arr[i]), 255);
break;
case LIL_BM(MULTIPLY):
for(int i = 0; i < 4; i++)
b->arr[i] = (b->arr[i] * t->arr[i]) / 255;
break;
case LIL_BM(SUBTRACT):
for(int i = 0; i < 3; i++)
b->arr[i] = max((b->arr[i] - t->arr[i]), 0);
break;
case LIL_BM(LIGHTEN):
for(int i = 0; i < 3; i++)
b->arr[i] = max((b->arr[i] * (255 - t->a) + t->arr[i] * t->a) / 255, b->arr[i]);
break;
case LIL_BM(DARKEN):
for(int i = 0; i < 3; i++)
b->arr[i] = min((b->arr[i] * (255 - t->a) + t->arr[i] * t->a) / 255, b->arr[i]);
break;
case LIL_BM(AVERAGE):
for(int i = 0; i < 4; i++)
b->arr[i] = (b->arr[i] + t->arr[i]) / 2;
break;
case LIL_BM(SCREEN):
for(int i = 0; i < 3; i++)
b->arr[i] = (b->arr[i] * (255 - t->arr[i]) + (t->arr[i] * t->arr[i])) / 255;
break;
case LIL_BM(DISSOLVE):
if((rand() % 255) < t->a)
b->c = t->c;
break;
case LIL_BM(DIFFERENCE):
for(int i = 0; i < 3; i++)
b->arr[i] = abs((signed)t->arr[i] - (signed)b->arr[i]);
break;
case LIL_BM(XOR):
b->rgb ^= t->rgb;
break;
case LIL_BM(AND):
b->rgb &= t->rgb;
break;
case LIL_BM(OR):
b->rgb |= t->rgb;
break;
}
}
const char* const lil_compMethodList[] = {
"alpha", "replace",
"add", "multiply", "subtract",
"lighten", "darken", "average",
"screen", "dissolve", "difference",
"xor", "and", "or",
NULL,
};
const char* const lil_sampleMethodList[] = {
"bilinear", "box", "nearest",
NULL,
};
const char* const lil_sampleMethodBoundsList[] = {
"transparent", "black", "white",
"mirror", "edge",
NULL,
};
#define TRANS ((lil_Colour){0})
__attribute__((hot)) lil_Colour lil_sampleImage(const enum lil_SampleMode sm, const lil_Image* img, const lil_Number x, const lil_Number y, const lil_Number sx, const lil_Number sy){
lil_Number x1, y1, x2, y2;
lil_Colour a, b, c, d;
switch(sm & 0xff){
case LIL_SMBILINEAR: (void)0;
x1 = floor(x);
y1 = floor(y);
x2 = x1 + 1.;
y2 = y1 + 1.;
const lil_Number xd = x - x1;
const lil_Number yd = y - y1;
const lil_Number xdd = 1. - xd;
const lil_Number ydd = 1. - yd;
a = isWithinBounds(img, floor(x1), floor(y1)) ? img->d[(int)XY(img, x1, y1)] : TRANS;
b = isWithinBounds(img, floor(x2), floor(y1)) ? img->d[(int)XY(img, x2, y1)] : TRANS;
c = isWithinBounds(img, floor(x1), floor(y2)) ? img->d[(int)XY(img, x1, y2)] : TRANS;
d = isWithinBounds(img, floor(x2), floor(y2)) ? img->d[(int)XY(img, x2, y2)] : TRANS;
for(int i = 0; i < 4; i++){
a.arr[i] = (
(lil_Number)a.arr[i] * xdd * ydd +
(lil_Number)b.arr[i] * xd * ydd +
(lil_Number)c.arr[i] * xdd * yd +
(lil_Number)d.arr[i] * xd * yd
);
}
return a;
case LIL_SMBOX: (void)0;
const unsigned bw = ceil(sx/1.);
const unsigned bh = ceil(sy/1.);
x1 = x;
y1 = y;
lil_Number rgba[4] = { 0, 0, 0, 0 };
for(unsigned y = 0; y < bh; y++){
for(unsigned x = 0; x < bw; x++){
if(!isWithinBounds(img, x + x1, y + y1))
continue;
size_t o = XY(img, x + x1, y + y1);
for(unsigned i = 0; i < 4; i++)
rgba[i] += img->d[o].arr[i];
}
}
for(unsigned i = 0; i < 4; i++)
a.arr[i] = rgba[i] / (bw * bh);
return a;
case LIL_SMNEAREST:
default:
return isWithinBounds(img, (int)x, (int)y) ? img->d[XY(img, (int)x, (int)y)] : TRANS;
}
}