gimp/app/composite/gimp-composite-generic.c

1167 lines
29 KiB
C
Raw Normal View History

2003-07-09 07:15:16 +08:00
/* The GIMP -- an image manipulation program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* -*- mode: c tab-width: 2; c-basic-indent: 2; indent-tabs-mode: nil -*-
2003-07-09 07:15:16 +08:00
*
* 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 pixelfiddeling paint-functions.
2003-07-09 07:15:16 +08:00
*/
#include "config.h"
2003-07-09 07:15:16 +08:00
#include <string.h>
#include <stdio.h>
2003-07-09 07:15:16 +08:00
#include <glib-object.h>
#include "libgimpcolor/gimpcolor.h"
2003-07-09 07:15:16 +08:00
2003-07-21 08:14:37 +08:00
#include "base/base-types.h"
2003-07-09 07:15:16 +08:00
#include "gimp-composite.h"
#define OPAQUE_OPACITY 255
#define TRANSPARENT_OPACITY 0
2003-07-09 07:15:16 +08:00
#define INT_MULT(a,b,t) ((t) = (a) * (b) + 0x80, ((((t) >> 8) + (t)) >> 8))
/* This version of INT_MULT3 is very fast, but suffers from some
slight roundoff errors. It returns the correct result 99.987
percent of the time */
#define INT_MULT3(a,b,c,t) ((t) = (a) * (b) * (c)+ 0x7F5B, ((((t) >> 7) + (t)) >> 16))
/*
This version of INT_MULT3 always gives the correct result, but runs at
approximatly one third the speed. */
/* #define INT_MULT3(a,b,c,t) (((a) * (b) * (c)+ 32512) / 65025.0)
*/
#define INT_BLEND(a,b,alpha,tmp) (INT_MULT((a)-(b), alpha, tmp) + (b))
#define RANDOM_TABLE_SIZE 4096
/* 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];
/*
*
* Pixel format type conversion
*
* XXX This implementation will not work for >8 bit colours.
* XXX This implementation is totally wrong.
*/
void
gimp_composite_convert_any_any_any_generic (GimpCompositeContext *ctx)
2003-07-09 07:15:16 +08:00
{
int i;
int j;
char *D = ctx->D;
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;
}
}
void
gimp_composite_color_any_any_any_generic (guchar * dest, const guchar * color, guint w, guint bytes)
2003-07-09 07:15:16 +08:00
{
/* 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;
}
}
}
void
gimp_composite_blend_any_any_any_generic (GimpCompositeContext *ctx)
2003-07-09 07:15:16 +08:00
{
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)
2003-07-09 07:15:16 +08:00
{
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
void
gimp_composite_darken_any_any_any_generic (GimpCompositeContext * ctx)
2003-07-09 07:15:16 +08:00
{
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;
}
}
void
gimp_composite_lighten_any_any_any_generic (GimpCompositeContext * ctx)
2003-07-09 07:15:16 +08:00
{
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;
}
}
void
gimp_composite_hue_any_any_any_generic (GimpCompositeContext * ctx)
2003-07-09 07:15:16 +08:00
{
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);
guint r1, g1, b1;
guint r2, g2, b2;
/* 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);
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;
}
}
void
gimp_composite_saturation_any_any_any_generic (GimpCompositeContext * ctx)
2003-07-09 07:15:16 +08:00
{
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);
guint r1, g1, b1;
guint r2, g2, b2;
/* 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;
}
}
void
gimp_composite_value_any_any_any_generic (GimpCompositeContext * ctx)
2003-07-09 07:15:16 +08:00
{
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);
guint r1, g1, b1;
guint r2, g2, b2;
/* 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;
}
}
void
gimp_composite_color_only_any_any_any_generic (GimpCompositeContext * ctx)
2003-07-09 07:15:16 +08:00
{
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);
guint r1, g1, b1;
guint r2, g2, b2;
/* 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_hls_int(&r1, &g1, &b1);
gimp_rgb_to_hls_int(&r2, &g2, &b2);
/* transfer hue and saturation to the source pixel */
r1 = r2;
b1 = b2;
/* set the destination */
gimp_hls_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;
}
}
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;
}
2003-07-09 07:15:16 +08:00
void
gimp_composite_multiply_any_any_any_generic (GimpCompositeContext * ctx)
2003-07-09 07:15:16 +08:00
{
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;
}
}
}
void
gimp_composite_divide_any_any_any_generic (GimpCompositeContext * ctx)
2003-07-09 07:15:16 +08:00
{
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;
}
}
void
gimp_composite_screen_any_any_any_generic (GimpCompositeContext * ctx)
2003-07-09 07:15:16 +08:00
{
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;
}
}
void
gimp_composite_overlay_any_any_any_generic (GimpCompositeContext * ctx)
2003-07-09 07:15:16 +08:00
{
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] = INT_MULT(src1[b], src1[b] + INT_MULT(2 * src2[b], 255 - src1[b], tmp), 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;
}
}
void
gimp_composite_dodge_any_any_any_generic (GimpCompositeContext * ctx)
2003-07-09 07:15:16 +08:00
{
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) CLAMP(tmp, 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;
}
}
void
gimp_composite_burn_any_any_any_generic (GimpCompositeContext * ctx)
2003-07-09 07:15:16 +08:00
{
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;
/* FIXME: Is the burn effect supposed to be dependant on the sign of this
* temporary variable? */
gint tmp;
while (length--)
{
for (b = 0; b < alpha; b++)
{
tmp = (255 - src1[b]) << 8;
tmp /= src2[b] + 1;
dest[b] = (guchar) CLAMP(255 - tmp, 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;
}
}
void
gimp_composite_hardlight_any_any_any_generic (GimpCompositeContext * ctx)
2003-07-09 07:15:16 +08:00
{
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) CLAMP(255 - (tmp >> 8), 0, 255);
}
else
{
tmp = (gint) src1[b] * ((gint) src2[b] << 1);
dest[b] = (guchar) CLAMP(tmp >> 8, 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;
}
}
void
gimp_composite_softlight_any_any_any_generic (GimpCompositeContext * ctx)
2003-07-09 07:15:16 +08:00
{
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;
}
}
void
gimp_composite_grain_extract_any_any_any_generic (GimpCompositeContext * ctx)
2003-07-09 07:15:16 +08:00
{
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;
}
}
void
gimp_composite_grain_merge_any_any_any_generic (GimpCompositeContext * ctx)
2003-07-09 07:15:16 +08:00
{
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;
}
}
void
gimp_composite_addition_any_any_any_generic (GimpCompositeContext * ctx)
2003-07-09 07:15:16 +08:00
{
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;
}
}
}
void
gimp_composite_subtract_any_any_any_generic (GimpCompositeContext * ctx)
2003-07-09 07:15:16 +08:00
{
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;
}
}
void
gimp_composite_difference_any_any_any_generic (GimpCompositeContext * ctx)
2003-07-09 07:15:16 +08:00
{
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;
}
}
void
gimp_composite_dissolve_any_any_any_generic (GimpCompositeContext * ctx)
2003-07-09 07:15:16 +08:00
{
GRand *gr;
gint alpha;
gint b;
gint combined_opacity;
gint db;
2003-07-09 07:15:16 +08:00
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];
2003-07-09 07:15:16 +08:00
/*
* if destination does not have an alpha channel, add one to it.
*/
2003-07-09 07:15:16 +08:00
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];
2003-07-09 07:15:16 +08:00
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);
2003-07-09 07:15:16 +08:00
if (mask) {
if (has_alpha)
combined_opacity = opacity * src[alpha] * (*mask) / (255 * 255);
else
combined_opacity = opacity * (*mask) / 255;
2003-07-09 07:15:16 +08:00
mask++;
} else {
if (has_alpha)
combined_opacity = opacity * src[alpha] / 255;
else
combined_opacity = opacity;
}
dest[alpha] = (rand_val >= combined_opacity) ? 0 : OPAQUE_OPACITY;
2003-07-09 07:15:16 +08:00
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;
}
void
gimp_composite_replace_any_any_any_generic (GimpCompositeContext *ctx)
2003-07-09 07:15:16 +08:00
{
ctx->D = ctx->B;
ctx->combine = REPLACE_INTEN;
}
void
gimp_composite_swap_any_any_any_generic (GimpCompositeContext * ctx)
2003-07-09 07:15:16 +08:00
{
guint length;
guchar *src = ctx->A;
guchar *dest = ctx->B;
guint bytes1 = gimp_composite_pixel_bpp[ctx->pixelformat_A];
length = ctx->n_pixels * bytes1;
while (length--)
{
*src = *src ^ *dest;
*dest = *dest ^ *src;
*src = *src ^ *dest;
src++;
dest++;
}
}
void
gimp_composite_normal_any_any_any_generic (GimpCompositeContext * ctx)
2003-07-09 07:15:16 +08:00
{
ctx->D = ctx->B;
}
void
gimp_composite_erase_any_any_any_generic (GimpCompositeContext *ctx)
2003-07-09 07:15:16 +08:00
{
ctx->D = ctx->B;
ctx->combine = (gimp_composite_pixel_alphap[ctx->pixelformat_A] && gimp_composite_pixel_alphap[ctx->pixelformat_B]) ? ERASE_INTEN : 0;
}
void
gimp_composite_anti_erase_any_any_any_generic (GimpCompositeContext *ctx)
2003-07-09 07:15:16 +08:00
{
ctx->D = ctx->B;
ctx->combine = (gimp_composite_pixel_alphap[ctx->pixelformat_A] && gimp_composite_pixel_alphap[ctx->pixelformat_B]) ? ANTI_ERASE_INTEN : 0;
}
void
gimp_composite_color_erase_any_any_any_generic (GimpCompositeContext *ctx)
2003-07-09 07:15:16 +08:00
{
ctx->D = ctx->B;
ctx->combine = (gimp_composite_pixel_alphap[ctx->pixelformat_A] && gimp_composite_pixel_alphap[ctx->pixelformat_B])
? COLOR_ERASE_INTEN
: 0;
2003-07-09 07:15:16 +08:00
}
void
gimp_composite_scale_any_any_any_generic (GimpCompositeContext * ctx)
2003-07-09 07:15:16 +08:00
{
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++;
}
}
void
gimp_composite_generic_init (void)
2003-07-09 07:15:16 +08:00
{
GRand *gr;
guint i;
2003-07-09 07:15:16 +08:00
#define RANDOM_SEED 314159265
/* generate a table of random seeds */
gr = g_rand_new_with_seed (RANDOM_SEED);
2003-07-09 07:15:16 +08:00
for (i = 0; i < RANDOM_TABLE_SIZE; i++)
random_table[i] = g_rand_int (gr);
g_rand_free (gr);
2003-07-09 07:15:16 +08:00
for (i = 0; i < 256; i++)
add_lut[i] = i;
for (i = 256; i <= 510; i++)
add_lut[i] = 255;
}