/* The GIMP -- an image manipulation program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * 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 of the License, 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. * */ /* * This file is supposed to contain the generic (read: C) implementation * of the pixel fiddling paint-functions. */ #include "config.h" #include #include #include #include "libgimpcolor/gimpcolor.h" #include "base/base-types.h" #include "gimp-composite.h" #include "gimp-composite-generic.h" #define OPAQUE_OPACITY 255 #define TRANSPARENT_OPACITY 0 #define RANDOM_TABLE_SIZE 4096 #define INT_MULT(a,b,t) ((t) = (a) * (b) + 0x80, ((((t) >> 8) + (t)) >> 8)) /* A drawable has an alphachannel if contains either 4 or 2 bytes data * aka GRAYA and RGBA and thus the macro below works. This will have * to change if we support bigger formats. We'll do it so for now because * masking is always cheaper than passing parameters over the stack. */ /* FIXME: Move to a global place */ #define HAS_ALPHA(bytes) (~bytes & 1) static guchar add_lut[511]; static gint32 random_table[RANDOM_TABLE_SIZE]; static guchar burn_lut[256][256]; /* * * Pixel format type conversion * * XXX This implementation will not work for >8 bit colours. * XXX This implementation is totally wrong. */ /** * gimp_composite_convert_any_any_any_generic: * @ctx: * * **/ void gimp_composite_convert_any_any_any_generic (GimpCompositeContext *ctx) { int i; int j; unsigned char *D = ctx->D; unsigned char *A = ctx->A; int bpp_A = gimp_composite_pixel_bpp[ctx->pixelformat_A]; int bpp_D = gimp_composite_pixel_bpp[ctx->pixelformat_D]; for (i = 0; i < ctx->n_pixels; i++) { for (j = 0; j < bpp_A; j++) { D[j] = A[j]; } D[j] = GIMP_COMPOSITE_ALPHA_OPAQUE; A += bpp_A; D += bpp_D; } } /** * gimp_composite_color_any_any_any_generic: * @dest: * @color: * @w: * @bytes: * * **/ void gimp_composite_color_any_any_any_generic (guchar *dest, const guchar *color, guint w, guint bytes) { /* dest % bytes and color % bytes must be 0 or we will crash when bytes = 2 or 4. Is this safe to assume? Lets find out. This is 4-7X as fast as the simple version. */ #if defined(sparc) || defined(__sparc__) guchar c0, c1, c2, c3; #else guchar c0, c1, c2; guint32 *longd, longc; guint16 *shortd, shortc; #endif switch (bytes) { case 1: memset(dest, *color, w); break; case 2: #if defined(sparc) || defined(__sparc__) c0 = color[0]; c1 = color[1]; while (w--) { dest[0] = c0; dest[1] = c1; dest += 2; } #else shortc = ((guint16 *) color)[0]; shortd = (guint16 *) dest; while (w--) { *shortd = shortc; shortd++; } #endif /* sparc || __sparc__ */ break; case 3: c0 = color[0]; c1 = color[1]; c2 = color[2]; while (w--) { dest[0] = c0; dest[1] = c1; dest[2] = c2; dest += 3; } break; case 4: #if defined(sparc) || defined(__sparc__) c0 = color[0]; c1 = color[1]; c2 = color[2]; c3 = color[3]; while (w--) { dest[0] = c0; dest[1] = c1; dest[2] = c2; dest[3] = c3; dest += 4; } #else longc = ((guint32 *) color)[0]; longd = (guint32 *) dest; while (w--) { *longd = longc; longd++; } #endif /* sparc || __sparc__ */ break; default: while (w--) { memcpy(dest, color, bytes); dest += bytes; } } } /** * gimp_composite_blend_any_any_any_generic: * @ctx: The compositing context. * * Perform a blend operation between sources ctx->A and ctx->B, using * the generalised algorithm: D = A * (255 - β) + B * β * * The result is left in ctx->D **/ void gimp_composite_blend_any_any_any_generic (GimpCompositeContext *ctx) { guchar *src1 = ctx->A; guchar *src2 = ctx->B; guchar *dest = ctx->D; guchar blend = ctx->blend.blend; guint bytes = gimp_composite_pixel_bpp[ctx->pixelformat_A]; guint w = ctx->n_pixels; guint b; const guchar blend2 = (255 - blend); while (w--) { for (b = 0; b < bytes; b++) dest[b] = (src1[b] * blend2 + src2[b] * blend) / 255; src1 += bytes; src2 += bytes; dest += bytes; } } #if 0 void gimp_composite_shade_generic (const guchar *src, guchar *dest, const guchar *col, guchar blend, guint w, guint bytes, guint has_alpha) { const guchar blend2 = (255 - blend); const guint alpha = (has_alpha) ? bytes - 1 : bytes; guint b; while (w--) { for (b = 0; b < alpha; b++) dest[b] = (src[b] * blend2 + col[b] * blend) / 255; if (has_alpha) dest[alpha] = src[alpha]; /* alpha channel */ src += bytes; dest += bytes; } } #endif /** * gimp_composite_darken_any_any_any_generic: * @ctx: The compositing context. * * Perform a darken operation between sources ctx->A and ctx->B, using * the generalised algorithm: * D_r = min(A_r, B_r); * D_g = min(A_g, B_g); * D_b = min(A_b, B_b); * D_a = min(A_a, B_a); * **/ void gimp_composite_darken_any_any_any_generic (GimpCompositeContext * ctx) { const guchar *src1 = ctx->A; const guchar *src2 = ctx->B; guchar *dest = ctx->D; guint length = ctx->n_pixels; guint bytes1 = gimp_composite_pixel_bpp[ctx->pixelformat_A]; guint bytes2 = gimp_composite_pixel_bpp[ctx->pixelformat_B]; const guint has_alpha1 = HAS_ALPHA(bytes1); const guint has_alpha2 = HAS_ALPHA(bytes2); const guint alpha = (has_alpha1 || has_alpha2) ? MAX(bytes1, bytes2) - 1 : bytes1; guint b; guchar s1, s2; while (length--) { for (b = 0; b < alpha; b++) { s1 = src1[b]; s2 = src2[b]; dest[b] = (s1 < s2) ? s1 : s2; } if (has_alpha1 && has_alpha2) dest[alpha] = MIN(src1[alpha], src2[alpha]); else if (has_alpha2) dest[alpha] = src2[alpha]; src1 += bytes1; src2 += bytes2; dest += bytes2; } } /** * gimp_composite_lighten_any_any_any_generic: * @ctx: The compositing context. * * Perform a lighten operation between sources ctx->A and ctx->B, using the * generalised algorithm: * D_r = max(A_r, B_r); * D_g = max(A_g, B_g); * D_b = max(A_b, B_b); * D_a = min(A_a, B_a); * **/ void gimp_composite_lighten_any_any_any_generic (GimpCompositeContext * ctx) { const guchar *src1 = ctx->A; const guchar *src2 = ctx->B; guchar *dest = ctx->D; guint length = ctx->n_pixels; guint bytes1 = gimp_composite_pixel_bpp[ctx->pixelformat_A]; guint bytes2 = gimp_composite_pixel_bpp[ctx->pixelformat_B]; const guint has_alpha1 = HAS_ALPHA(bytes1); const guint has_alpha2 = HAS_ALPHA(bytes2); const guint alpha = (has_alpha1 || has_alpha2) ? MAX(bytes1, bytes2) - 1 : bytes1; guint b; guchar s1, s2; while (length--) { for (b = 0; b < alpha; b++) { s1 = src1[b]; s2 = src2[b]; dest[b] = (s1 < s2) ? s2 : s1; } if (has_alpha1 && has_alpha2) dest[alpha] = MIN(src1[alpha], src2[alpha]); else if (has_alpha2) dest[alpha] = src2[alpha]; src1 += bytes1; src2 += bytes2; dest += bytes2; } } /** * gimp_composite_hue_any_any_any_generic: * @ctx: The compositing context. * * Perform a conversion to hue only of the source ctx->A using * the hue of ctx->B. **/ void gimp_composite_hue_any_any_any_generic (GimpCompositeContext * ctx) { const guchar *src1 = ctx->A; const guchar *src2 = ctx->B; guchar *dest = ctx->D; guint length = ctx->n_pixels; guint bytes1 = gimp_composite_pixel_bpp[ctx->pixelformat_A]; guint bytes2 = gimp_composite_pixel_bpp[ctx->pixelformat_B]; const guint has_alpha1 = HAS_ALPHA(bytes1); const guint has_alpha2 = HAS_ALPHA(bytes2); gint r1, g1, b1; gint r2, g2, b2; if (bytes1 > 2) { /* assumes inputs are only 4 byte RGBA pixels */ while (length--) { r1 = src1[0]; g1 = src1[1]; b1 = src1[2]; r2 = src2[0]; g2 = src2[1]; b2 = src2[2]; gimp_rgb_to_hsv_int (&r1, &g1, &b1); gimp_rgb_to_hsv_int (&r2, &g2, &b2); /* Composition should have no effect if saturation is zero. * otherwise, black would be painted red (see bug #123296). */ if (g2) r1 = r2; /* set the destination */ gimp_hsv_to_rgb_int (&r1, &g1, &b1); dest[0] = r1; dest[1] = g1; dest[2] = b1; if (has_alpha1 && has_alpha2) dest[3] = MIN (src1[3], src2[3]); else if (has_alpha2) dest[3] = src2[3]; src1 += bytes1; src2 += bytes2; dest += bytes2; } } else { ctx->D = ctx->B; } } /** * gimp_composite_saturation_any_any_any_generic: * @ctx: The compositing context. * * Perform a conversion to saturation only of the source ctx->A using * the saturation level of ctx->B. * **/ void gimp_composite_saturation_any_any_any_generic (GimpCompositeContext * ctx) { const guchar *src1 = ctx->A; const guchar *src2 = ctx->B; guchar *dest = ctx->D; guint length = ctx->n_pixels; guint bytes1 = gimp_composite_pixel_bpp[ctx->pixelformat_A]; guint bytes2 = gimp_composite_pixel_bpp[ctx->pixelformat_B]; const guint has_alpha1 = HAS_ALPHA(bytes1); const guint has_alpha2 = HAS_ALPHA(bytes2); gint r1, g1, b1; gint r2, g2, b2; if (bytes1 > 2) { /* assumes inputs are only 4 byte RGBA pixels */ while (length--) { r1 = src1[0]; g1 = src1[1]; b1 = src1[2]; r2 = src2[0]; g2 = src2[1]; b2 = src2[2]; gimp_rgb_to_hsv_int(&r1, &g1, &b1); gimp_rgb_to_hsv_int(&r2, &g2, &b2); g1 = g2; /* set the destination */ gimp_hsv_to_rgb_int(&r1, &g1, &b1); dest[0] = r1; dest[1] = g1; dest[2] = b1; if (has_alpha1 && has_alpha2) dest[3] = MIN(src1[3], src2[3]); else if (has_alpha2) dest[3] = src2[3]; src1 += bytes1; src2 += bytes2; dest += bytes2; } } else { ctx->D = ctx->B; } } /** * gimp_composite_value_any_any_any_generic: * @ctx: The compositing context. * * Perform a conversion to value only of the source ctx->A using * the value of ctx->B. * **/ void gimp_composite_value_any_any_any_generic (GimpCompositeContext * ctx) { const guchar *src1 = ctx->A; const guchar *src2 = ctx->B; guchar *dest = ctx->D; guint length = ctx->n_pixels; guint bytes1 = gimp_composite_pixel_bpp[ctx->pixelformat_A]; guint bytes2 = gimp_composite_pixel_bpp[ctx->pixelformat_B]; const guint has_alpha1 = HAS_ALPHA(bytes1); const guint has_alpha2 = HAS_ALPHA(bytes2); gint r1, g1, b1; gint r2, g2, b2; if (bytes1 > 2) { /* assumes inputs are only 4 byte RGBA pixels */ /* assumes inputs are only 4 byte RGBA pixels */ while (length--) { r1 = src1[0]; g1 = src1[1]; b1 = src1[2]; r2 = src2[0]; g2 = src2[1]; b2 = src2[2]; gimp_rgb_to_hsv_int(&r1, &g1, &b1); gimp_rgb_to_hsv_int(&r2, &g2, &b2); b1 = b2; /* set the destination */ gimp_hsv_to_rgb_int(&r1, &g1, &b1); dest[0] = r1; dest[1] = g1; dest[2] = b1; if (has_alpha1 && has_alpha2) dest[3] = MIN(src1[3], src2[3]); else if (has_alpha2) dest[3] = src2[3]; src1 += bytes1; src2 += bytes2; dest += bytes2; } } else { ctx->D = ctx->B; } } /** * gimp_composite_color_only_any_any_any_generic: * @ctx: The compositing context. * * Perform a conversion to of the source ctx->A using * the hue and saturation values of ctx->B. * **/ void gimp_composite_color_only_any_any_any_generic (GimpCompositeContext * ctx) { const guchar *src1 = ctx->A; const guchar *src2 = ctx->B; guchar *dest = ctx->D; guint length = ctx->n_pixels; guint bytes1 = gimp_composite_pixel_bpp[ctx->pixelformat_A]; guint bytes2 = gimp_composite_pixel_bpp[ctx->pixelformat_B]; const guint has_alpha1 = HAS_ALPHA(bytes1); const guint has_alpha2 = HAS_ALPHA(bytes2); gint r1, g1, b1; gint r2, g2, b2; if (bytes1 > 2) { /* assumes inputs are only 4 byte RGBA pixels */ while (length--) { r1 = src1[0]; g1 = src1[1]; b1 = src1[2]; r2 = src2[0]; g2 = src2[1]; b2 = src2[2]; gimp_rgb_to_hsl_int(&r1, &g1, &b1); gimp_rgb_to_hsl_int(&r2, &g2, &b2); /* transfer hue and saturation to the source pixel */ r1 = r2; g1 = g2; /* set the destination */ gimp_hsl_to_rgb_int(&r1, &g1, &b1); dest[0] = r1; dest[1] = g1; dest[2] = b1; if (has_alpha1 && has_alpha2) dest[3] = MIN(src1[3], src2[3]); else if (has_alpha2) dest[3] = src2[3]; src1 += bytes1; src2 += bytes2; dest += bytes2; } } else { ctx->D = ctx->B; } } /** * gimp_composite_behind_any_any_any_generic: * @ctx: The compositing context. * * Perform a behind operation to between the pixel sources ctx->A and ctx->B. * **/ void gimp_composite_behind_any_any_any_generic (GimpCompositeContext * ctx) { ctx->D = ctx->B; ctx->combine = gimp_composite_pixel_alphap[ctx->pixelformat_A] ? BEHIND_INTEN : NO_COMBINATION; } /** * gimp_composite_multiply_any_any_any_generic: * @ctx: The compositing context. * * Perform an RGB[A] multiply operation between the pixel sources * ctx->A and ctx->B. * **/ void gimp_composite_multiply_any_any_any_generic (GimpCompositeContext * ctx) { const guchar *src1 = ctx->A; const guchar *src2 = ctx->B; guchar *dest = ctx->D; guint length = ctx->n_pixels; guint bytes1 = gimp_composite_pixel_bpp[ctx->pixelformat_A]; guint bytes2 = gimp_composite_pixel_bpp[ctx->pixelformat_B]; const guint has_alpha1 = HAS_ALPHA(bytes1); const guint has_alpha2 = HAS_ALPHA(bytes2); const guint alpha = (has_alpha1 || has_alpha2) ? MAX(bytes1, bytes2) - 1 : bytes1; guint b, tmp; if (has_alpha1 && has_alpha2) { while (length--) { for (b = 0; b < alpha; b++) dest[b] = INT_MULT(src1[b], src2[b], tmp); dest[alpha] = MIN(src1[alpha], src2[alpha]); src1 += bytes1; src2 += bytes2; dest += bytes2; } } else if (has_alpha2) { while (length--) { for (b = 0; b < alpha; b++) dest[b] = INT_MULT(src1[b], src2[b], tmp); dest[alpha] = src2[alpha]; src1 += bytes1; src2 += bytes2; dest += bytes2; } } else { while (length--) { for (b = 0; b < alpha; b++) dest[b] = INT_MULT(src1[b], src2[b], tmp); src1 += bytes1; src2 += bytes2; dest += bytes2; } } } /** * gimp_composite_divide_any_any_any_generic: * @ctx: The compositing context. * * Perform an RGB[A] divide operation between the pixel sources ctx->A * and ctx->B. ctx->A is the numerator, ctx->B the denominator. * **/ void gimp_composite_divide_any_any_any_generic (GimpCompositeContext * ctx) { const guchar *src1 = ctx->A; const guchar *src2 = ctx->B; guchar *dest = ctx->D; guint length = ctx->n_pixels; guint bytes1 = gimp_composite_pixel_bpp[ctx->pixelformat_A]; guint bytes2 = gimp_composite_pixel_bpp[ctx->pixelformat_B]; const guint has_alpha1 = HAS_ALPHA(bytes1); const guint has_alpha2 = HAS_ALPHA(bytes2); const guint alpha = (has_alpha1 || has_alpha2) ? MAX(bytes1, bytes2) - 1 : bytes1; guint b, result; while (length--) { for (b = 0; b < alpha; b++) { result = ((src1[b] * 256) / (1 + src2[b])); dest[b] = MIN(result, 255); } if (has_alpha1 && has_alpha2) dest[alpha] = MIN(src1[alpha], src2[alpha]); else if (has_alpha2) dest[alpha] = src2[alpha]; src1 += bytes1; src2 += bytes2; dest += bytes2; } } /** * gimp_composite_screen_any_any_any_generic: * @ctx: The compositing context. * * Perform an RGB[A] screen operation between the pixel sources * ctx->A and ctx->B, using the generalised algorithm: * * D = 255 - (255 - A) * (255 - B) * **/ void gimp_composite_screen_any_any_any_generic (GimpCompositeContext * ctx) { const guchar *src1 = ctx->A; const guchar *src2 = ctx->B; guchar *dest = ctx->D; guint length = ctx->n_pixels; guint bytes1 = gimp_composite_pixel_bpp[ctx->pixelformat_A]; guint bytes2 = gimp_composite_pixel_bpp[ctx->pixelformat_B]; const guint has_alpha1 = HAS_ALPHA(bytes1); const guint has_alpha2 = HAS_ALPHA(bytes2); const guint alpha = (has_alpha1 || has_alpha2) ? MAX(bytes1, bytes2) - 1 : bytes1; guint b, tmp; while (length--) { for (b = 0; b < alpha; b++) dest[b] = 255 - INT_MULT((255 - src1[b]), (255 - src2[b]), tmp); if (has_alpha1 && has_alpha2) dest[alpha] = MIN(src1[alpha], src2[alpha]); else if (has_alpha2) dest[alpha] = src2[alpha]; src1 += bytes1; src2 += bytes2; dest += bytes2; } } /** * gimp_composite_overlay_any_any_any_generic: * @ctx: The compositing context. * * Perform an RGB[A] overlay operation between the pixel sources * ctx->A and ctx->B, using the generalised algorithm: * * D = A * (A + (2 * B) * (255 - A)) * **/ void gimp_composite_overlay_any_any_any_generic (GimpCompositeContext * ctx) { const guchar *src1 = ctx->A; const guchar *src2 = ctx->B; guchar *dest = ctx->D; guint length = ctx->n_pixels; guint bytes1 = gimp_composite_pixel_bpp[ctx->pixelformat_A]; guint bytes2 = gimp_composite_pixel_bpp[ctx->pixelformat_B]; const guint has_alpha1 = HAS_ALPHA(bytes1); const guint has_alpha2 = HAS_ALPHA(bytes2); const guint alpha = (has_alpha1 || has_alpha2) ? MAX(bytes1, bytes2) - 1 : bytes1; guint b, tmp, tmpM; while (length--) { for (b = 0; b < alpha; b++) { dest[b] = INT_MULT(src1[b], src1[b] + INT_MULT(2 * src2[b], 255 - src1[b], tmpM), tmp); } if (has_alpha1 && has_alpha2) dest[alpha] = MIN(src1[alpha], src2[alpha]); else if (has_alpha2) dest[alpha] = src2[alpha]; src1 += bytes1; src2 += bytes2; dest += bytes2; } } /** * gimp_composite_dodge_any_any_any_generic: * @ctx: The compositing context. * * Perform an RGB[A] dodge operation between the pixel sources * ctx->A and ctx->B, using the generalised algorithm: * * D = saturation of 255 or (A * 256) / (256 - B) * **/ void gimp_composite_dodge_any_any_any_generic (GimpCompositeContext * ctx) { const guchar *src1 = ctx->A; const guchar *src2 = ctx->B; guchar *dest = ctx->D; guint length = ctx->n_pixels; guint bytes1 = gimp_composite_pixel_bpp[ctx->pixelformat_A]; guint bytes2 = gimp_composite_pixel_bpp[ctx->pixelformat_B]; const guint has_alpha1 = HAS_ALPHA(bytes1); const guint has_alpha2 = HAS_ALPHA(bytes2); const guint alpha = (has_alpha1 || has_alpha2) ? MAX(bytes1, bytes2) - 1 : bytes1; guint b, tmp; while (length--) { for (b = 0; b < alpha; b++) { tmp = src1[b] << 8; tmp /= 256 - src2[b]; dest[b] = (guchar) MIN (tmp, 255); } if (has_alpha1 && has_alpha2) dest[alpha] = MIN (src1[alpha], src2[alpha]); else if (has_alpha2) dest[alpha] = src2[alpha]; src1 += bytes1; src2 += bytes2; dest += bytes2; } } /** * gimp_composite_burn_any_any_any_generic: * @ctx: The compositing context. * * Perform an RGB[A] dodge operation between the pixel sources * ctx->A and ctx->B, using the generalised algorithm: * * D = saturation of 255 or depletion of 0, of ((255 - A) * 256) / (B + 1) * **/ void gimp_composite_burn_any_any_any_generic (GimpCompositeContext * ctx) { const guchar *src1 = ctx->A; const guchar *src2 = ctx->B; guchar *dest = ctx->D; guint length = ctx->n_pixels; guint bytes1 = gimp_composite_pixel_bpp[ctx->pixelformat_A]; guint bytes2 = gimp_composite_pixel_bpp[ctx->pixelformat_B]; const guint has_alpha1 = HAS_ALPHA(bytes1); const guint has_alpha2 = HAS_ALPHA(bytes2); const guint alpha = (has_alpha1 || has_alpha2) ? MAX(bytes1, bytes2) - 1 : bytes1; guint b; while (length--) { for (b = 0; b < alpha; b++) dest[b] = burn_lut[src1[b]][src2[b]]; if (has_alpha1 && has_alpha2) dest[alpha] = MIN(src1[alpha], src2[alpha]); else if (has_alpha2) dest[alpha] = src2[alpha]; src1 += bytes1; src2 += bytes2; dest += bytes2; } } /** * gimp_composite_hardlight_any_any_any_generic: * @ctx: The compositing context. * * Perform an RGB[A] hardlight operation between the pixel sources * ctx->A and ctx->B. * **/ void gimp_composite_hardlight_any_any_any_generic (GimpCompositeContext * ctx) { const guchar *src1 = ctx->A; const guchar *src2 = ctx->B; guchar *dest = ctx->D; guint length = ctx->n_pixels; guint bytes1 = gimp_composite_pixel_bpp[ctx->pixelformat_A]; guint bytes2 = gimp_composite_pixel_bpp[ctx->pixelformat_B]; const guint has_alpha1 = HAS_ALPHA(bytes1); const guint has_alpha2 = HAS_ALPHA(bytes2); const guint alpha = (has_alpha1 || has_alpha2) ? MAX(bytes1, bytes2) - 1 : bytes1; guint b, tmp; while (length--) { for (b = 0; b < alpha; b++) { if (src2[b] > 128) { tmp = ((gint) 255 - src1[b]) * ((gint) 255 - ((src2[b] - 128) << 1)); dest[b] = (guchar) MIN (255 - (tmp >> 8), 255); } else { tmp = (gint) src1[b] * ((gint) src2[b] << 1); dest[b] = (guchar) MIN (tmp >> 8, 255); } } if (has_alpha1 && has_alpha2) dest[alpha] = MIN(src1[alpha], src2[alpha]); else if (has_alpha2) dest[alpha] = src2[alpha]; src1 += bytes1; src2 += bytes2; dest += bytes2; } } /** * gimp_composite_softlight_any_any_any_generic: * @ctx: The compositing context. * * Perform an RGB[A] softlight operation between the pixel sources * ctx->A and ctx->B. * **/ void gimp_composite_softlight_any_any_any_generic (GimpCompositeContext * ctx) { const guchar *src1 = ctx->A; const guchar *src2 = ctx->B; guchar *dest = ctx->D; guint length = ctx->n_pixels; guint bytes1 = gimp_composite_pixel_bpp[ctx->pixelformat_A]; guint bytes2 = gimp_composite_pixel_bpp[ctx->pixelformat_B]; const guint has_alpha1 = gimp_composite_pixel_alphap[ctx->pixelformat_A]; const guint has_alpha2 = gimp_composite_pixel_alphap[ctx->pixelformat_B]; const guint alpha = (has_alpha1 || has_alpha2) ? MAX(bytes1, bytes2) - 1 : bytes1; guint b, tmpS, tmpM, tmp1, tmp2, tmp3; while (length--) { for (b = 0; b < alpha; b++) { /* Mix multiply and screen */ tmpM = INT_MULT(src1[b], src2[b], tmpM); tmpS = 255 - INT_MULT((255 - src1[b]), (255 - src2[b]), tmp1); dest[b] = INT_MULT((255 - src1[b]), tmpM, tmp2) + INT_MULT(src1[b], tmpS, tmp3); } if (has_alpha1 && has_alpha2) dest[alpha] = MIN(src1[alpha], src2[alpha]); else if (has_alpha2) dest[alpha] = src2[alpha]; src1 += bytes1; src2 += bytes2; dest += bytes2; } } /** * gimp_composite_grain_extract_any_any_any_generic: * @ctx: The compositing context. * * Perform an RGB[A] grain-extract operation between the pixel sources * ctx->A and ctx->B. * **/ void gimp_composite_grain_extract_any_any_any_generic (GimpCompositeContext * ctx) { const guchar *src1 = ctx->A; const guchar *src2 = ctx->B; guchar *dest = ctx->D; guint length = ctx->n_pixels; guint bytes1 = gimp_composite_pixel_bpp[ctx->pixelformat_A]; guint bytes2 = gimp_composite_pixel_bpp[ctx->pixelformat_B]; const guint has_alpha1 = gimp_composite_pixel_alphap[ctx->pixelformat_A]; const guint has_alpha2 = gimp_composite_pixel_alphap[ctx->pixelformat_B]; const guint alpha = (has_alpha1 || has_alpha2) ? MAX(bytes1, bytes2) - 1 : bytes1; guint b; gint diff; while (length--) { for (b = 0; b < alpha; b++) { diff = src1[b] - src2[b] + 128; dest[b] = (guchar) CLAMP(diff, 0, 255); } if (has_alpha1 && has_alpha2) dest[alpha] = MIN(src1[alpha], src2[alpha]); else if (has_alpha2) dest[alpha] = src2[alpha]; src1 += bytes1; src2 += bytes2; dest += bytes2; } } /** * gimp_composite_grain_merge_any_any_any_generic: * @ctx: The compositing context. * * Perform an RGB[A] grain-merge operation between the pixel sources * ctx->A and ctx->B. * **/ void gimp_composite_grain_merge_any_any_any_generic (GimpCompositeContext * ctx) { const guchar *src1 = ctx->A; const guchar *src2 = ctx->B; guchar *dest = ctx->D; guint length = ctx->n_pixels; guint bytes1 = gimp_composite_pixel_bpp[ctx->pixelformat_A]; guint bytes2 = gimp_composite_pixel_bpp[ctx->pixelformat_B]; const guint has_alpha1 = HAS_ALPHA(bytes1); const guint has_alpha2 = HAS_ALPHA(bytes2); const guint alpha = (has_alpha1 || has_alpha2) ? MAX(bytes1, bytes2) - 1 : bytes1; guint b; gint sum; while (length--) { for (b = 0; b < alpha; b++) { /* Add, re-center and clip. */ sum = src1[b] + src2[b] - 128; dest[b] = (guchar) CLAMP(sum, 0, 255); } if (has_alpha1 && has_alpha2) dest[alpha] = MIN(src1[alpha], src2[alpha]); else if (has_alpha2) dest[alpha] = src2[alpha]; src1 += bytes1; src2 += bytes2; dest += bytes2; } } /** * gimp_composite_addition_any_any_any_generic: * @ctx: The compositing context. * * Perform an RGB[A] addition operation of the pixel sources ctx->A * and ctx->B. * **/ void gimp_composite_addition_any_any_any_generic (GimpCompositeContext * ctx) { const guchar *A = ctx->A; const guchar *B = ctx->B; guchar *D = ctx->D; guint length = ctx->n_pixels; guint bytes1 = gimp_composite_pixel_bpp[ctx->pixelformat_A]; guint bytes2 = gimp_composite_pixel_bpp[ctx->pixelformat_B]; const guint has_alpha1 = gimp_composite_pixel_alphap[ctx->pixelformat_A]; const guint has_alpha2 = gimp_composite_pixel_alphap[ctx->pixelformat_B]; const guint alpha = (has_alpha1 || has_alpha2) ? MAX(bytes1, bytes2) - 1 : bytes1; guint b; if (has_alpha1 && has_alpha2) { while (length--) { for (b = 0; b < alpha; b++) D[b] = add_lut[A[b] + B[b]]; D[alpha] = MIN(A[alpha], B[alpha]); A += bytes1; B += bytes2; D += bytes2; } } else if (has_alpha2) { while (length--) { for (b = 0; b < alpha; b++) D[b] = add_lut[A[b] + B[b]]; D[alpha] = B[alpha]; A += bytes1; B += bytes2; D += bytes2; } } else { while (length--) { for (b = 0; b < alpha; b++) D[b] = add_lut[A[b] + B[b]]; A += bytes1; B += bytes2; D += bytes2; } } } /** * gimp_composite_subtract_any_any_any_generic: * @ctx: The compositing context. * * Perform an RGB[A] subtract operation of the pixel source * ctx-B from ctx->A. * **/ void gimp_composite_subtract_any_any_any_generic (GimpCompositeContext * ctx) { const guchar *src1 = ctx->A; const guchar *src2 = ctx->B; guchar *dest = ctx->D; guint length = ctx->n_pixels; guint bytes1 = gimp_composite_pixel_bpp[ctx->pixelformat_A]; guint bytes2 = gimp_composite_pixel_bpp[ctx->pixelformat_B]; const guint has_alpha1 = gimp_composite_pixel_alphap[ctx->pixelformat_A]; const guint has_alpha2 = gimp_composite_pixel_alphap[ctx->pixelformat_B]; const guint alpha = (has_alpha1 || has_alpha2) ? MAX(bytes1, bytes2) - 1 : bytes1; guint b; gint diff; while (length--) { for (b = 0; b < alpha; b++) { diff = src1[b] - src2[b]; dest[b] = (diff < 0) ? 0 : diff; } if (has_alpha1 && has_alpha2) dest[alpha] = MIN(src1[alpha], src2[alpha]); else if (has_alpha2) dest[alpha] = src2[alpha]; src1 += bytes1; src2 += bytes2; dest += bytes2; } } /** * gimp_composite_difference_any_any_any_generic: * @ctx: The compositing context. * * Perform an RGB[A] difference operation between the pixel sources * ctx->A and ctx->B. * **/ void gimp_composite_difference_any_any_any_generic (GimpCompositeContext * ctx) { const guchar *src1 = ctx->A; const guchar *src2 = ctx->B; guchar *dest = ctx->D; guint length = ctx->n_pixels; guint bytes1 = gimp_composite_pixel_bpp[ctx->pixelformat_A]; guint bytes2 = gimp_composite_pixel_bpp[ctx->pixelformat_B]; const guint has_alpha1 = gimp_composite_pixel_alphap[ctx->pixelformat_A]; const guint has_alpha2 = gimp_composite_pixel_alphap[ctx->pixelformat_B]; const guint alpha = (has_alpha1 || has_alpha2) ? MAX(bytes1, bytes2) - 1 : bytes1; guint b; gint diff; while (length--) { for (b = 0; b < alpha; b++) { diff = src1[b] - src2[b]; dest[b] = (diff < 0) ? -diff : diff; } if (has_alpha1 && has_alpha2) dest[alpha] = MIN(src1[alpha], src2[alpha]); else if (has_alpha2) dest[alpha] = src2[alpha]; src1 += bytes1; src2 += bytes2; dest += bytes2; } } /** * gimp_composite_dissolve_any_any_any_generic: * @ctx: The compositing context. * * Perform an RGB[A] alpha dissolve operation between the pixel * sources ctx->A and ctx->B. * **/ void gimp_composite_dissolve_any_any_any_generic (GimpCompositeContext * ctx) { GRand *gr; gint alpha; gint b; gint combined_opacity; gint db; gint length = ctx->n_pixels; gint opacity = ctx->dissolve.opacity; gint sb = gimp_composite_pixel_bpp[ctx->pixelformat_B]; gint x = ctx->dissolve.x; gint y = ctx->dissolve.y; guchar *mask = ctx->M; gint32 rand_val; guchar *dest = ctx->D; guchar *src = ctx->B; guint has_alpha = gimp_composite_pixel_alphap[ctx->pixelformat_B]; /* * if destination does not have an alpha channel, add one to it. */ if (!gimp_composite_pixel_alphap[ctx->pixelformat_D]) { ctx->pixelformat_D = gimp_composite_pixel_alpha[ctx->pixelformat_D]; } db = gimp_composite_pixel_bpp[ctx->pixelformat_D]; gr = g_rand_new_with_seed(random_table[y % RANDOM_TABLE_SIZE]); for (b = 0; b < x; b ++) g_rand_int (gr); alpha = db - 1; /* * XXX NB: The mask is assumed to be a linear array of bytes, no * accounting for the mask being of a particular pixel format. */ while (length--) { /* preserve the intensity values */ for (b = 0; b < alpha; b++) dest[b] = src[b]; /* dissolve if random value is >= opacity */ rand_val = g_rand_int_range(gr, 0, 255); if (mask) { if (has_alpha) combined_opacity = opacity * src[alpha] * (*mask) / (255 * 255); else combined_opacity = opacity * (*mask) / 255; mask++; } else { if (has_alpha) combined_opacity = opacity * src[alpha] / 255; else combined_opacity = opacity; } dest[alpha] = (rand_val >= combined_opacity) ? TRANSPARENT_OPACITY : OPAQUE_OPACITY; dest += db; src += sb; } g_rand_free(gr); ctx->combine = gimp_composite_pixel_alphap[ctx->pixelformat_A] ? COMBINE_INTEN_A_INTEN_A : COMBINE_INTEN_INTEN_A; } /** * gimp_composite_replace_any_any_any_generic: * @ctx: The compositing context. * * Perform an RGB[A] replace operation of the pixel * source ctx->A with the ctx->B. * **/ void gimp_composite_replace_any_any_any_generic (GimpCompositeContext *ctx) { ctx->D = ctx->B; ctx->combine = REPLACE_INTEN; } /** * gimp_composite_swap_any_any_any_generic: * @ctx: The compositing context. * * Swap the contents of ctx->A and ctx->B. * **/ void gimp_composite_swap_any_any_any_generic (GimpCompositeContext * ctx) { guchar *src = ctx->A; guchar *dest = ctx->B; guint bytes1 = gimp_composite_pixel_bpp[ctx->pixelformat_A]; guint length = ctx->n_pixels * bytes1; while (length--) { guchar tmp = *dest; *dest = *src; *src = tmp; src++; dest++; } } /** * gimp_composite_normal_any_any_any_generic: * @ctx: The compositing context. * * Perform an RGB[A] over operation of the pixel * source ctx->B on ctx->A. * **/ void gimp_composite_normal_any_any_any_generic (GimpCompositeContext * ctx) { ctx->D = ctx->B; } /** * gimp_composite_erase_any_any_any_generic: * @ctx: The compositing context. * * Perform an RGB[A] "erase" operation of the pixel * source ctx->A using the alpha information in ctx->B. * **/ void gimp_composite_erase_any_any_any_generic (GimpCompositeContext *ctx) { ctx->D = ctx->B; ctx->combine = (gimp_composite_pixel_alphap[ctx->pixelformat_A] && gimp_composite_pixel_alphap[ctx->pixelformat_B]) ? ERASE_INTEN : 0; } /** * gimp_composite_anti_erase_any_any_any_generic: * @ctx: The compositing context. * * Perform an RGB[A] "anti-erase" operation of the pixel * source ctx->A using the alpha information in ctx->B. * **/ void gimp_composite_anti_erase_any_any_any_generic (GimpCompositeContext *ctx) { ctx->D = ctx->B; ctx->combine = (gimp_composite_pixel_alphap[ctx->pixelformat_A] && gimp_composite_pixel_alphap[ctx->pixelformat_B]) ? ANTI_ERASE_INTEN : 0; } /** * gimp_composite_color_erase_any_any_any_generic: * @ctx: The compositing context. * * Perform an RGB[A] "color-erase" operation of the pixel * source ctx->A using the alpha information in ctx->B. * **/ void gimp_composite_color_erase_any_any_any_generic (GimpCompositeContext *ctx) { ctx->D = ctx->B; ctx->combine = (gimp_composite_pixel_alphap[ctx->pixelformat_A] && gimp_composite_pixel_alphap[ctx->pixelformat_B]) ? COLOR_ERASE_INTEN : 0; } /** * gimp_composite_scale_any_any_any_generic: * @ctx: The compositing context. * * Perform an RGB[A] scale operation of the pixel source ctx->A using * the scale coefficient on the compositing context, @ctx. * **/ void gimp_composite_scale_any_any_any_generic (GimpCompositeContext * ctx) { const guchar *src1 = ctx->A; guchar *dest = ctx->D; guint length = ctx->n_pixels; guint bytes1 = (ctx->pixelformat_A == GIMP_PIXELFORMAT_V8) ? 1 : (ctx->pixelformat_A == GIMP_PIXELFORMAT_VA8) ? 2 : (ctx->pixelformat_A == GIMP_PIXELFORMAT_RGB8) ? 3 : (ctx->pixelformat_A == GIMP_PIXELFORMAT_RGBA8) ? 4 : 0; gint tmp; length = ctx->n_pixels * bytes1; while (length--) { *dest++ = (guchar) INT_MULT(*src1, ctx->scale.scale, tmp); src1++; } } /** * gimp_composite_generic_init: * * Initialise the generic set of compositing functions. * * Returns: boolean indicating that the initialisation was successful. **/ gboolean gimp_composite_generic_init (void) { GRand *gr; guint i; gint a, b; #define RANDOM_SEED 314159265 /* generate a table of random seeds */ gr = g_rand_new_with_seed (RANDOM_SEED); for (i = 0; i < RANDOM_TABLE_SIZE; i++) random_table[i] = g_rand_int (gr); g_rand_free (gr); /* generate a table for burn compositing */ for (a = 0; a < 256; a++) for (b = 0; b < 256; b++) { /* FIXME: Is the burn effect supposed to be dependant on the sign * of this temporary variable? */ gint tmp; tmp = (255 - a) << 8; tmp /= b + 1; tmp = (255 - tmp); burn_lut[a][b] = CLAMP (tmp, 0, 255); } /* generate a table for saturate add */ for (i = 0; i < 256; i++) add_lut[i] = i; for (i = 256; i <= 510; i++) add_lut[i] = 255; return TRUE; }