/* -----------------------------[ XCF Loader ]----------------------------- This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common.h" #include #define EINA_FLT_EQ(a, b) (fabsf((float)a - (float)b) <= FLT_EPSILON) #define RS R_VAL(src + s_idx) #define GS G_VAL(src + s_idx) #define BS B_VAL(src + s_idx) #define AS A_VAL(src + s_idx) #define RD R_VAL(dest + d_idx) #define GD G_VAL(dest + d_idx) #define BD B_VAL(dest + d_idx) #define AD A_VAL(dest + d_idx) #define EPS 0.00001 #define PI 3.141592654 #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define INT_MULT(a,b,t) ((t) = (a) * (b) + 0x80, ((((t) >> 8) + (t)) >> 8)) #define LINEAR(x,y,w) ((w*y + x)*4) static void rgb_to_hls (DATA8 *red, DATA8 *green, DATA8 *blue) { int r, g, b; double h, l, s; int min, max; int delta; r = *red; g = *green; b = *blue; if (r > g) { max = MAX (r, b); min = MIN (g, b); } else { max = MAX (g, b); min = MIN (r, b); } l = (max + min) / 2.0; if (max == min) { s = 0.0; h = 0.0; } else { delta = (max - min); if (l < 128) s = 255 * (double) delta / (double) (max + min); else s = 255 * (double) delta / (double) (511 - max - min); if (r == max) h = (g - b) / (double) delta; else if (g == max) h = 2 + (b - r) / (double) delta; else h = 4 + (r - g) / (double) delta; h = h * 42.5; if (h < 0) h += 255; else if (h > 255) h -= 255; } *red = h; *green = l; *blue = s; } static DATA8 gimp_hls_value (double n1, double n2, double hue) { double value; if (hue > 255) hue -= 255; else if (hue < 0) hue += 255; if (hue < 42.5) value = n1 + (n2 - n1) * (hue / 42.5); else if (hue < 127.5) value = n2; else if (hue < 170) value = n1 + (n2 - n1) * ((170 - hue) / 42.5); else value = n1; return (DATA8) (value * 255); } static void hls_to_rgb (DATA8 *hue, DATA8 *lightness, DATA8 *saturation) { double h, l, s; double m1, m2; h = *hue; l = *lightness; s = *saturation; if (EINA_FLT_EQ(s, 0)) { /* achromatic case */ *hue = l; *lightness = l; *saturation = l; } else { if (l < 128) m2 = (l * (255 + s)) / 65025.0; else m2 = (l + s - (l * s) / 255.0) / 255.0; m1 = (l / 127.5) - m2; /* chromatic case */ *hue = gimp_hls_value (m1, m2, h + 85); *lightness = gimp_hls_value (m1, m2, h); *saturation = gimp_hls_value (m1, m2, h - 85); } } static void rgb_to_hsv (DATA8 *red, DATA8 *green, DATA8 *blue) { int r, g, b; double h, s, v; int min, max; int delta; h = 0.0; r = *red; g = *green; b = *blue; if (r > g) { max = MAX (r, b); min = MIN (g, b); } else { max = MAX (g, b); min = MIN (r, b); } v = max; if (max != 0) s = ((max - min) * 255) / (double) max; else s = 0; if (EINA_FLT_EQ(s, 0)) h = 0; else { delta = max - min; if (r == max) h = (g - b) / (double) delta; else if (g == max) h = 2 + (b - r) / (double) delta; else if (b == max) h = 4 + (r - g) / (double) delta; h *= 42.5; if (h < 0) h += 255; if (h > 255) h -= 255; } *red = h; *green = s; *blue = v; } static void hsv_to_rgb (DATA8 *hue, DATA8 *saturation, DATA8 *value) { double h, s, v; double f, p, q, t; if (*saturation == 0) { *hue = *value; *saturation = *value; /* *value = *value; */ } else { h = *hue * 6.0 / 255.0; s = *saturation / 255.0; v = *value / 255.0; f = h - (int) h; p = v * (1.0 - s); q = v * (1.0 - (s * f)); t = v * (1.0 - (s * (1.0 - f))); switch ((int) h) { case 0: *hue = v * 255; *saturation = t * 255; *value = p * 255; break; case 1: *hue = q * 255; *saturation = v * 255; *value = p * 255; break; case 2: *hue = p * 255; *saturation = v * 255; *value = t * 255; break; case 3: *hue = p * 255; *saturation = q * 255; *value = v * 255; break; case 4: *hue = t * 255; *saturation = p * 255; *value = v * 255; break; case 5: *hue = v * 255; *saturation = p * 255; *value = q * 255; break; } } } /* translate negative destinations */ static void _clip(int * src_tl_x, int * src_tl_y, int * src_br_x, int * src_br_y, int * dest_x, int * dest_y, int dest_w, int dest_h) { if (*dest_x + *src_br_x >= dest_w) { *src_br_x -= (*dest_x + *src_br_x) - dest_w; } if (*dest_y + *src_br_y >= dest_h) { *src_br_y -= (*dest_y + *src_br_y) - dest_h; } if (*dest_x < 0) { *src_tl_x = -(*dest_x); *dest_x = 0; } if (*dest_y < 0) { *src_tl_y = -(*dest_y); *dest_y = 0; } } // FIXME: make sure layer alpha is used/applied in all cases void combine_pixels_normal (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y) { int x, y, s_idx, d_idx; int src_tl_x = 0, src_tl_y = 0; int src_br_x = src_w, src_br_y = src_h; int b; unsigned char src_alpha; unsigned char new_alpha; float ratio, compl_ratio; unsigned long tmp; _clip(&src_tl_x, &src_tl_y, &src_br_x, &src_br_y, &dest_x, &dest_y, dest_w, dest_h); for (y = src_tl_y; y < src_br_y; y++) for (x = src_tl_x; x < src_br_x; x++) { d_idx = LINEAR((dest_x + x - src_tl_x), (dest_y + y - src_tl_y), dest_w); s_idx = LINEAR(x, y, src_w); src_alpha = AS; if (src_alpha != 0) { if (src_alpha == 255) new_alpha = src_alpha; else new_alpha = AD + INT_MULT((255u - AD), src_alpha, tmp); b = 3; if (new_alpha != 0) { ratio = (float) src_alpha / new_alpha; compl_ratio = 1.0 - ratio; do { b--; dest[d_idx + b] = (unsigned char) (src[s_idx + b] * ratio + dest[d_idx + b] * compl_ratio + EPS); } while (b); } AD = new_alpha; } } } void combine_pixels_add (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y) { int x, y, s_idx, d_idx; int src_tl_x = 0, src_tl_y = 0; int src_br_x = src_w, src_br_y = src_h; int tmp, tmp2; _clip(&src_tl_x, &src_tl_y, &src_br_x, &src_br_y, &dest_x, &dest_y, dest_w, dest_h); for (y = src_tl_y; y < src_br_y; y++) for (x = src_tl_x; x < src_br_x; x++) { d_idx = LINEAR((dest_x + x - src_tl_x), (dest_y + y - src_tl_y), dest_w); s_idx = LINEAR(x, y, src_w); tmp2 = INT_MULT(AS, RS, tmp); tmp = RD + tmp2; RD = (tmp > 255 ? 255 : tmp); tmp2 = INT_MULT(AS, GS, tmp); tmp = GD + tmp2; GD = (tmp > 255 ? 255 : tmp); tmp2 = INT_MULT(AS, BS, tmp); tmp = BD + tmp2; BD = (tmp > 255 ? 255 : tmp); // AD = MIN(AD, AS); } } void combine_pixels_sub (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y) { int x, y, s_idx, d_idx; int src_tl_x = 0, src_tl_y = 0; int src_br_x = src_w, src_br_y = src_h; int tmp, tmp2; _clip(&src_tl_x, &src_tl_y, &src_br_x, &src_br_y, &dest_x, &dest_y, dest_w, dest_h); for (y = src_tl_y; y < src_br_y; y++) for (x = src_tl_x; x < src_br_x; x++) { d_idx = LINEAR((dest_x + x - src_tl_x), (dest_y + y - src_tl_y), dest_w); s_idx = LINEAR(x, y, src_w); tmp2 = INT_MULT(AS, RS, tmp); tmp = RD - tmp2; RD = (tmp < 0 ? 0 : tmp); tmp2 = INT_MULT(AS, GS, tmp); tmp = GD - tmp2; GD = (tmp < 0 ? 0 : tmp); tmp2 = INT_MULT(AS, BS, tmp); tmp = BD - tmp2; BD = (tmp < 0 ? 0 : tmp); // AD = MIN(AD, AS); } } void combine_pixels_diff (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y) { int x, y, s_idx, d_idx; int src_tl_x = 0, src_tl_y = 0; int src_br_x = src_w, src_br_y = src_h; int tmp, tmp2; _clip(&src_tl_x, &src_tl_y, &src_br_x, &src_br_y, &dest_x, &dest_y, dest_w, dest_h); for (y = src_tl_y; y < src_br_y; y++) for (x = src_tl_x; x < src_br_x; x++) { d_idx = LINEAR((dest_x + x - src_tl_x), (dest_y + y - src_tl_y), dest_w); s_idx = LINEAR(x, y, src_w); tmp2 = INT_MULT(AS, RS, tmp); tmp = RD - tmp2; RD = (tmp < 0 ? -tmp : tmp); tmp2 = INT_MULT(AS, GS, tmp); tmp = GD - tmp2; GD = (tmp < 0 ? -tmp : tmp); tmp2 = INT_MULT(AS, BS, tmp); tmp = BD - tmp2; BD = (tmp < 0 ? -tmp : tmp); // AD = MIN(AD, AS); } } void combine_pixels_darken (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y) { int x, y, s_idx, d_idx; int src_tl_x = 0, src_tl_y = 0; int src_br_x = src_w, src_br_y = src_h; _clip(&src_tl_x, &src_tl_y, &src_br_x, &src_br_y, &dest_x, &dest_y, dest_w, dest_h); for (y = src_tl_y; y < src_br_y; y++) for (x = src_tl_x; x < src_br_x; x++) { d_idx = LINEAR((dest_x + x - src_tl_x), (dest_y + y - src_tl_y), dest_w); s_idx = LINEAR(x, y, src_w); RD = MIN(RD, RS); GD = MIN(GD, GS); BD = MIN(BD, BS); // AD = MIN(AD, AS); } } void combine_pixels_lighten (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y) { int x, y, s_idx, d_idx; int src_tl_x = 0, src_tl_y = 0; int src_br_x = src_w, src_br_y = src_h; _clip(&src_tl_x, &src_tl_y, &src_br_x, &src_br_y, &dest_x, &dest_y, dest_w, dest_h); for (y = src_tl_y; y < src_br_y; y++) for (x = src_tl_x; x < src_br_x; x++) { d_idx = LINEAR((dest_x + x - src_tl_x), (dest_y + y - src_tl_y), dest_w); s_idx = LINEAR(x, y, src_w); RD = MAX(RD, RS); GD = MAX(GD, GS); BD = MAX(BD, BS); // AD = MIN(AD, AS); } } void combine_pixels_mult (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y) { int x, y, s_idx, d_idx; int src_tl_x = 0, src_tl_y = 0; int src_br_x = src_w, src_br_y = src_h; int tmp, tmp2, tmp3; _clip(&src_tl_x, &src_tl_y, &src_br_x, &src_br_y, &dest_x, &dest_y, dest_w, dest_h); for (y = src_tl_y; y < src_br_y; y++) for (x = src_tl_x; x < src_br_x; x++) { d_idx = LINEAR((dest_x + x - src_tl_x), (dest_y + y - src_tl_y), dest_w); s_idx = LINEAR(x, y, src_w); tmp2 = INT_MULT(RS, AS, tmp); tmp2 = INT_MULT(RD, tmp2, tmp); tmp3 = INT_MULT(RD, (255 - AS), tmp); RD = (tmp2 + tmp3); tmp2 = INT_MULT(GS, AS, tmp); tmp2 = INT_MULT(GD, tmp2, tmp); tmp3 = INT_MULT(GD, (255 - AS), tmp); GD = (tmp2 + tmp3); tmp2 = INT_MULT(BS, AS, tmp); tmp2 = INT_MULT(BD, tmp2, tmp); tmp3 = INT_MULT(BD, (255 - AS), tmp); BD = (tmp2 + tmp3); // AS = MIN(AS, AD); } // combine_pixels_normal(src, src_w, src_h, dest, dest_w, dest_h, dest_x, dest_y); } void combine_pixels_div (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y) { int x, y, s_idx, d_idx; int src_tl_x = 0, src_tl_y = 0; int src_br_x = src_w, src_br_y = src_h; _clip(&src_tl_x, &src_tl_y, &src_br_x, &src_br_y, &dest_x, &dest_y, dest_w, dest_h); for (y = src_tl_y; y < src_br_y; y++) for (x = src_tl_x; x < src_br_x; x++) { d_idx = LINEAR((dest_x + x - src_tl_x), (dest_y + y - src_tl_y), dest_w); s_idx = LINEAR(x, y, src_w); RS = MIN(255, ((float)RD / (RS + 1)) * 256); GS = MIN(255, ((float)GD / (GS + 1)) * 256); BS = MIN(255, ((float)BD / (BS + 1)) * 256); // AS = MIN(AD, AS); } combine_pixels_normal(src, src_w, src_h, dest, dest_w, dest_h, dest_x, dest_y); } void combine_pixels_screen (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y) { int x, y, s_idx, d_idx; int src_tl_x = 0, src_tl_y = 0; int src_br_x = src_w, src_br_y = src_h; _clip(&src_tl_x, &src_tl_y, &src_br_x, &src_br_y, &dest_x, &dest_y, dest_w, dest_h); for (y = src_tl_y; y < src_br_y; y++) for (x = src_tl_x; x < src_br_x; x++) { d_idx = LINEAR((dest_x + x - src_tl_x), (dest_y + y - src_tl_y), dest_w); s_idx = LINEAR(x, y, src_w); RD = 255 - (((255 - RD) * (255 - RS)) >> 8); GD = 255 - (((255 - GD) * (255 - GS)) >> 8); BD = 255 - (((255 - BD) * (255 - BS)) >> 8); // AD = MIN(AD, AS); } } void combine_pixels_overlay (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y) { int x, y, s_idx, d_idx; int src_tl_x = 0, src_tl_y = 0; int src_br_x = src_w, src_br_y = src_h; int tmp_screen, tmp_mult; _clip(&src_tl_x, &src_tl_y, &src_br_x, &src_br_y, &dest_x, &dest_y, dest_w, dest_h); for (y = src_tl_y; y < src_br_y; y++) for (x = src_tl_x; x < src_br_x; x++) { d_idx = LINEAR((dest_x + x - src_tl_x), (dest_y + y - src_tl_y), dest_w); s_idx = LINEAR(x, y, src_w); tmp_screen = 255 - (((255 - RD) * (255 - RS)) >> 8); tmp_mult = (RD * RS) >> 8; RD = (RD * tmp_screen + (255 - RD) * tmp_mult) >> 8; tmp_screen = 255 - (((255 - GD) * (255 - GS)) >> 8); tmp_mult = (GD * GS) >> 8; GD = (GD * tmp_screen + (255 - GD) * tmp_mult) >> 8; tmp_screen = 255 - (((255 - BD) * (255 - BS)) >> 8); tmp_mult = (BD * BS) >> 8; BD = (BD * tmp_screen + (255 - BD) * tmp_mult) >> 8; // AD = MIN(AD, AS); } } static void combine_pixels_hsv (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y, int mode) { int x, y, s_idx, d_idx; int src_tl_x = 0, src_tl_y = 0; int src_br_x = src_w, src_br_y = src_h; _clip(&src_tl_x, &src_tl_y, &src_br_x, &src_br_y, &dest_x, &dest_y, dest_w, dest_h); for (y = src_tl_y; y < src_br_y; y++) for (x = src_tl_x; x < src_br_x; x++) { d_idx = LINEAR((dest_x + x - src_tl_x), (dest_y + y - src_tl_y), dest_w); s_idx = LINEAR(x, y, src_w); rgb_to_hsv(&RS, &GS, &BS); rgb_to_hsv(&RD, &GD, &BD); switch (mode) { case 0: /* hue mode */ RD = RS; break; case 1: /* saturation mode */ GD = GS; break; case 2: /* value mode */ BD = BS; break; default: break; } hsv_to_rgb(&RD, &GD, &BD); // AD = MIN(AD, AS); } } void combine_pixels_hue (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y) { combine_pixels_hsv(src, src_w, src_h, dest, dest_w, dest_h, dest_x, dest_y, 0); } void combine_pixels_sat (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y) { combine_pixels_hsv(src, src_w, src_h, dest, dest_w, dest_h, dest_x, dest_y, 1); } void combine_pixels_val (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y) { combine_pixels_hsv(src, src_w, src_h, dest, dest_w, dest_h, dest_x, dest_y, 2); } void combine_pixels_col (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y) { int x, y, s_idx, d_idx; int src_tl_x = 0, src_tl_y = 0; int src_br_x = src_w, src_br_y = src_h; _clip(&src_tl_x, &src_tl_y, &src_br_x, &src_br_y, &dest_x, &dest_y, dest_w, dest_h); for (y = src_tl_y; y < src_br_y; y++) for (x = src_tl_x; x < src_br_x; x++) { d_idx = LINEAR((dest_x + x - src_tl_x), (dest_y + y - src_tl_y), dest_w); s_idx = LINEAR(x, y, src_w); rgb_to_hls(&RS, &GS, &BS); rgb_to_hls(&RD, &GD, &BD); RD = RS; BD = BS; hls_to_rgb(&RD, &GD, &BD); // AD = MIN(AD, AS); } } void combine_pixels_diss (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y) { int x, y, s_idx, d_idx; int src_tl_x = 0, src_tl_y = 0; int src_br_x = src_w, src_br_y = src_h; srand(12345); _clip(&src_tl_x, &src_tl_y, &src_br_x, &src_br_y, &dest_x, &dest_y, dest_w, dest_h); for (y = src_tl_y; y < src_br_y; y++) for (x = src_tl_x; x < src_br_x; x++) { d_idx = LINEAR((dest_x + x - src_tl_x), (dest_y + y - src_tl_y), dest_w); s_idx = LINEAR(x, y, src_w); if ((rand() % 255) < AS) { int b; unsigned char src_alpha; unsigned char new_alpha; float ratio, compl_ratio; unsigned long tmp; src_alpha = AS; if (src_alpha != 0) { if (src_alpha == 255) new_alpha = src_alpha; else new_alpha = AD + INT_MULT((255u - AD), src_alpha, tmp); b = 3; if (new_alpha != 0) { ratio = (float) src_alpha / new_alpha; compl_ratio = 1.0 - ratio; do { b--; dest[d_idx + b] = (unsigned char) (src[s_idx + b] * ratio + dest[d_idx + b] * compl_ratio + EPS); } while (b); } AD = new_alpha; } } } }