mirror of https://github.com/GNOME/gimp.git
1996 lines
48 KiB
C
1996 lines
48 KiB
C
/* 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.
|
|
*/
|
|
|
|
#ifndef __PAINT_FUNCS_GENERIC_H__
|
|
#define __PAINT_FUNCS_GENERIC_H__
|
|
|
|
|
|
#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)
|
|
|
|
/* FIXME: Move to a more global place */
|
|
struct apply_layer_mode_struct
|
|
{
|
|
guint bytes1 : 3;
|
|
guint bytes2 : 3;
|
|
guchar *src1;
|
|
guchar *src2;
|
|
guchar *mask;
|
|
guchar **dest;
|
|
gint x;
|
|
gint y;
|
|
guint opacity;
|
|
guint length;
|
|
CombinationMode combine;
|
|
};
|
|
|
|
static guchar add_lut[511];
|
|
static gint32 random_table[RANDOM_TABLE_SIZE];
|
|
|
|
void
|
|
color_pixels (guchar *dest,
|
|
const guchar *color,
|
|
guint w,
|
|
guint bytes)
|
|
{
|
|
switch (bytes)
|
|
{
|
|
case 1:
|
|
memset (dest, *color, w);
|
|
break;
|
|
|
|
case 2:
|
|
#if defined(sparc) || defined(__sparc__)
|
|
{
|
|
const guchar c0 = color[0];
|
|
const guchar c1 = color[1];
|
|
|
|
while (w--)
|
|
{
|
|
dest[0] = c0;
|
|
dest[1] = c1;
|
|
dest += 2;
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
const guint16 shortc = ((const guint16 *) color)[0];
|
|
guint16 *shortd = (guint16 *) dest;
|
|
|
|
while (w--)
|
|
{
|
|
*shortd = shortc;
|
|
shortd++;
|
|
}
|
|
}
|
|
#endif /* sparc || __sparc__ */
|
|
break;
|
|
|
|
case 3:
|
|
{
|
|
const guchar c0 = color[0];
|
|
const guchar c1 = color[1];
|
|
const guchar c2 = color[2];
|
|
|
|
while (w--)
|
|
{
|
|
dest[0] = c0;
|
|
dest[1] = c1;
|
|
dest[2] = c2;
|
|
dest += 3;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
#if defined(sparc) || defined(__sparc__)
|
|
{
|
|
const guchar c0 = color[0];
|
|
const guchar c1 = color[1];
|
|
const guchar c2 = color[2];
|
|
const guchar c3 = color[3];
|
|
|
|
while (w--)
|
|
{
|
|
dest[0] = c0;
|
|
dest[1] = c1;
|
|
dest[2] = c2;
|
|
dest[3] = c3;
|
|
dest += 4;
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
const guint32 longc = ((const guint32 *) color)[0];
|
|
guint32 *longd = (guint32 *) dest;
|
|
|
|
while (w--)
|
|
{
|
|
*longd = longc;
|
|
longd++;
|
|
}
|
|
}
|
|
#endif /* sparc || __sparc__ */
|
|
break;
|
|
|
|
default:
|
|
while (w--)
|
|
{
|
|
memcpy (dest, color, bytes);
|
|
dest += bytes;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
color_pixels_mask (guchar *dest,
|
|
guchar *mask,
|
|
const guchar *color,
|
|
guint w,
|
|
guint bytes)
|
|
{
|
|
guchar c0, c1, c2;
|
|
gint alpha;
|
|
|
|
alpha = HAS_ALPHA (bytes) ? bytes - 1 : bytes;
|
|
|
|
switch (bytes)
|
|
{
|
|
case 1:
|
|
memset (dest, *color, w);
|
|
break;
|
|
|
|
case 2:
|
|
c0 = color[0];
|
|
while (w--)
|
|
{
|
|
dest[0] = c0;
|
|
dest[1] = *mask++;
|
|
dest += 2;
|
|
}
|
|
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:
|
|
c0 = color[0];
|
|
c1 = color[1];
|
|
c2 = color[2];
|
|
while (w--)
|
|
{
|
|
dest[0] = c0;
|
|
dest[1] = c1;
|
|
dest[2] = c2;
|
|
dest[3] = *mask++;
|
|
dest += 4;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
pattern_pixels_mask (guchar *dest,
|
|
guchar *mask,
|
|
TempBuf *pattern,
|
|
guint w,
|
|
guint bytes,
|
|
gint x,
|
|
gint y)
|
|
{
|
|
guchar *pat, *p;
|
|
gint alpha, b;
|
|
gint i;
|
|
|
|
/* Get a pointer to the appropriate scanline of the pattern buffer */
|
|
pat = (temp_buf_data (pattern) +
|
|
(y % pattern->height) * pattern->width * pattern->bytes);
|
|
|
|
alpha = HAS_ALPHA (bytes) ? bytes - 1 : bytes;
|
|
|
|
/*
|
|
* image data = pattern data for all but alpha
|
|
*
|
|
* If (image has alpha)
|
|
* if (there's a mask)
|
|
* image data = mask for alpha;
|
|
* else
|
|
* image data = opaque for alpha.
|
|
*
|
|
* if (pattern has alpha)
|
|
* multiply existing alpha channel by pattern alpha
|
|
* (normalised to (0..1))
|
|
*/
|
|
|
|
for (i = 0; i < w; i++)
|
|
{
|
|
p = pat + ((i + x) % pattern->width) * pattern->bytes;
|
|
|
|
for (b = 0; b < alpha; b++)
|
|
dest[b] = p[b];
|
|
|
|
if (HAS_ALPHA (bytes))
|
|
{
|
|
if (mask)
|
|
dest[alpha] = *mask++;
|
|
else
|
|
dest[alpha] = OPAQUE_OPACITY;
|
|
|
|
if (HAS_ALPHA (pattern->bytes))
|
|
dest[alpha] = (guchar) (dest[alpha] *
|
|
p[alpha] / (gdouble) OPAQUE_OPACITY);
|
|
}
|
|
|
|
dest += bytes;
|
|
}
|
|
}
|
|
|
|
inline void
|
|
blend_pixels (const guchar *src1,
|
|
const guchar *src2,
|
|
guchar *dest,
|
|
guchar blend,
|
|
guint w,
|
|
guint bytes)
|
|
{
|
|
guint b;
|
|
|
|
if (HAS_ALPHA (bytes))
|
|
{
|
|
const guint blend1 = 256 - blend;
|
|
const guint blend2 = blend + 1;
|
|
const guint c = bytes - 1;
|
|
|
|
while (w--)
|
|
{
|
|
guint a1 = blend1 * src1[c];
|
|
guint a2 = blend2 * src2[c];
|
|
guint a = a1 + a2;
|
|
|
|
if (!a)
|
|
{
|
|
for (b = 0; b < bytes; b++)
|
|
dest[b] = 0;
|
|
}
|
|
else
|
|
{
|
|
for (b = 0; b < c; b++)
|
|
dest[b] = (src1[b] * a1 + src2[b] * a2) / a;
|
|
|
|
dest[c] = a >> 8;
|
|
}
|
|
|
|
src1 += bytes;
|
|
src2 += bytes;
|
|
dest += bytes;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const guchar blend1 = 255 - blend;
|
|
|
|
while (w--)
|
|
{
|
|
for (b = 0; b < bytes; b++)
|
|
dest[b] = (src1[b] * blend1 + src2[b] * blend) / 255;
|
|
|
|
src1 += bytes;
|
|
src2 += bytes;
|
|
dest += bytes;
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void
|
|
shade_pixels (const guchar *src,
|
|
guchar *dest,
|
|
const guchar *col,
|
|
guchar blend,
|
|
guint w,
|
|
guint bytes,
|
|
gboolean 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;
|
|
}
|
|
}
|
|
|
|
|
|
inline void
|
|
extract_alpha_pixels (const guchar *src,
|
|
const guchar *mask,
|
|
guchar *dest,
|
|
guint w,
|
|
guint bytes)
|
|
{
|
|
const guint alpha = bytes - 1;
|
|
gint tmp;
|
|
|
|
if (mask)
|
|
{
|
|
const guchar *m = mask;
|
|
while (w--)
|
|
{
|
|
*dest++ = INT_MULT(src[alpha], *m, tmp);
|
|
m++;
|
|
src += bytes;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (w--)
|
|
{
|
|
*dest++ = INT_MULT(src[alpha], OPAQUE_OPACITY, tmp);
|
|
src += bytes;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static inline void
|
|
darken_pixels (const guchar *src1,
|
|
const guchar *src2,
|
|
guchar *dest,
|
|
guint length,
|
|
guint bytes1,
|
|
guint bytes2)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
static inline void
|
|
lighten_pixels (const guchar *src1,
|
|
const guchar *src2,
|
|
guchar *dest,
|
|
guint length,
|
|
guint bytes1,
|
|
guint bytes2)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
static inline void
|
|
hue_only_pixels (const guchar *src1,
|
|
const guchar *src2,
|
|
guchar *dest,
|
|
guint length,
|
|
guint bytes1,
|
|
guint bytes2)
|
|
{
|
|
const guint has_alpha1 = HAS_ALPHA (bytes1);
|
|
const guint has_alpha2 = HAS_ALPHA (bytes2);
|
|
gint r1, g1, b1;
|
|
gint 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;
|
|
}
|
|
}
|
|
|
|
|
|
static inline void
|
|
saturation_only_pixels (const guchar *src1,
|
|
const guchar *src2,
|
|
guchar *dest,
|
|
guint length,
|
|
guint bytes1,
|
|
guint bytes2)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
static inline void
|
|
value_only_pixels (const guchar *src1,
|
|
const guchar *src2,
|
|
guchar *dest,
|
|
guint length,
|
|
guint bytes1,
|
|
guint bytes2)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
static inline void
|
|
color_only_pixels (const guchar *src1,
|
|
const guchar *src2,
|
|
guchar *dest,
|
|
guint length,
|
|
guint bytes1,
|
|
guint bytes2)
|
|
{
|
|
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_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;
|
|
}
|
|
}
|
|
|
|
|
|
static inline void
|
|
multiply_pixels (const guchar *src1,
|
|
const guchar *src2,
|
|
guchar *dest,
|
|
guint length,
|
|
guint bytes1,
|
|
guint bytes2)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static inline void
|
|
divide_pixels (const guchar *src1,
|
|
const guchar *src2,
|
|
guchar *dest,
|
|
guint length,
|
|
guint bytes1,
|
|
guint bytes2)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
static inline void
|
|
screen_pixels (const guchar *src1,
|
|
const guchar *src2,
|
|
guchar *dest,
|
|
guint length,
|
|
guint bytes1,
|
|
guint bytes2)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
static inline void
|
|
overlay_pixels (const guchar *src1,
|
|
const guchar *src2,
|
|
guchar *dest,
|
|
guint length,
|
|
guint bytes1,
|
|
guint bytes2)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
static inline void
|
|
dodge_pixels (const guchar *src1,
|
|
const guchar *src2,
|
|
guchar *dest,
|
|
guint length,
|
|
guint bytes1,
|
|
guint bytes2)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
static inline void
|
|
burn_pixels (const guchar *src1,
|
|
const guchar *src2,
|
|
guchar *dest,
|
|
guint length,
|
|
guint bytes1,
|
|
guint bytes2)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
static inline void
|
|
hardlight_pixels (const guchar *src1,
|
|
const guchar *src2,
|
|
guchar *dest,
|
|
guint length,
|
|
guint bytes1,
|
|
guint bytes2)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
static inline void
|
|
softlight_pixels (const guchar *src1,
|
|
const guchar *src2,
|
|
guchar *dest,
|
|
guint length,
|
|
guint bytes1,
|
|
guint bytes2)
|
|
{
|
|
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, 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;
|
|
}
|
|
}
|
|
|
|
|
|
static inline void
|
|
grain_extract_pixels (const guchar *src1,
|
|
const guchar *src2,
|
|
guchar *dest,
|
|
guint length,
|
|
guint bytes1,
|
|
guint bytes2)
|
|
{
|
|
guint alpha, b;
|
|
gint diff;
|
|
const guint has_alpha1 = HAS_ALPHA (bytes1);
|
|
const guint has_alpha2 = HAS_ALPHA (bytes2);
|
|
|
|
alpha = (has_alpha1 || has_alpha2) ? MAX (bytes1, bytes2) - 1 : bytes1;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
static inline void
|
|
grain_merge_pixels (const guchar *src1,
|
|
const guchar *src2,
|
|
guchar *dest,
|
|
guint length,
|
|
guint bytes1,
|
|
guint bytes2)
|
|
{
|
|
gint alpha, b;
|
|
gint sum;
|
|
const guint has_alpha1 = HAS_ALPHA (bytes1);
|
|
const guint has_alpha2 = HAS_ALPHA (bytes2);
|
|
|
|
alpha = (has_alpha1 || has_alpha2) ? MAX (bytes1, bytes2) - 1 : bytes1;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
static inline void
|
|
add_pixels (const guchar *src1,
|
|
const guchar *src2,
|
|
guchar *dest,
|
|
guint length,
|
|
guint bytes1,
|
|
guint bytes2)
|
|
{
|
|
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] = add_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;
|
|
}
|
|
}
|
|
|
|
|
|
static inline void
|
|
subtract_pixels (const guchar *src1,
|
|
const guchar *src2,
|
|
guchar *dest,
|
|
guint length,
|
|
guint bytes1,
|
|
guint bytes2)
|
|
{
|
|
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 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;
|
|
}
|
|
}
|
|
|
|
|
|
static inline void
|
|
difference_pixels (const guchar *src1,
|
|
const guchar *src2,
|
|
guchar *dest,
|
|
guint length,
|
|
guint bytes1,
|
|
guint bytes2)
|
|
{
|
|
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 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;
|
|
}
|
|
}
|
|
|
|
|
|
static inline void
|
|
dissolve_pixels (const guchar *src,
|
|
guchar *mask,
|
|
guchar *dest,
|
|
gint x,
|
|
gint y,
|
|
gint opacity,
|
|
gint length,
|
|
gint sb,
|
|
gint db,
|
|
guint has_alpha)
|
|
{
|
|
gint alpha, b;
|
|
gint32 rand_val;
|
|
gint combined_opacity;
|
|
GRand *gr;
|
|
|
|
gr = g_rand_new_with_seed (random_table[y % RANDOM_TABLE_SIZE]);
|
|
|
|
/* Ignore x random values so we get a deterministic result */
|
|
for (b = 0; b < x; b ++)
|
|
g_rand_int (gr);
|
|
|
|
alpha = db - 1;
|
|
|
|
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) ? 0 : OPAQUE_OPACITY;
|
|
|
|
src += sb;
|
|
dest += db;
|
|
}
|
|
|
|
g_rand_free (gr);
|
|
|
|
}
|
|
|
|
static inline void
|
|
replace_pixels (guchar *src1,
|
|
guchar *src2,
|
|
guchar *dest,
|
|
guchar *mask,
|
|
gint length,
|
|
gint opacity,
|
|
const gboolean *affect,
|
|
gint bytes1,
|
|
gint bytes2)
|
|
{
|
|
gint alpha;
|
|
gint b;
|
|
gdouble a_val, a_recip, mask_val;
|
|
gdouble norm_opacity;
|
|
gint s1_a, s2_a;
|
|
gint new_val;
|
|
|
|
if (bytes1 != bytes2)
|
|
{
|
|
g_warning ("replace_pixels only works on commensurate pixel regions");
|
|
return;
|
|
}
|
|
|
|
alpha = bytes1 - 1;
|
|
norm_opacity = opacity * (1.0 / 65536.0);
|
|
|
|
while (length --)
|
|
{
|
|
mask_val = mask[0] * norm_opacity;
|
|
/* calculate new alpha first. */
|
|
s1_a = src1[alpha];
|
|
s2_a = src2[alpha];
|
|
a_val = s1_a + mask_val * (s2_a - s1_a);
|
|
|
|
if (a_val == 0) /* In any case, write out versions of the blending function */
|
|
/* that result when combinations of s1_a, s2_a, and */
|
|
/* mask_val --> 0 (or mask_val -->1) */
|
|
{
|
|
/* Case 1: s1_a, s2_a, AND mask_val all approach 0+: */
|
|
/* Case 2: s1_a AND s2_a both approach 0+, regardless of mask_val: */
|
|
|
|
if (s1_a + s2_a == 0.0)
|
|
{
|
|
for (b = 0; b < alpha; b++)
|
|
{
|
|
new_val = 0.5 + (gdouble) src1[b] +
|
|
mask_val * ((gdouble) src2[b] - (gdouble) src1[b]);
|
|
|
|
dest[b] = affect[b] ? MIN (new_val, 255) : src1[b];
|
|
}
|
|
}
|
|
|
|
/* Case 3: mask_val AND s1_a both approach 0+, regardless of s2_a */
|
|
else if (s1_a + mask_val == 0.0)
|
|
{
|
|
for (b = 0; b < alpha; b++)
|
|
{
|
|
dest[b] = src1[b];
|
|
}
|
|
}
|
|
|
|
/* Case 4: mask_val -->1 AND s2_a -->0, regardless of s1_a */
|
|
else if (1.0 - mask_val + s2_a == 0.0)
|
|
{
|
|
for (b = 0; b < alpha; b++)
|
|
{
|
|
dest[b] = affect[b] ? src2[b] : src1[b];
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
a_recip = 1.0 / a_val;
|
|
/* possible optimization: fold a_recip into s1_a and s2_a */
|
|
for (b = 0; b < alpha; b++)
|
|
{
|
|
new_val = 0.5 + a_recip * (src1[b] * s1_a + mask_val *
|
|
(src2[b] * s2_a - src1[b] * s1_a));
|
|
dest[b] = affect[b] ? MIN (new_val, 255) : src1[b];
|
|
}
|
|
}
|
|
|
|
dest[alpha] = affect[alpha] ? a_val + 0.5: s1_a;
|
|
src1 += bytes1;
|
|
src2 += bytes2;
|
|
dest += bytes2;
|
|
mask++;
|
|
}
|
|
}
|
|
|
|
inline void
|
|
swap_pixels (guchar *src,
|
|
guchar *dest,
|
|
guint length)
|
|
{
|
|
while (length--)
|
|
{
|
|
*src = *src ^ *dest;
|
|
*dest = *dest ^ *src;
|
|
*src = *src ^ *dest;
|
|
src++;
|
|
dest++;
|
|
}
|
|
}
|
|
|
|
inline void
|
|
scale_pixels (const guchar *src,
|
|
guchar *dest,
|
|
guint length,
|
|
gint scale)
|
|
{
|
|
gint tmp;
|
|
|
|
while (length --)
|
|
{
|
|
*dest++ = (guchar) INT_MULT (*src, scale, tmp);
|
|
src++;
|
|
}
|
|
}
|
|
|
|
inline void
|
|
add_alpha_pixels (const guchar *src,
|
|
guchar *dest,
|
|
guint length,
|
|
guint bytes)
|
|
{
|
|
gint alpha, b;
|
|
|
|
alpha = bytes + 1;
|
|
|
|
while (length --)
|
|
{
|
|
for (b = 0; b < bytes; b++)
|
|
dest[b] = src[b];
|
|
|
|
dest[b] = OPAQUE_OPACITY;
|
|
|
|
src += bytes;
|
|
dest += alpha;
|
|
}
|
|
}
|
|
|
|
|
|
inline void
|
|
flatten_pixels (const guchar *src,
|
|
guchar *dest,
|
|
const guchar *bg,
|
|
guint length,
|
|
guint bytes)
|
|
{
|
|
gint alpha, b;
|
|
gint t1, t2;
|
|
|
|
alpha = bytes - 1;
|
|
while (length --)
|
|
{
|
|
for (b = 0; b < alpha; b++)
|
|
dest[b] = INT_MULT (src[b], src[alpha], t1) +
|
|
INT_MULT (bg[b], (255 - src[alpha]), t2);
|
|
|
|
src += bytes;
|
|
dest += alpha;
|
|
}
|
|
}
|
|
|
|
|
|
inline void
|
|
gray_to_rgb_pixels (const guchar *src,
|
|
guchar *dest,
|
|
guint length,
|
|
guint bytes)
|
|
{
|
|
gint b;
|
|
gint dest_bytes;
|
|
gboolean has_alpha;
|
|
|
|
has_alpha = (bytes == 2) ? TRUE : FALSE;
|
|
dest_bytes = (has_alpha) ? 4 : 3;
|
|
|
|
while (length --)
|
|
{
|
|
for (b = 0; b < bytes; b++)
|
|
dest[b] = src[0];
|
|
|
|
if (has_alpha)
|
|
dest[3] = src[1];
|
|
|
|
src += bytes;
|
|
dest += dest_bytes;
|
|
}
|
|
}
|
|
|
|
|
|
inline void
|
|
apply_mask_to_alpha_channel (guchar *src,
|
|
const guchar *mask,
|
|
guint opacity,
|
|
guint length,
|
|
guint bytes)
|
|
{
|
|
glong tmp;
|
|
|
|
src += bytes - 1;
|
|
|
|
if (opacity == 255)
|
|
{
|
|
while (length --)
|
|
{
|
|
*src = INT_MULT(*src, *mask, tmp);
|
|
mask++;
|
|
src += bytes;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (length --)
|
|
{
|
|
*src = INT_MULT3(*src, *mask, opacity, tmp);
|
|
mask++;
|
|
src += bytes;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
inline void
|
|
combine_mask_and_alpha_channel_stipple (guchar *src,
|
|
const guchar *mask,
|
|
guint opacity,
|
|
guint length,
|
|
guint bytes)
|
|
{
|
|
gint mask_val;
|
|
gint tmp;
|
|
|
|
/* align with alpha channel */
|
|
src += bytes - 1;
|
|
|
|
if (opacity != 255)
|
|
while (length --)
|
|
{
|
|
mask_val = INT_MULT(*mask, opacity, tmp);
|
|
*src = *src + INT_MULT((255 - *src) , mask_val, tmp);
|
|
|
|
src += bytes;
|
|
mask++;
|
|
}
|
|
else
|
|
while (length --)
|
|
{
|
|
*src = *src + INT_MULT((255 - *src) , *mask, tmp);
|
|
|
|
src += bytes;
|
|
mask++;
|
|
}
|
|
}
|
|
|
|
|
|
inline void
|
|
combine_mask_and_alpha_channel_stroke (guchar *src,
|
|
const guchar *mask,
|
|
guint opacity,
|
|
guint length,
|
|
guint bytes)
|
|
{
|
|
gint mask_val;
|
|
gint tmp;
|
|
|
|
/* align with alpha channel */
|
|
src += bytes - 1;
|
|
|
|
if (opacity != 255)
|
|
while (length --)
|
|
{
|
|
if (opacity > *src)
|
|
{
|
|
mask_val = INT_MULT(*mask, opacity, tmp);
|
|
*src = *src + INT_MULT((opacity - *src) , mask_val, tmp);
|
|
}
|
|
|
|
src += bytes;
|
|
mask++;
|
|
}
|
|
else
|
|
while (length --)
|
|
{
|
|
*src = *src + INT_MULT((255 - *src) , *mask, tmp);
|
|
|
|
src += bytes;
|
|
mask++;
|
|
}
|
|
}
|
|
|
|
|
|
inline void
|
|
copy_gray_to_inten_a_pixels (const guchar *src,
|
|
guchar *dest,
|
|
guint length,
|
|
guint bytes)
|
|
{
|
|
gint b;
|
|
gint alpha;
|
|
|
|
alpha = bytes - 1;
|
|
|
|
while (length --)
|
|
{
|
|
for (b = 0; b < alpha; b++)
|
|
dest[b] = *src;
|
|
dest[b] = OPAQUE_OPACITY;
|
|
|
|
src ++;
|
|
dest += bytes;
|
|
}
|
|
}
|
|
|
|
|
|
inline void
|
|
initial_channel_pixels (const guchar *src,
|
|
guchar *dest,
|
|
guint length,
|
|
guint bytes)
|
|
{
|
|
gint alpha, b;
|
|
|
|
alpha = bytes - 1;
|
|
|
|
while (length --)
|
|
{
|
|
for (b = 0; b < alpha; b++)
|
|
dest[b] = src[0];
|
|
|
|
dest[alpha] = OPAQUE_OPACITY;
|
|
|
|
dest += bytes;
|
|
src ++;
|
|
}
|
|
}
|
|
|
|
|
|
inline void
|
|
initial_indexed_pixels (const guchar *src,
|
|
guchar *dest,
|
|
const guchar *cmap,
|
|
guint length)
|
|
{
|
|
gint col_index;
|
|
|
|
/* This function assumes always that we're mapping from
|
|
* an RGB colormap to an RGBA image...
|
|
*/
|
|
while (length--)
|
|
{
|
|
col_index = *src++ * 3;
|
|
*dest++ = cmap[col_index++];
|
|
*dest++ = cmap[col_index++];
|
|
*dest++ = cmap[col_index++];
|
|
*dest++ = OPAQUE_OPACITY;
|
|
}
|
|
}
|
|
|
|
|
|
inline void
|
|
initial_indexed_a_pixels (const guchar *src,
|
|
guchar *dest,
|
|
const guchar *mask,
|
|
const guchar *no_mask,
|
|
const guchar *cmap,
|
|
guint opacity,
|
|
guint length)
|
|
{
|
|
gint col_index;
|
|
guchar new_alpha;
|
|
const guchar *m;
|
|
glong tmp;
|
|
|
|
if (mask)
|
|
m = mask;
|
|
else
|
|
m = no_mask;
|
|
|
|
while (length --)
|
|
{
|
|
col_index = *src++ * 3;
|
|
new_alpha = INT_MULT3(*src, *m, opacity, tmp);
|
|
src++;
|
|
*dest++ = cmap[col_index++];
|
|
*dest++ = cmap[col_index++];
|
|
*dest++ = cmap[col_index++];
|
|
/* Set the alpha channel */
|
|
*dest++ = (new_alpha > 127) ? OPAQUE_OPACITY : TRANSPARENT_OPACITY;
|
|
|
|
if (mask)
|
|
m++;
|
|
}
|
|
}
|
|
|
|
|
|
inline void
|
|
initial_inten_pixels (const guchar *src,
|
|
guchar *dest,
|
|
const guchar *mask,
|
|
const guchar *no_mask,
|
|
guint opacity,
|
|
const gboolean *affect,
|
|
guint length,
|
|
guint bytes)
|
|
{
|
|
gint b;
|
|
gint tmp;
|
|
gint l;
|
|
const guchar *m;
|
|
guchar *destp;
|
|
const guchar *srcp;
|
|
const gint dest_bytes = bytes + 1;
|
|
|
|
if (!affect[bytes])
|
|
opacity = 255;
|
|
|
|
if (mask)
|
|
{
|
|
m = mask;
|
|
|
|
/* This function assumes the source has no alpha channel and
|
|
* the destination has an alpha channel. So dest_bytes = bytes + 1
|
|
*/
|
|
|
|
if (bytes == 3 && affect[0] && affect[1] && affect[2])
|
|
{
|
|
|
|
destp = dest + bytes;
|
|
|
|
if (opacity != 0)
|
|
while(length--)
|
|
{
|
|
dest[0] = src[0];
|
|
dest[1] = src[1];
|
|
dest[2] = src[2];
|
|
dest[3] = INT_MULT(opacity, *m, tmp);
|
|
src += bytes;
|
|
dest += dest_bytes;
|
|
m++;
|
|
}
|
|
else
|
|
while(length--)
|
|
{
|
|
dest[0] = src[0];
|
|
dest[1] = src[1];
|
|
dest[2] = src[2];
|
|
dest[3] = opacity;
|
|
src += bytes;
|
|
dest += dest_bytes;
|
|
}
|
|
return;
|
|
}
|
|
|
|
for (b =0; b < bytes; b++)
|
|
{
|
|
destp = dest + b;
|
|
srcp = src + b;
|
|
l = length;
|
|
|
|
if (affect[b])
|
|
while(l--)
|
|
{
|
|
*destp = *srcp;
|
|
srcp += bytes;
|
|
destp += dest_bytes;
|
|
}
|
|
else
|
|
while(l--)
|
|
{
|
|
*destp = 0;
|
|
destp += dest_bytes;
|
|
}
|
|
}
|
|
|
|
/* fill the alpha channel */
|
|
|
|
destp = dest + bytes;
|
|
|
|
if (opacity != 0)
|
|
while (length--)
|
|
{
|
|
*destp = INT_MULT(opacity , *m, tmp);
|
|
destp += dest_bytes;
|
|
m++;
|
|
}
|
|
else
|
|
while (length--)
|
|
{
|
|
*destp = opacity;
|
|
destp += dest_bytes;
|
|
}
|
|
}
|
|
else /* If no mask */
|
|
{
|
|
/* This function assumes the source has no alpha channel and
|
|
* the destination has an alpha channel. So dest_bytes = bytes + 1
|
|
*/
|
|
|
|
if (bytes == 3 && affect[0] && affect[1] && affect[2])
|
|
{
|
|
destp = dest + bytes;
|
|
|
|
while(length--)
|
|
{
|
|
dest[0] = src[0];
|
|
dest[1] = src[1];
|
|
dest[2] = src[2];
|
|
dest[3] = opacity;
|
|
src += bytes;
|
|
dest += dest_bytes;
|
|
}
|
|
return;
|
|
}
|
|
|
|
for (b = 0; b < bytes; b++)
|
|
{
|
|
destp = dest + b;
|
|
srcp = src + b;
|
|
l = length;
|
|
|
|
if (affect[b])
|
|
while(l--)
|
|
{
|
|
*destp = *srcp;
|
|
srcp += bytes;
|
|
destp += dest_bytes;
|
|
}
|
|
else
|
|
while(l--)
|
|
{
|
|
*destp = 0;
|
|
destp += dest_bytes;
|
|
}
|
|
}
|
|
|
|
/* fill the alpha channel */
|
|
|
|
destp = dest + bytes;
|
|
|
|
while (length--)
|
|
{
|
|
*destp = opacity;
|
|
destp += dest_bytes;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
inline void
|
|
initial_inten_a_pixels (const guchar *src,
|
|
guchar *dest,
|
|
const guchar *mask,
|
|
guint opacity,
|
|
const gboolean *affect,
|
|
guint length,
|
|
guint bytes)
|
|
{
|
|
gint alpha, b;
|
|
const guchar *m;
|
|
glong tmp;
|
|
|
|
alpha = bytes - 1;
|
|
if (mask)
|
|
{
|
|
m = mask;
|
|
while (length --)
|
|
{
|
|
for (b = 0; b < alpha; b++)
|
|
dest[b] = src[b] * affect[b];
|
|
|
|
/* Set the alpha channel */
|
|
dest[alpha] = affect [alpha] ? INT_MULT3(opacity, src[alpha], *m, tmp)
|
|
: 255;
|
|
|
|
m++;
|
|
|
|
dest += bytes;
|
|
src += bytes;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (length --)
|
|
{
|
|
for (b = 0; b < alpha; b++)
|
|
dest[b] = src[b] * affect[b];
|
|
|
|
/* Set the alpha channel */
|
|
dest[alpha] = affect [alpha] ? INT_MULT(opacity , src[alpha], tmp) : 255;
|
|
|
|
dest += bytes;
|
|
src += bytes;
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void
|
|
component_pixels (const guchar *src,
|
|
guchar *dest,
|
|
guint length,
|
|
guint bytes,
|
|
guint pixel)
|
|
{
|
|
src += pixel;
|
|
|
|
while (length --)
|
|
{
|
|
*dest = *src;
|
|
|
|
src += bytes;
|
|
dest++;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
layer_normal_mode (struct apply_layer_mode_struct *alms)
|
|
{
|
|
/* assumes we're applying src2 TO src1 */
|
|
*(alms->dest) = alms->src2;
|
|
}
|
|
|
|
static void
|
|
layer_dissolve_mode (struct apply_layer_mode_struct *alms)
|
|
{
|
|
const guint has_alpha1 = HAS_ALPHA (alms->bytes1);
|
|
const guint has_alpha2 = HAS_ALPHA (alms->bytes2);
|
|
guint dest_bytes;
|
|
|
|
/* Since dissolve requires an alpha channel... */
|
|
if (has_alpha2)
|
|
dest_bytes = alms->bytes2;
|
|
else
|
|
dest_bytes = alms->bytes2 + 1;
|
|
|
|
dissolve_pixels (alms->src2, alms->mask, *(alms->dest),
|
|
alms->x, alms->y,
|
|
alms->opacity, alms->length,
|
|
alms->bytes2, dest_bytes,
|
|
has_alpha2);
|
|
|
|
alms->combine = has_alpha1 ? COMBINE_INTEN_A_INTEN_A : COMBINE_INTEN_INTEN_A;
|
|
}
|
|
|
|
static void
|
|
layer_multiply_mode (struct apply_layer_mode_struct *alms)
|
|
{
|
|
multiply_pixels (alms->src1, alms->src2, *(alms->dest), alms->length,
|
|
alms->bytes1, alms->bytes2);
|
|
}
|
|
|
|
static void
|
|
layer_divide_mode (struct apply_layer_mode_struct *alms)
|
|
{
|
|
divide_pixels (alms->src1, alms->src2, *(alms->dest), alms->length,
|
|
alms->bytes1, alms->bytes2);
|
|
}
|
|
|
|
static void
|
|
layer_screen_mode (struct apply_layer_mode_struct *alms)
|
|
{
|
|
screen_pixels (alms->src1, alms->src2, *(alms->dest), alms->length,
|
|
alms->bytes1, alms->bytes2);
|
|
}
|
|
|
|
static void
|
|
layer_overlay_mode (struct apply_layer_mode_struct *alms)
|
|
{
|
|
overlay_pixels (alms->src1, alms->src2, *(alms->dest), alms->length,
|
|
alms->bytes1, alms->bytes2);
|
|
}
|
|
|
|
static void
|
|
layer_difference_mode (struct apply_layer_mode_struct *alms)
|
|
{
|
|
difference_pixels (alms->src1, alms->src2, *(alms->dest), alms->length,
|
|
alms->bytes1, alms->bytes2);
|
|
}
|
|
|
|
static void
|
|
layer_addition_mode (struct apply_layer_mode_struct *alms)
|
|
{
|
|
add_pixels (alms->src1, alms->src2, *(alms->dest), alms->length,
|
|
alms->bytes1, alms->bytes2);
|
|
}
|
|
|
|
static void
|
|
layer_subtract_mode (struct apply_layer_mode_struct *alms)
|
|
{
|
|
subtract_pixels (alms->src1, alms->src2, *(alms->dest), alms->length,
|
|
alms->bytes1, alms->bytes2);
|
|
}
|
|
|
|
static void
|
|
layer_darken_only_mode (struct apply_layer_mode_struct *alms)
|
|
{
|
|
darken_pixels (alms->src1, alms->src2, *(alms->dest), alms->length,
|
|
alms->bytes1, alms->bytes2);
|
|
}
|
|
|
|
static void
|
|
layer_lighten_only_mode (struct apply_layer_mode_struct *alms)
|
|
{
|
|
lighten_pixels (alms->src1, alms->src2, *(alms->dest), alms->length,
|
|
alms->bytes1, alms->bytes2);
|
|
}
|
|
|
|
static void
|
|
layer_hue_mode (struct apply_layer_mode_struct *alms)
|
|
{
|
|
/* only works on RGB color images */
|
|
if (alms->bytes1 > 2)
|
|
hue_only_pixels (alms->src1, alms->src2, *(alms->dest), alms->length,
|
|
alms->bytes1, alms->bytes2);
|
|
else
|
|
*(alms->dest) = alms->src2;
|
|
}
|
|
|
|
static void
|
|
layer_saturation_mode (struct apply_layer_mode_struct *alms)
|
|
{
|
|
/* only works on RGB color images */
|
|
if (alms->bytes1 > 2)
|
|
saturation_only_pixels (alms->src1, alms->src2, *(alms->dest),
|
|
alms->length, alms->bytes1, alms->bytes2);
|
|
else
|
|
*(alms->dest) = alms->src2;
|
|
}
|
|
|
|
static void
|
|
layer_value_mode (struct apply_layer_mode_struct *alms)
|
|
{
|
|
/* only works on RGB color images */
|
|
if (alms->bytes1 > 2)
|
|
value_only_pixels (alms->src1, alms->src2, *(alms->dest), alms->length,
|
|
alms->bytes1, alms->bytes2);
|
|
else
|
|
*(alms->dest) = alms->src2;
|
|
}
|
|
|
|
static void
|
|
layer_color_mode (struct apply_layer_mode_struct *alms)
|
|
{
|
|
/* only works on RGB color images */
|
|
if (alms->bytes1 > 2)
|
|
color_only_pixels (alms->src1, alms->src2, *(alms->dest), alms->length,
|
|
alms->bytes1, alms->bytes2);
|
|
else
|
|
*(alms->dest) = alms->src2;
|
|
}
|
|
|
|
static void
|
|
layer_behind_mode (struct apply_layer_mode_struct *alms)
|
|
{
|
|
*(alms->dest) = alms->src2;
|
|
if (HAS_ALPHA (alms->bytes1))
|
|
alms->combine = BEHIND_INTEN;
|
|
else
|
|
alms->combine = NO_COMBINATION;
|
|
}
|
|
|
|
static void
|
|
layer_replace_mode (struct apply_layer_mode_struct *alms)
|
|
{
|
|
*(alms->dest) = alms->src2;
|
|
alms->combine = REPLACE_INTEN;
|
|
}
|
|
|
|
static void
|
|
layer_erase_mode (struct apply_layer_mode_struct *alms)
|
|
{
|
|
*(alms->dest) = alms->src2;
|
|
/* If both sources have alpha channels, call erase function.
|
|
* Otherwise, just combine in the normal manner
|
|
*/
|
|
alms->combine =
|
|
(HAS_ALPHA (alms->bytes1) && HAS_ALPHA (alms->bytes2)) ? ERASE_INTEN : 0;
|
|
}
|
|
|
|
static void
|
|
layer_anti_erase_mode (struct apply_layer_mode_struct *alms)
|
|
{
|
|
*(alms->dest) = alms->src2;
|
|
alms->combine =
|
|
(HAS_ALPHA (alms->bytes1) && HAS_ALPHA (alms->bytes2)) ? ANTI_ERASE_INTEN : 0;
|
|
}
|
|
|
|
static void
|
|
layer_color_erase_mode (struct apply_layer_mode_struct *alms)
|
|
{
|
|
*(alms->dest) = alms->src2;
|
|
alms->combine =
|
|
(HAS_ALPHA (alms->bytes1) && HAS_ALPHA (alms->bytes2)) ? COLOR_ERASE_INTEN : 0;
|
|
}
|
|
|
|
static void
|
|
layer_dodge_mode (struct apply_layer_mode_struct *alms)
|
|
{
|
|
dodge_pixels (alms->src1, alms->src2, *(alms->dest), alms->length,
|
|
alms->bytes1, alms->bytes2);
|
|
}
|
|
|
|
static void
|
|
layer_burn_mode (struct apply_layer_mode_struct *alms)
|
|
{
|
|
burn_pixels (alms->src1, alms->src2, *(alms->dest), alms->length,
|
|
alms->bytes1, alms->bytes2);
|
|
}
|
|
|
|
static void
|
|
layer_hardlight_mode (struct apply_layer_mode_struct *alms)
|
|
{
|
|
hardlight_pixels (alms->src1, alms->src2, *(alms->dest), alms->length,
|
|
alms->bytes1, alms->bytes2);
|
|
}
|
|
|
|
static void
|
|
layer_softlight_mode (struct apply_layer_mode_struct *alms)
|
|
{
|
|
softlight_pixels (alms->src1, alms->src2, *(alms->dest), alms->length,
|
|
alms->bytes1, alms->bytes2);
|
|
}
|
|
|
|
static void
|
|
layer_grain_extract_mode (struct apply_layer_mode_struct *alms)
|
|
{
|
|
grain_extract_pixels (alms->src1, alms->src2, *(alms->dest), alms->length,
|
|
alms->bytes1, alms->bytes2);
|
|
}
|
|
|
|
static void
|
|
layer_grain_merge_mode (struct apply_layer_mode_struct *alms)
|
|
{
|
|
grain_merge_pixels (alms->src1, alms->src2, *(alms->dest), alms->length,
|
|
alms->bytes1, alms->bytes2);
|
|
}
|
|
|
|
#endif /* __PAINT_FUNCS_GENERIC_H__ */
|