From ade35a297abb12c5fceebba1ee6ac72a3a16014c Mon Sep 17 00:00:00 2001 From: Ivan Mahonin Date: May 03 2022 14:46:13 +0000 Subject: curves --- diff --git a/data/icy-color.png b/data/icy-color.png deleted file mode 100644 index f7c260c..0000000 Binary files a/data/icy-color.png and /dev/null differ diff --git a/data/icy-color.tga b/data/icy-color.tga deleted file mode 100644 index 4c3bf08..0000000 Binary files a/data/icy-color.tga and /dev/null differ diff --git a/data/icy-curve.png b/data/icy-curve.png deleted file mode 100644 index f995a02..0000000 Binary files a/data/icy-curve.png and /dev/null differ diff --git a/data/icy-curve.tga b/data/icy-curve.tga deleted file mode 100644 index 1300b9e..0000000 Binary files a/data/icy-curve.tga and /dev/null differ diff --git a/data/icy-demo-black.png b/data/icy-demo-black.png index ac35c02..d9cc373 100644 Binary files a/data/icy-demo-black.png and b/data/icy-demo-black.png differ diff --git a/data/icy-demo.png b/data/icy-demo.png index 82765ad..bf76677 100644 Binary files a/data/icy-demo.png and b/data/icy-demo.png differ diff --git a/filter.base.inc.c b/filter.base.inc.c index 5ef0dc7..07b8db6 100644 --- a/filter.base.inc.c +++ b/filter.base.inc.c @@ -123,6 +123,24 @@ void filterCurveByCh(Img *img, Img *icurve, int ch) { } +void filterPow(Img *img, double val) { + if (!img->data) return; + for(double *p = img->data, *e = p + 4*img->w*img->h; p < e; p += 4) { + p[0] = pow(p[0], val); + p[1] = pow(p[1], val); + p[2] = pow(p[2], val); + p[3] = pow(p[3], val); + } +} + + +void filterPowCh(Img *img, int ch, double val) { + if (!img->data) return; + for(double *p = img->data + ch, *e = p + 4*img->w*img->h; p < e; p += 4) + *p = pow(*p, val); +} + + void filterScreenToCompositeHSV(Img *img) { if (!img->data) return; for(double *p = img->data, *e = p + 4*img->w*img->h; p < e; p += 4) { diff --git a/filter.curve.inc.c b/filter.curve.inc.c new file mode 100644 index 0000000..5cf80e9 --- /dev/null +++ b/filter.curve.inc.c @@ -0,0 +1,147 @@ + + +double calcCurvePointEx(double *curveX, int stepX, double *curveY, int stepY, int count, double x) { + if (count <= 0) return 0; + if (count == 1) return *curveY; + if (x <= curveX[0]) return *curveY; + if (x >= curveX[(count-1)*stepX]) return curveY[(count-1)*stepY]; + + int i = 0; + while(i+1 < count && curveX[(i+1)*stepX] < x) + ++i; + + int i0 = i > 0 ? i-1 : i; + int i1 = i; + int i2 = i1+1 < count ? i1+1 : i1; + int i3 = i2+1 < count ? i2+1 : i2; + + double p0[] = { curveX[i0*stepX], curveY[i0*stepY] }; + double p1[] = { curveX[i1*stepX], curveY[i1*stepY] }; + double p2[] = { curveX[i2*stepX], curveY[i2*stepY] }; + double p3[] = { curveX[i3*stepX], curveY[i3*stepY] }; + + double dx = p2[0] - p1[0]; + if (dx <= IMG_PRECISION) return p1[1]; + + double a = p1[1]; + double b = p2[1]; + double ta = p2[0]-p0[0]; ta = ta > IMG_PRECISION ? (p2[1]-p0[1])/ta*dx : 0; + double tb = p3[0]-p1[0]; tb = tb > IMG_PRECISION ? (p3[1]-p1[1])/tb*dx : 0; + double l = (x - p1[0])/dx; + + return a + (ta + (3*(b-a)-ta-ta-tb + (2*(a-b)+ta+tb)*l)*l)*l; +} + + +double calcCurvePoint(double *curve, int count, double x) + { return calcCurvePointEx(curve, 2, curve+1, 2, count, x); } + + +double calcTCurvePointEx( + double *curveX, int stepX, + double *curveY, int stepY, + double *curveT, int stepT, + int count, double x) +{ + if (count <= 0) return 0; + if (count == 1) return *curveY; + if (x <= curveX[0]) return *curveY; + if (x >= curveX[(count-1)*stepX]) return curveY[(count-1)*stepY]; + + int i0 = 0, i1 = 1; + while(i1+1 < count && curveX[i1*stepX] < x) + ++i0, ++i1; + + double x0 = curveX[i0*stepX]; + double x1 = curveX[i1*stepX]; + double dx = x1 - x0; + if (dx <= IMG_PRECISION) return curveY[i0*stepY]; + + double a = curveY[i0*stepY]; + double b = curveY[i1*stepY]; + double ta = curveT[i0*stepT]*dx; + double tb = curveT[i1*stepT]*dx; + double l = (x - x0)/dx; + + return a + (ta + (3*(b-a)-ta-ta-tb + (2*(a-b)+ta+tb)*l)*l)*l; +} + + +double calcTCurvePoint(double *curve, int count, double x) + { return calcTCurvePointEx(curve, 3, curve+1, 3, curve+2, 3, count, x); } + + + +void filterRasterizeCurve(Img *img, double *curve, int count, double l0, double l1) { + if (!img->data) return; + for(int x = 0; x < img->w; ++x) { + double v = calcCurvePoint(curve, count, l0 + (l1 - l0)*x/(img->w - 1)); + for(int y = 0; y < img->h; ++y) { + double *p = imgPixel(img, x, y); + p[0] = p[1] = p[2] = p[3] = v; + } + } +} + + +void filterRasterizeCurveCh(Img *img, int ch, double *curve, int count, double l0, double l1) { + if (!img->data) return; + for(int x = 0; x < img->w; ++x) { + double v = calcCurvePoint(curve, count, l0 + (l1 - l0)*x/(img->w - 1)); + for(int y = 0; y < img->h; ++y) + imgPixel(img, x, y)[ch] = v; + } +} + + +void filterRasterizeCurveColor(Img *img, double *curve, int count, double l0, double l1) { + if (!img->data) return; + for(int x = 0; x < img->w; ++x) { + double l = l0 + (l1 - l0)*x/(img->w - 1); + double c[] = { + calcCurvePointEx(curve, 5, curve+1, 5, count, l), + calcCurvePointEx(curve, 5, curve+2, 5, count, l), + calcCurvePointEx(curve, 5, curve+3, 5, count, l), + calcCurvePointEx(curve, 5, curve+4, 5, count, l) }; + for(int y = 0; y < img->h; ++y) + memcpy(imgPixel(img, x, y), c, sizeof(c)); + } +} + + +void filterRasterizeTCurve(Img *img, double *curve, int count, double l0, double l1) { + if (!img->data) return; + for(int x = 0; x < img->w; ++x) { + double v = calcTCurvePoint(curve, count, l0 + (l1 - l0)*x/(img->w - 1)); + for(int y = 0; y < img->h; ++y) { + double *p = imgPixel(img, x, y); + p[0] = p[1] = p[2] = p[3] = v; + } + } +} + + +void filterRasterizeTCurveCh(Img *img, int ch, double *curve, int count, double l0, double l1) { + if (!img->data) return; + for(int x = 0; x < img->w; ++x) { + double v = calcTCurvePoint(curve, count, l0 + (l1 - l0)*x/(img->w - 1)); + for(int y = 0; y < img->h; ++y) + imgPixel(img, x, y)[ch] = v; + } +} + + +void filterRasterizeTCurveColor(Img *img, double *curve, int count, double l0, double l1) { + if (!img->data) return; + for(int x = 0; x < img->w; ++x) { + double l = l0 + (l1 - l0)*x/(img->w - 1); + double c[] = { + calcTCurvePointEx(curve, 6, curve+1, 6, curve+2, 6, count, l), + calcTCurvePointEx(curve, 6, curve+1, 6, curve+3, 6, count, l), + calcTCurvePointEx(curve, 6, curve+1, 6, curve+4, 6, count, l), + calcTCurvePointEx(curve, 6, curve+1, 6, curve+5, 6, count, l) }; + for(int y = 0; y < img->h; ++y) + memcpy(imgPixel(img, x, y), c, sizeof(c)); + } +} + diff --git a/filter.icy.inc.c b/filter.icy.inc.c index f29fb62..676aba1 100644 --- a/filter.icy.inc.c +++ b/filter.icy.inc.c @@ -1,6 +1,6 @@ -void filterIcy(Img *img, Img *icyCurve, Img *icyColor, double size, int withWind) { +void filterIcy(Img *img, double size, int withWind) { if (!img->data) return; size = fabs(size); if (!(size > IMG_PRECISION)) withWind = FALSE; @@ -19,6 +19,19 @@ void filterIcy(Img *img, Img *icyCurve, Img *icyColor, double size, int withWind const double optWindNoise1 = 0.75; const double optWindAtt = withWind ? pow(0.95, 4/size) : 0; + double curve[][3] = { + { 0 , 0 , 0 }, + { 0.15, 0.65, 0 }, + { 0.35, 0.15, 0 }, + { 0.65, 0.85, 0 }, + { 0.85, 0.35, 0 }, + { 1 , 1 , 0 } }; + double curveColor[][5] = { + { 0 , 0 , 0 , 0 , 1 }, + { 0.6, 0.4, 0.4, 0.6, 1 }, + { 1 , 1 , 1 , 1 , 1 } }; + + double blurSize = size/2; if (optNoiseWidth0) filterMulNoiseGray(img, img->w/optNoiseWidth0, img->h/optNoiseWidth0, 1-optNoise0, 1); if (optNoiseWidth1) filterMulNoiseGray(img, img->w/optNoiseWidth1, img->h/optNoiseWidth1, 1-optNoise1, 1); @@ -29,28 +42,13 @@ void filterIcy(Img *img, Img *icyCurve, Img *icyColor, double size, int withWind clmMulColor(&m, optBright0); filterMatrix(img, &m); - if (icyCurve && icyCurve->data) { - //int mi = 0; - //for(int i = 1; i < icyCurve->w; ++i) - // if (imgPixel(icyCurve, i-1, 0)[0] - IMG_PRECISION <= imgPixel(icyCurve, i, 0)[0]) - // mi = i; else break; - int mi = 128; - int mmi = size > 4 ? round(mi*4/size) : mi; - if (mmi < mi && mi < icyCurve->w - 1) { - Img imgA = {}, imgB = {}; - imgCropTo(&imgA, icyCurve, 0, 0, mi, icyCurve->h); - filterResampleCubic(&imgA, mmi, icyCurve->h); - imgInit(&imgB, icyCurve->w - mi + mmi + 1, icyCurve->h); - imgCopyTo(&imgB, &imgA, 1, 0, 0, 0, imgA.w, imgA.h); - imgCopyTo(&imgB, icyCurve, mmi + 1, 0, mi, 0, icyCurve->w - mi, icyCurve->h); - filterFillCh(&imgB, 3, 1); - filterCurveByCh(img, &imgB, 0); - imgDestroy(&imgA); - imgDestroy(&imgB); - } else { - filterCurveByCh(img, icyCurve, 0); - } - } + Img imgC = {}; + imgInit(&imgC, 512, 1); + + curve[0][0] = curve[1][0]*(1 - 4/size); + filterRasterizeTCurve(&imgC, curve[0], sizeof(curve)/sizeof(*curve), curve[0][0], 1); + filterFillCh(&imgC, 3, 1); + filterCurveByCh(img, &imgC, 0); clmMulColor(&m, optBright1); filterMatrix(img, &m); @@ -73,8 +71,9 @@ void filterIcy(Img *img, Img *icyCurve, Img *icyColor, double size, int withWind imgDestroy(&wind); } - if (icyColor) - filterCurveByCh(img, icyColor, 0); + filterRasterizeCurveColor(&imgC, curveColor[0], sizeof(curveColor)/sizeof(*curveColor), 0, 1); + filterCurveByCh(img, &imgC, 0); + imgDestroy(&imgC); //clmScreenToCompositeYUV(&m); //filterMatrix(img, &m); @@ -86,15 +85,12 @@ int icyGenerate(Img *img, int argc, char **argv) { if (img) imgDestroy(img); char *inFile = "", *outFile = ""; - char *curveFile = "data/icy-curve.tga", *colorFile = "data/icy-color.tga"; double size = 4; int withWind = TRUE; int withoutWind = FALSE; ArgDesc descs[] = { { 'i', &inFile, &pargString, "path to input image" }, { 'o', &outFile, &pargString, "path to output image" }, - { 'c', &curveFile, &pargString, "path to image with crystal curve" }, - { 'l', &colorFile, &pargString, "path to image with color curve" }, { 's', &size, &pargDouble, "effect size" }, { 'w', &withWind, NULL, "add icicles (default)" }, { 'n', &withoutWind, NULL, "no icicles" }, @@ -109,22 +105,16 @@ int icyGenerate(Img *img, int argc, char **argv) { if (withoutWind) withWind = FALSE; Img imgC = {}; - Img imgCurve = {}; - Img imgColor = {}; if (!imgLoad(&imgC, inFile)) return FALSE; - imgLoad(&imgCurve, curveFile); - imgLoad(&imgColor, colorFile); - filterIcy(&imgC, &imgCurve, &imgColor, size, withWind); + filterIcy(&imgC, size, withWind); if (!imgSave(&imgC, outFile)) imgDestroy(&imgC); int success = !!imgC.data; if (img) imgSwap(img, &imgC); - imgDestroy(&imgCurve); - imgDestroy(&imgCurve); imgDestroy(&imgC); return success; diff --git a/icy-heli.c b/icy-heli.c index 9758670..3c66d2f 100644 --- a/icy-heli.c +++ b/icy-heli.c @@ -14,6 +14,7 @@ #include "filter.base.inc.c" #include "filter.matrix.inc.c" #include "filter.blend.inc.c" +#include "filter.curve.inc.c" #include "filter.noise.inc.c" #include "filter.blur.inc.c" #include "filter.wind.inc.c" @@ -72,3 +73,4 @@ int main(int largc, char **largv) { windowRun(); return 0; } + diff --git a/icy.c b/icy.c index 27a1c7c..69db658 100644 --- a/icy.c +++ b/icy.c @@ -29,6 +29,7 @@ double randomFloat() #include "filter.base.inc.c" #include "filter.matrix.inc.c" #include "filter.blend.inc.c" +#include "filter.curve.inc.c" #include "filter.noise.inc.c" #include "filter.blur.inc.c" #include "filter.wind.inc.c" diff --git a/img.ldr.png.inc.c b/img.ldr.png.inc.c index a94d54e..d54fd5c 100644 --- a/img.ldr.png.inc.c +++ b/img.ldr.png.inc.c @@ -53,7 +53,7 @@ int imageLoadPng(const char *path, int *outWidth, int *outHeight, unsigned char unsigned char *data = (unsigned char*)calloc(1, 4*width*height); png_bytep *rows = (png_bytep*)malloc(height*sizeof(png_bytep)); for(unsigned int i = 0; i < height; i++) - rows[height-i-1] = data + i*4*width; + rows[i] = data + i*4*width; png_read_image(png, rows); png_read_end(png, 0); free(rows); @@ -92,7 +92,7 @@ int imageSavePng(const char *path, int width, int height, const unsigned char *p png_bytep *rows = (png_bytep*)malloc(height*sizeof(png_bytep)); for(int i = 0; i < height; i++) - rows[height-1-i] = (png_bytep)(pixels + i*4*width); + rows[i] = (png_bytep)(pixels + i*4*width); png_write_image(png, rows); png_write_end(png, info); free(rows);