mirror of
https://gitlab.gnome.org/GNOME/gimp
synced 2024-10-20 19:43:01 +00:00
f6858e21d1
* app/*.[ch]: Actually use the enum types GimpImageType, GimpImageBaseType, LayerModeEffects, PaintApplicationMode, BrushApplicationMode, GimpFillType and ConvertPaletteType, instead of just int or gint. Hopefully I catched most of the places where these should be used. Add an enum ConvolutionType, suffix the too general constants NORMAL, ABSOLUTE and NEGATIVE with _CONVOL. Use NORMAL_MODE instead of NORMAL in some places (this was what was intended). Fix some minor gccisms. * app/apptypes.h: New file. This file contains the above enumeration types, and some opaque struct typedefs. It was necessary to collect these in one header that doesn't include other headers, because when we started using the above mentioned types in the headers, all hell broke loose because of the spaghetti-like cross-inclusion mess between headers. (An example: Header A includes header B, which includes header C which includes A. B uses a type defined in A. This is not defined, because A hasn't defined it yet at the point where it includes B, and A included from B of course is skipped as we already are reading A.)
6068 lines
129 KiB
C
6068 lines
129 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.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
|
|
#include "appenv.h"
|
|
#include "gimprc.h"
|
|
#include "paint_funcs.h"
|
|
#include "boundary.h"
|
|
#include "tile_manager.h"
|
|
|
|
#include "tile_manager_pvt.h" /* For copy-on-write */
|
|
#include "tile.h" /* ick. */
|
|
|
|
#include "libgimp/gimpintl.h"
|
|
|
|
#define STD_BUF_SIZE 1021
|
|
#define MAXDIFF 195076
|
|
#define HASH_TABLE_SIZE 1021
|
|
#define RANDOM_TABLE_SIZE 4096
|
|
#define RANDOM_SEED 314159265
|
|
#define EPSILON 0.0001
|
|
|
|
#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))
|
|
|
|
typedef enum
|
|
{
|
|
MinifyX_MinifyY,
|
|
MinifyX_MagnifyY,
|
|
MagnifyX_MinifyY,
|
|
MagnifyX_MagnifyY
|
|
} ScaleType;
|
|
|
|
/* Layer modes information */
|
|
typedef struct _LayerMode LayerMode;
|
|
struct _LayerMode
|
|
{
|
|
int affect_alpha; /* does the layer mode affect the alpha channel */
|
|
int increase_opacity; /* layer mode can increase opacity */
|
|
int decrease_opacity; /* layer mode can decrease opacity */
|
|
char *name; /* layer mode specification */
|
|
};
|
|
|
|
LayerMode layer_modes[] = /* This must obviously be in the same
|
|
* order as the corresponding values
|
|
* in the LayerModeEffects enumeration.
|
|
*/
|
|
{
|
|
{ 1, 1, 0, N_("Normal") },
|
|
{ 1, 1, 0, N_("Dissolve") },
|
|
{ 1, 1, 0, N_("Behind") },
|
|
{ 0, 0, 0, N_("Multiply (Burn)") },
|
|
{ 0, 0, 0, N_("Screen") },
|
|
{ 0, 0, 0, N_("Overlay") },
|
|
{ 0, 0, 0, N_("Difference") },
|
|
{ 0, 0, 0, N_("Addition") },
|
|
{ 0, 0, 0, N_("Subtraction") },
|
|
{ 0, 0, 0, N_("Darken Only") },
|
|
{ 0, 0, 0, N_("Lighten Only") },
|
|
{ 0, 0, 0, N_("Hue") },
|
|
{ 0, 0, 0, N_("Saturation") },
|
|
{ 0, 0, 0, N_("Color") },
|
|
{ 0, 0, 0, N_("Value") },
|
|
{ 0, 0, 0, N_("Divide (Dodge)") },
|
|
{ 1, 0, 1, N_("Erase") },
|
|
{ 1, 1, 1, N_("Replace") },
|
|
{ 1, 0, 1, N_("Anti Erase") }
|
|
};
|
|
|
|
/* ColorHash structure */
|
|
typedef struct _ColorHash ColorHash;
|
|
|
|
struct _ColorHash
|
|
{
|
|
int pixel; /* R << 16 | G << 8 | B */
|
|
int index; /* colormap index */
|
|
GimpImage* gimage;
|
|
};
|
|
|
|
static ColorHash color_hash_table [HASH_TABLE_SIZE];
|
|
static int random_table [RANDOM_TABLE_SIZE];
|
|
static int color_hash_misses;
|
|
static int color_hash_hits;
|
|
static unsigned char * tmp_buffer; /* temporary buffer available upon request */
|
|
static int tmp_buffer_size;
|
|
static unsigned char no_mask = OPAQUE_OPACITY;
|
|
static int add_lut[256][256];
|
|
|
|
/*******************************/
|
|
/* Local function prototypes */
|
|
static int * make_curve (double, int *);
|
|
static void run_length_encode (unsigned char *, int *, int, int);
|
|
#if 0
|
|
static void draw_segments (PixelRegion *, BoundSeg *, int, int, int, int);
|
|
#endif
|
|
static double cubic (double, int, int, int, int);
|
|
static void apply_layer_mode_replace (unsigned char *, unsigned char *,
|
|
unsigned char *, unsigned char *,
|
|
int, int, int,
|
|
int, int, int, int *);
|
|
|
|
|
|
void
|
|
update_tile_rowhints (Tile* tile, int ymin, int ymax)
|
|
{
|
|
int bpp, ewidth, eheight;
|
|
int x,y;
|
|
guchar* ptr;
|
|
guchar alpha;
|
|
TileRowHint thishint;
|
|
|
|
#ifdef HINTS_SANITY
|
|
g_assert(tile!=NULL);
|
|
#endif
|
|
|
|
bpp = tile_bpp (tile);
|
|
ewidth = tile_ewidth (tile);
|
|
eheight = tile_eheight (tile);
|
|
|
|
if (bpp == 1 || bpp == 3)
|
|
{
|
|
for (y=ymin; y<=ymax; y++)
|
|
tile_set_rowhint (tile, y, TILEROWHINT_OPAQUE);
|
|
|
|
return;
|
|
}
|
|
|
|
if (bpp == 4)
|
|
{
|
|
#ifdef HINTS_SANITY
|
|
g_assert(tile!=NULL);
|
|
#endif
|
|
|
|
ptr = tile_data_pointer (tile, 0, ymin);
|
|
|
|
#ifdef HINTS_SANITY
|
|
g_assert(ptr!=NULL);
|
|
#endif
|
|
|
|
for (y = ymin; y <= ymax; y++)
|
|
{
|
|
thishint = tile_get_rowhint (tile, y);
|
|
|
|
#ifdef HINTS_SANITY
|
|
if (thishint==TILEROWHINT_BROKEN)
|
|
g_error("BROKEN y=%d",y);
|
|
if (thishint==TILEROWHINT_OUTOFRANGE)
|
|
g_error("OOR y=%d",y);
|
|
if (thishint==TILEROWHINT_UNDEFINED)
|
|
g_error("UNDEFINED y=%d - bpp=%d ew=%d eh=%d",
|
|
y,bpp,ewidth,eheight);
|
|
#endif
|
|
|
|
#ifdef HINTS_SANITY
|
|
if (thishint == TILEROWHINT_TRANSPARENT ||
|
|
thishint == TILEROWHINT_MIXED ||
|
|
thishint == TILEROWHINT_OPAQUE)
|
|
{
|
|
goto next_row4;
|
|
}
|
|
|
|
if (thishint != TILEROWHINT_UNKNOWN)
|
|
{
|
|
g_error("MEGABOGUS y=%d - bpp=%d ew=%d eh=%d",
|
|
y,bpp,ewidth,eheight);
|
|
}
|
|
#endif
|
|
|
|
if (thishint == TILEROWHINT_UNKNOWN)
|
|
{
|
|
alpha = ptr[3];
|
|
|
|
/* row is all-opaque or all-transparent? */
|
|
if (alpha == 0 || alpha == 255)
|
|
{
|
|
if (ewidth > 1)
|
|
{
|
|
for (x = 1; x < ewidth; x++)
|
|
{
|
|
if (ptr[x*4 + 3] != alpha)
|
|
{
|
|
tile_set_rowhint (tile, y, TILEROWHINT_MIXED);
|
|
goto next_row4;
|
|
}
|
|
}
|
|
}
|
|
tile_set_rowhint (tile, y,
|
|
(alpha == 0) ?
|
|
TILEROWHINT_TRANSPARENT :
|
|
TILEROWHINT_OPAQUE);
|
|
}
|
|
else
|
|
{
|
|
tile_set_rowhint (tile, y, TILEROWHINT_MIXED);
|
|
}
|
|
}
|
|
|
|
next_row4:
|
|
ptr += 4 * ewidth;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (bpp == 2)
|
|
{
|
|
#ifdef HINTS_SANITY
|
|
g_assert(tile!=NULL);
|
|
#endif
|
|
|
|
ptr = tile_data_pointer (tile, 0, ymin);
|
|
|
|
#ifdef HINTS_SANITY
|
|
g_assert(ptr!=NULL);
|
|
#endif
|
|
|
|
for (y = ymin; y <= ymax; y++)
|
|
{
|
|
thishint = tile_get_rowhint (tile, y);
|
|
|
|
#ifdef HINTS_SANITY
|
|
if (thishint==TILEROWHINT_BROKEN)
|
|
g_error("BROKEN y=%d",y);
|
|
if (thishint==TILEROWHINT_OUTOFRANGE)
|
|
g_error("OOR y=%d",y);
|
|
if (thishint==TILEROWHINT_UNDEFINED)
|
|
g_error("UNDEFINED y=%d - bpp=%d ew=%d eh=%d",
|
|
y,bpp,ewidth,eheight);
|
|
#endif
|
|
|
|
#ifdef HINTS_SANITY
|
|
if (thishint == TILEROWHINT_TRANSPARENT ||
|
|
thishint == TILEROWHINT_MIXED ||
|
|
thishint == TILEROWHINT_OPAQUE)
|
|
{
|
|
goto next_row2;
|
|
}
|
|
|
|
if (thishint != TILEROWHINT_UNKNOWN)
|
|
{
|
|
g_error("MEGABOGUS y=%d - bpp=%d ew=%d eh=%d",
|
|
y,bpp,ewidth,eheight);
|
|
}
|
|
#endif
|
|
|
|
if (thishint == TILEROWHINT_UNKNOWN)
|
|
{
|
|
alpha = ptr[1];
|
|
|
|
/* row is all-opaque or all-transparent? */
|
|
if (alpha == 0 || alpha == 255)
|
|
{
|
|
if (ewidth > 1)
|
|
{
|
|
for (x = 1; x < ewidth; x++)
|
|
{
|
|
if (ptr[x*2 + 1] != alpha)
|
|
{
|
|
tile_set_rowhint (tile, y, TILEROWHINT_MIXED);
|
|
goto next_row2;
|
|
}
|
|
}
|
|
}
|
|
tile_set_rowhint (tile, y,
|
|
(alpha == 0) ?
|
|
TILEROWHINT_TRANSPARENT :
|
|
TILEROWHINT_OPAQUE);
|
|
}
|
|
else
|
|
{
|
|
tile_set_rowhint (tile, y, TILEROWHINT_MIXED);
|
|
}
|
|
}
|
|
|
|
next_row2:
|
|
ptr += 2 * ewidth;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
g_warning ("update_tile_rowhints: Don't know about tiles with bpp==%d", bpp);
|
|
}
|
|
|
|
|
|
static unsigned char *
|
|
paint_funcs_get_buffer (int size)
|
|
{
|
|
if (size > tmp_buffer_size)
|
|
{
|
|
tmp_buffer_size = size;
|
|
tmp_buffer = (unsigned char *) g_realloc (tmp_buffer, size);
|
|
}
|
|
|
|
return tmp_buffer;
|
|
}
|
|
|
|
|
|
/*
|
|
* The equations: g(r) = exp (- r^2 / (2 * sigma^2))
|
|
* r = sqrt (x^2 + y ^2)
|
|
*/
|
|
|
|
static int *
|
|
make_curve (double sigma,
|
|
int *length)
|
|
{
|
|
int *curve;
|
|
double sigma2;
|
|
double l;
|
|
int temp;
|
|
int i, n;
|
|
|
|
sigma2 = 2 * sigma * sigma;
|
|
l = sqrt (-sigma2 * log (1.0 / 255.0));
|
|
|
|
n = ceil (l) * 2;
|
|
if ((n % 2) == 0)
|
|
n += 1;
|
|
|
|
curve = g_malloc (sizeof (int) * n);
|
|
|
|
*length = n / 2;
|
|
curve += *length;
|
|
curve[0] = 255;
|
|
|
|
for (i = 1; i <= *length; i++)
|
|
{
|
|
temp = (int) (exp (- (i * i) / sigma2) * 255);
|
|
curve[-i] = temp;
|
|
curve[i] = temp;
|
|
}
|
|
|
|
return curve;
|
|
}
|
|
|
|
|
|
static void
|
|
run_length_encode (unsigned char *src,
|
|
int *dest,
|
|
int w,
|
|
int bytes)
|
|
{
|
|
int start;
|
|
int i;
|
|
int j;
|
|
unsigned char last;
|
|
|
|
last = *src;
|
|
src += bytes;
|
|
start = 0;
|
|
|
|
for (i = 1; i < w; i++)
|
|
{
|
|
if (*src != last)
|
|
{
|
|
for (j = start; j < i; j++)
|
|
{
|
|
*dest++ = (i - j);
|
|
*dest++ = last;
|
|
}
|
|
start = i;
|
|
last = *src;
|
|
}
|
|
src += bytes;
|
|
}
|
|
|
|
for (j = start; j < i; j++)
|
|
{
|
|
*dest++ = (i - j);
|
|
*dest++ = last;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
static void
|
|
draw_segments (PixelRegion *destPR,
|
|
BoundSeg *bs,
|
|
int num_segs,
|
|
int off_x,
|
|
int off_y,
|
|
int opacity)
|
|
{
|
|
int x1, y1, x2, y2;
|
|
int tmp, i, length;
|
|
unsigned char *line;
|
|
|
|
length = MAXIMUM (destPR->w, destPR->h);
|
|
line = paint_funcs_get_buffer (length);
|
|
memset (line, opacity, length);
|
|
|
|
for (i = 0; i < num_segs; i++)
|
|
{
|
|
x1 = bs[i].x1 + off_x;
|
|
y1 = bs[i].y1 + off_y;
|
|
x2 = bs[i].x2 + off_x;
|
|
y2 = bs[i].y2 + off_y;
|
|
|
|
if (bs[i].open == 0)
|
|
{
|
|
/* If it is vertical */
|
|
if (x1 == x2)
|
|
{
|
|
x1 -= 1;
|
|
x2 -= 1;
|
|
}
|
|
else
|
|
{
|
|
y1 -= 1;
|
|
y2 -= 1;
|
|
}
|
|
}
|
|
|
|
/* render segment */
|
|
x1 = BOUNDS (x1, 0, destPR->w - 1);
|
|
y1 = BOUNDS (y1, 0, destPR->h - 1);
|
|
x2 = BOUNDS (x2, 0, destPR->w - 1);
|
|
y2 = BOUNDS (y2, 0, destPR->h - 1);
|
|
|
|
if (x1 == x2)
|
|
{
|
|
if (y2 < y1)
|
|
{
|
|
tmp = y1;
|
|
y1 = y2;
|
|
y2 = tmp;
|
|
}
|
|
pixel_region_set_col (destPR, x1, y1, (y2 - y1), line);
|
|
}
|
|
else
|
|
{
|
|
if (x2 < x1)
|
|
{
|
|
tmp = x1;
|
|
x1 = x2;
|
|
x2 = tmp;
|
|
}
|
|
pixel_region_set_row (destPR, x1, y1, (x2 - x1), line);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static double
|
|
cubic (double dx,
|
|
int jm1,
|
|
int j,
|
|
int jp1,
|
|
int jp2)
|
|
{
|
|
double result;
|
|
|
|
result = ((( ( - jm1 + j - jp1 + jp2 ) * dx +
|
|
( jm1 + jm1 - j - j + jp1 - jp2 ) ) * dx +
|
|
( - jm1 + jp1 ) ) * dx + j );
|
|
|
|
if (result < 0.0)
|
|
result = 0.0;
|
|
if (result > 255.0)
|
|
result = 255.0;
|
|
|
|
return result;
|
|
}
|
|
|
|
/*********************/
|
|
/* FUNCTIONS */
|
|
/*********************/
|
|
|
|
void
|
|
paint_funcs_setup ()
|
|
{
|
|
int i;
|
|
int j,k;
|
|
int tmp_sum;
|
|
|
|
/* allocate the temporary buffer */
|
|
tmp_buffer = (unsigned char *) g_malloc (STD_BUF_SIZE);
|
|
tmp_buffer_size = STD_BUF_SIZE;
|
|
|
|
/* initialize the color hash table--invalidate all entries */
|
|
for (i = 0; i < HASH_TABLE_SIZE; i++)
|
|
color_hash_table[i].gimage = NULL;
|
|
color_hash_misses = 0;
|
|
color_hash_hits = 0;
|
|
|
|
/* generate a table of random seeds */
|
|
srand (RANDOM_SEED);
|
|
for (i = 0; i < RANDOM_TABLE_SIZE; i++)
|
|
random_table[i] = rand ();
|
|
|
|
for (i = 0; i < RANDOM_TABLE_SIZE; i++)
|
|
{
|
|
int tmp;
|
|
int swap = i + rand () % (RANDOM_TABLE_SIZE - i);
|
|
tmp = random_table[i];
|
|
random_table[i] = random_table[swap];
|
|
random_table[swap] = tmp;
|
|
}
|
|
|
|
for (j = 0; j < 256; j++)
|
|
{ /* rows */
|
|
for (k = 0; k < 256; k++)
|
|
{ /* column */
|
|
tmp_sum = j + k;
|
|
/* printf("tmp_sum: %d", tmp_sum); */
|
|
if(tmp_sum > 255)
|
|
tmp_sum = 255;
|
|
/* printf(" max: %d \n", add_lut[j][k]); */
|
|
add_lut[j][k] = tmp_sum;
|
|
}
|
|
}
|
|
|
|
/* for (j = 0; j < 255; j++) */
|
|
/* { //rows */
|
|
/* for (k = 0; k < 255; k++) */
|
|
/* { //column */
|
|
/* printf ("%d",add_lut[j][k]); */
|
|
/* printf(" "); */
|
|
/* } */
|
|
/* printf("\n"); */
|
|
/* } */
|
|
|
|
}
|
|
|
|
|
|
void
|
|
paint_funcs_free ()
|
|
{
|
|
/* free the temporary buffer */
|
|
g_free (tmp_buffer);
|
|
|
|
/* print out the hash table statistics
|
|
printf ("RGB->indexed hash table lookups: %d\n", color_hash_hits + color_hash_misses);
|
|
printf ("RGB->indexed hash table hits: %d\n", color_hash_hits);
|
|
printf ("RGB->indexed hash table misses: %d\n", color_hash_misses);
|
|
printf ("RGB->indexed hash table hit rate: %f\n",
|
|
100.0 * color_hash_hits / (color_hash_hits + color_hash_misses));
|
|
*/
|
|
}
|
|
|
|
|
|
void
|
|
color_pixels (unsigned char *dest,
|
|
const unsigned char *color,
|
|
int w,
|
|
int 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.
|
|
*/
|
|
register unsigned char c0, c1, c2;
|
|
register guint32 *longd, longc;
|
|
register guint16 *shortd, shortc;
|
|
|
|
switch (bytes)
|
|
{
|
|
case 1:
|
|
memset(dest, *color, w);
|
|
break;
|
|
case 2:
|
|
shortc = ((guint16 *)color)[0];
|
|
shortd = (guint16 *)dest;
|
|
while (w--)
|
|
{
|
|
*shortd = shortc;
|
|
shortd++;
|
|
}
|
|
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:
|
|
longc = ((guint32 *)color)[0];
|
|
longd = (guint32 *)dest;
|
|
while (w--)
|
|
{
|
|
*longd = longc;
|
|
longd++;
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
int b;
|
|
while (w--)
|
|
{
|
|
for (b = 0; b < bytes; b++)
|
|
dest[b] = color[b];
|
|
|
|
dest += bytes;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
blend_pixels (const unsigned char *src1,
|
|
const unsigned char *src2,
|
|
unsigned char *dest,
|
|
int blend,
|
|
int w,
|
|
int bytes,
|
|
int has_alpha)
|
|
{
|
|
int b;
|
|
unsigned char 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;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
shade_pixels (const unsigned char *src,
|
|
unsigned char *dest,
|
|
const unsigned char *col,
|
|
int blend,
|
|
int w,
|
|
int bytes,
|
|
int has_alpha)
|
|
{
|
|
int alpha, b;
|
|
unsigned char blend2 = (255 - blend);
|
|
|
|
alpha = (has_alpha) ? bytes - 1 : bytes;
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
extract_alpha_pixels (const unsigned char *src,
|
|
const unsigned char *mask,
|
|
unsigned char *dest,
|
|
int w,
|
|
int bytes)
|
|
{
|
|
int alpha;
|
|
const unsigned char * m;
|
|
int tmp;
|
|
/* printf("[eap:%d]", w);*/
|
|
|
|
if (mask)
|
|
m = mask;
|
|
else
|
|
m = &no_mask;
|
|
|
|
alpha = bytes - 1;
|
|
while (w --)
|
|
{
|
|
*dest++ = INT_MULT(src[alpha], *m, tmp);
|
|
|
|
if (mask)
|
|
m++;
|
|
src += bytes;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
darken_pixels (const unsigned char *src1,
|
|
const unsigned char *src2,
|
|
unsigned char *dest,
|
|
int length,
|
|
int bytes1,
|
|
int bytes2,
|
|
int has_alpha1,
|
|
int has_alpha2)
|
|
{
|
|
int b, alpha;
|
|
unsigned char s1, s2;
|
|
|
|
alpha = (has_alpha1 || has_alpha2) ? MAXIMUM (bytes1, bytes2) - 1 : bytes1;
|
|
|
|
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
|
|
lighten_pixels (const unsigned char *src1,
|
|
const unsigned char *src2,
|
|
unsigned char *dest,
|
|
int length,
|
|
int bytes1,
|
|
int bytes2,
|
|
int has_alpha1,
|
|
int has_alpha2)
|
|
{
|
|
int b, alpha;
|
|
unsigned char s1, s2;
|
|
|
|
alpha = (has_alpha1 || has_alpha2) ? MAXIMUM (bytes1, bytes2) - 1 : bytes1;
|
|
|
|
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
|
|
hsv_only_pixels (const unsigned char *src1,
|
|
const unsigned char *src2,
|
|
unsigned char *dest,
|
|
int mode,
|
|
int length,
|
|
int bytes1,
|
|
int bytes2,
|
|
int has_alpha1,
|
|
int has_alpha2)
|
|
{
|
|
int r1, g1, b1;
|
|
int 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];
|
|
rgb_to_hsv (&r1, &g1, &b1);
|
|
rgb_to_hsv (&r2, &g2, &b2);
|
|
|
|
switch (mode)
|
|
{
|
|
case HUE_MODE:
|
|
r1 = r2;
|
|
break;
|
|
case SATURATION_MODE:
|
|
g1 = g2;
|
|
break;
|
|
case VALUE_MODE:
|
|
b1 = b2;
|
|
break;
|
|
}
|
|
|
|
/* set the destination */
|
|
hsv_to_rgb (&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
|
|
color_only_pixels (const unsigned char *src1,
|
|
const unsigned char *src2,
|
|
unsigned char *dest,
|
|
int mode,
|
|
int length,
|
|
int bytes1,
|
|
int bytes2,
|
|
int has_alpha1,
|
|
int has_alpha2)
|
|
{
|
|
int r1, g1, b1;
|
|
int 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];
|
|
rgb_to_hls (&r1, &g1, &b1);
|
|
rgb_to_hls (&r2, &g2, &b2);
|
|
|
|
/* transfer hue and saturation to the source pixel */
|
|
r1 = r2;
|
|
b1 = b2;
|
|
|
|
/* set the destination */
|
|
hls_to_rgb (&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
|
|
multiply_pixels (const unsigned char *src1,
|
|
const unsigned char *src2,
|
|
unsigned char *dest,
|
|
int length,
|
|
int bytes1,
|
|
int bytes2,
|
|
int has_alpha1,
|
|
int has_alpha2)
|
|
{
|
|
int alpha, b;
|
|
int tmp;
|
|
alpha = (has_alpha1 || has_alpha2) ? MAXIMUM (bytes1, bytes2) - 1 : bytes1;
|
|
|
|
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
|
|
divide_pixels (const unsigned char *src1,
|
|
const unsigned char *src2,
|
|
unsigned char *dest,
|
|
int length,
|
|
int bytes1,
|
|
int bytes2,
|
|
int has_alpha1,
|
|
int has_alpha2)
|
|
{
|
|
int alpha, b, result;
|
|
|
|
alpha = (has_alpha1 || has_alpha2) ? MAXIMUM (bytes1, bytes2) - 1 : bytes1;
|
|
|
|
while (length --)
|
|
{
|
|
for (b = 0; b < alpha; b++) {
|
|
result = ((src1[b] * 256) / (1+src2[b]));
|
|
dest[b] = MINIMUM(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
|
|
screen_pixels (const unsigned char *src1,
|
|
const unsigned char *src2,
|
|
unsigned char *dest,
|
|
int length,
|
|
int bytes1,
|
|
int bytes2,
|
|
int has_alpha1,
|
|
int has_alpha2)
|
|
{
|
|
int alpha, b;
|
|
int tmp;
|
|
|
|
alpha = (has_alpha1 || has_alpha2) ? MAXIMUM (bytes1, bytes2) - 1 : bytes1;
|
|
|
|
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
|
|
overlay_pixels (const unsigned char *src1,
|
|
const unsigned char *src2,
|
|
unsigned char *dest,
|
|
int length,
|
|
int bytes1,
|
|
int bytes2,
|
|
int has_alpha1,
|
|
int has_alpha2)
|
|
{
|
|
int alpha, b;
|
|
int tmp;
|
|
|
|
alpha = (has_alpha1 || has_alpha2) ? MAXIMUM (bytes1, bytes2) - 1 : bytes1;
|
|
|
|
while (length --)
|
|
{
|
|
/* for (b = 0; b < alpha; b++)
|
|
{
|
|
screen = 255 - INT_MULT((255 - src1[b]), (255 - src2[b]), tmp);
|
|
mult = INT_MULT(src1[b] ,src2[b], tmp);
|
|
dest[b] = INT_BLEND(screen , mult, src1[b], tmp);
|
|
}
|
|
*/
|
|
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
|
|
add_pixels (const unsigned char *src1,
|
|
const unsigned char *src2,
|
|
unsigned char *dest,
|
|
int length,
|
|
int bytes1,
|
|
int bytes2,
|
|
int has_alpha1,
|
|
int has_alpha2)
|
|
{
|
|
int alpha, b;
|
|
/* int sum; */
|
|
|
|
alpha = (has_alpha1 || has_alpha2) ? MAXIMUM (bytes1, bytes2) - 1 : bytes1;
|
|
|
|
while (length --)
|
|
{
|
|
for (b = 0; b < alpha; b++)
|
|
{
|
|
/* sum = src1[b] + src2[b]; */
|
|
dest[b] = add_lut[ (src1[b])] [(src2[b])];
|
|
/* dest[b] = MAX255 (sum); */
|
|
/* dest[b] = sum | ((sum&256) - ((sum&256) >> 8)); */
|
|
/* dest[b] = (sum > 255) ? 255 : sum; */ /* older, little slower */
|
|
}
|
|
|
|
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
|
|
subtract_pixels (const unsigned char *src1,
|
|
const unsigned char *src2,
|
|
unsigned char *dest,
|
|
int length,
|
|
int bytes1,
|
|
int bytes2,
|
|
int has_alpha1,
|
|
int has_alpha2)
|
|
{
|
|
int alpha, b;
|
|
int diff;
|
|
|
|
alpha = (has_alpha1 || has_alpha2) ? MAXIMUM (bytes1, bytes2) - 1 : bytes1;
|
|
|
|
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
|
|
difference_pixels (const unsigned char *src1,
|
|
const unsigned char *src2,
|
|
unsigned char *dest,
|
|
int length,
|
|
int bytes1,
|
|
int bytes2,
|
|
int has_alpha1,
|
|
int has_alpha2)
|
|
{
|
|
int alpha, b;
|
|
int diff;
|
|
|
|
alpha = (has_alpha1 || has_alpha2) ? MAXIMUM (bytes1, bytes2) - 1 : bytes1;
|
|
|
|
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
|
|
dissolve_pixels (const unsigned char *src,
|
|
unsigned char *dest,
|
|
int x,
|
|
int y,
|
|
int opacity,
|
|
int length,
|
|
int sb,
|
|
int db,
|
|
int has_alpha)
|
|
{
|
|
int alpha, b;
|
|
int rand_val;
|
|
|
|
#if defined(ENABLE_MP) && defined(__GLIBC__)
|
|
/* The glibc 2.1 documentation recommends using the SVID random functions
|
|
* instead of rand_r
|
|
*/
|
|
struct drand48_data seed;
|
|
long temp_val;
|
|
|
|
srand48_r (random_table[y % RANDOM_TABLE_SIZE], &seed);
|
|
for (b = 0; b < x; b++)
|
|
lrand48_r (&seed, &temp_val);
|
|
#elif defined(ENABLE_MP) && !defined(__GLIBC__)
|
|
/* If we are running with multiple threads rand_r give _much_ better
|
|
* performance than rand
|
|
*/
|
|
unsigned int seed;
|
|
seed = random_table[y % RANDOM_TABLE_SIZE];
|
|
for (b = 0; b < x; b++)
|
|
rand_r (&seed);
|
|
#else
|
|
/* Set up the random number generator */
|
|
srand (random_table[y % RANDOM_TABLE_SIZE]);
|
|
for (b = 0; b < x; b++)
|
|
rand ();
|
|
#endif
|
|
|
|
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 */
|
|
#if defined(ENABLE_MP) && defined(__GLIBC__)
|
|
lrand48_r (&seed, &temp_val);
|
|
rand_val = temp_val & 0xff;
|
|
#elif defined(ENABLE_MP) && !defined(__GLIBC__)
|
|
rand_val = (rand_r (&seed) & 0xff);
|
|
#else
|
|
rand_val = (rand () & 0xff);
|
|
#endif
|
|
if (has_alpha)
|
|
dest[alpha] = (rand_val > src[alpha]) ? 0 : src[alpha];
|
|
else
|
|
dest[alpha] = (rand_val > opacity) ? 0 : OPAQUE_OPACITY;
|
|
|
|
dest += db;
|
|
src += sb;
|
|
}
|
|
}
|
|
|
|
void
|
|
replace_pixels (unsigned char *src1,
|
|
unsigned char *src2,
|
|
unsigned char *dest,
|
|
unsigned char *mask,
|
|
int length,
|
|
int opacity,
|
|
int *affect,
|
|
int bytes1,
|
|
int bytes2)
|
|
{
|
|
int alpha;
|
|
int b;
|
|
double a_val, a_recip, mask_val;
|
|
double norm_opacity;
|
|
int s1_a, s2_a;
|
|
int new_val;
|
|
|
|
if (bytes1 != bytes2)
|
|
{
|
|
g_warning ("replace_pixels only works on commensurate pixel regions");
|
|
return;
|
|
}
|
|
|
|
alpha = bytes1 - 1;
|
|
norm_opacity = opacity * (1.0 / 65025.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)
|
|
a_recip = 0;
|
|
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++;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
swap_pixels (unsigned char *src,
|
|
unsigned char *dest,
|
|
int length)
|
|
{
|
|
while (length--)
|
|
{
|
|
*src = *src ^ *dest;
|
|
*dest = *dest ^ *src;
|
|
*src = *src ^ *dest;
|
|
src++;
|
|
dest++;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
scale_pixels (const unsigned char *src,
|
|
unsigned char *dest,
|
|
int length,
|
|
int scale)
|
|
{
|
|
int tmp;
|
|
while (length --)
|
|
{
|
|
*dest++ = (unsigned char) INT_MULT(*src,scale,tmp);
|
|
src++;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
add_alpha_pixels (const unsigned char *src,
|
|
unsigned char *dest,
|
|
int length,
|
|
int bytes)
|
|
{
|
|
int 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;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
flatten_pixels (const unsigned char *src,
|
|
unsigned char *dest,
|
|
const unsigned char *bg,
|
|
int length,
|
|
int bytes)
|
|
{
|
|
int alpha, b;
|
|
int 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;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
gray_to_rgb_pixels (const unsigned char *src,
|
|
unsigned char *dest,
|
|
int length,
|
|
int bytes)
|
|
{
|
|
int b;
|
|
int dest_bytes;
|
|
int has_alpha;
|
|
|
|
has_alpha = (bytes == 2) ? 1 : 0;
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
apply_mask_to_alpha_channel (unsigned char *src,
|
|
const unsigned char *mask,
|
|
int opacity,
|
|
int length,
|
|
int bytes)
|
|
{
|
|
long 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;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
combine_mask_and_alpha_channel (unsigned char *src,
|
|
const unsigned char *mask,
|
|
int opacity,
|
|
int length,
|
|
int bytes)
|
|
{
|
|
int mask_val;
|
|
int alpha;
|
|
int tmp;
|
|
alpha = bytes - 1;
|
|
src += alpha;
|
|
|
|
if (opacity != 255)
|
|
while (length --)
|
|
{
|
|
mask_val = INT_MULT(*mask, opacity, tmp);
|
|
mask++;
|
|
*src = *src + INT_MULT((255 - *src) , mask_val, tmp);
|
|
src += bytes;
|
|
}
|
|
else
|
|
while (length --)
|
|
{
|
|
*src = *src + INT_MULT((255 - *src) , *mask, tmp);
|
|
src += bytes;
|
|
mask++;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
copy_gray_to_inten_a_pixels (const unsigned char *src,
|
|
unsigned char *dest,
|
|
int length,
|
|
int bytes)
|
|
{
|
|
int b;
|
|
int alpha;
|
|
|
|
alpha = bytes - 1;
|
|
while (length --)
|
|
{
|
|
for (b = 0; b < alpha; b++)
|
|
dest[b] = *src;
|
|
dest[b] = OPAQUE_OPACITY;
|
|
|
|
src ++;
|
|
dest += bytes;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
initial_channel_pixels (const unsigned char *src,
|
|
unsigned char *dest,
|
|
int length,
|
|
int bytes)
|
|
{
|
|
int alpha, b;
|
|
|
|
alpha = bytes - 1;
|
|
while (length --)
|
|
{
|
|
for (b = 0; b < alpha; b++)
|
|
dest[b] = src[0];
|
|
|
|
dest[alpha] = OPAQUE_OPACITY;
|
|
|
|
dest += bytes;
|
|
src ++;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
initial_indexed_pixels (const unsigned char *src,
|
|
unsigned char *dest,
|
|
const unsigned char *cmap,
|
|
int length)
|
|
{
|
|
int 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;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
initial_indexed_a_pixels (const unsigned char *src,
|
|
unsigned char *dest,
|
|
const unsigned char *mask,
|
|
const unsigned char *cmap,
|
|
int opacity,
|
|
int length)
|
|
{
|
|
int col_index;
|
|
unsigned char new_alpha;
|
|
const unsigned char * m;
|
|
long 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++;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
initial_inten_pixels (const unsigned char *src,
|
|
unsigned char *dest,
|
|
const unsigned char *mask,
|
|
int opacity,
|
|
const int *affect,
|
|
int length,
|
|
int bytes)
|
|
{
|
|
int b, dest_bytes;
|
|
const unsigned char * m;
|
|
int tmp;
|
|
int l;
|
|
unsigned char *destp;
|
|
const unsigned char *srcp;
|
|
|
|
if (mask)
|
|
m = mask;
|
|
else
|
|
m = &no_mask;
|
|
|
|
/* This function assumes the source has no alpha channel and
|
|
* the destination has an alpha channel. So dest_bytes = bytes + 1
|
|
*/
|
|
dest_bytes = bytes + 1;
|
|
|
|
if (bytes == 3 && affect[0] && affect[1] && affect[2])
|
|
{
|
|
if (!affect[bytes])
|
|
opacity = 0;
|
|
destp = dest + bytes;
|
|
if (mask && 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 */
|
|
if (!affect[bytes])
|
|
opacity = 0;
|
|
destp = dest + bytes;
|
|
if (mask && opacity != 0)
|
|
while (length--)
|
|
{
|
|
*destp = INT_MULT(opacity , *m, tmp);
|
|
destp += dest_bytes;
|
|
m++;
|
|
}
|
|
else
|
|
while (length--)
|
|
{
|
|
*destp = opacity;
|
|
destp += dest_bytes;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
initial_inten_a_pixels (const unsigned char *src,
|
|
unsigned char *dest,
|
|
const unsigned char *mask,
|
|
int opacity,
|
|
const int *affect,
|
|
int length,
|
|
int bytes)
|
|
{
|
|
int alpha, b;
|
|
const unsigned char * m;
|
|
long 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)
|
|
: 0;
|
|
|
|
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) : 0;
|
|
|
|
dest += bytes;
|
|
src += bytes;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
combine_indexed_and_indexed_pixels (const unsigned char *src1,
|
|
const unsigned char *src2,
|
|
unsigned char *dest,
|
|
const unsigned char *mask,
|
|
int opacity,
|
|
const int *affect,
|
|
int length,
|
|
int bytes)
|
|
{
|
|
int b;
|
|
unsigned char new_alpha;
|
|
const unsigned char * m;
|
|
int tmp;
|
|
if (mask)
|
|
{
|
|
m = mask;
|
|
while (length --)
|
|
{
|
|
new_alpha = INT_MULT(*m , opacity, tmp);
|
|
|
|
for (b = 0; b < bytes; b++)
|
|
dest[b] = (affect[b] && new_alpha > 127) ? src2[b] : src1[b];
|
|
|
|
m++;
|
|
|
|
src1 += bytes;
|
|
src2 += bytes;
|
|
dest += bytes;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (length --)
|
|
{
|
|
new_alpha = opacity;
|
|
|
|
for (b = 0; b < bytes; b++)
|
|
dest[b] = (affect[b] && new_alpha > 127) ? src2[b] : src1[b];
|
|
|
|
src1 += bytes;
|
|
src2 += bytes;
|
|
dest += bytes;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
combine_indexed_and_indexed_a_pixels (const unsigned char *src1,
|
|
const unsigned char *src2,
|
|
unsigned char *dest,
|
|
const unsigned char *mask,
|
|
int opacity,
|
|
const int *affect,
|
|
int length,
|
|
int bytes)
|
|
{
|
|
int b, alpha;
|
|
unsigned char new_alpha;
|
|
const unsigned char * m;
|
|
int src2_bytes;
|
|
long tmp;
|
|
alpha = 1;
|
|
src2_bytes = 2;
|
|
|
|
if (mask)
|
|
{
|
|
m = mask;
|
|
while (length --)
|
|
{
|
|
new_alpha = INT_MULT3(src2[alpha], *m, opacity, tmp);
|
|
|
|
for (b = 0; b < bytes; b++)
|
|
dest[b] = (affect[b] && new_alpha > 127) ? src2[b] : src1[b];
|
|
|
|
m++;
|
|
|
|
src1 += bytes;
|
|
src2 += src2_bytes;
|
|
dest += bytes;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (length --)
|
|
{
|
|
new_alpha = INT_MULT(src2[alpha], opacity, tmp);
|
|
|
|
for (b = 0; b < bytes; b++)
|
|
dest[b] = (affect[b] && new_alpha > 127) ? src2[b] : src1[b];
|
|
|
|
src1 += bytes;
|
|
src2 += src2_bytes;
|
|
dest += bytes;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
combine_indexed_a_and_indexed_a_pixels (const unsigned char *src1,
|
|
const unsigned char *src2,
|
|
unsigned char *dest,
|
|
const unsigned char *mask,
|
|
int opacity,
|
|
const int *affect,
|
|
int length,
|
|
int bytes)
|
|
{
|
|
int b, alpha;
|
|
unsigned char new_alpha;
|
|
const unsigned char * m;
|
|
long tmp;
|
|
|
|
alpha = 1;
|
|
|
|
if (mask)
|
|
{
|
|
m = mask;
|
|
while (length --)
|
|
{
|
|
new_alpha = INT_MULT3(src2[alpha], *m, opacity, tmp);
|
|
|
|
for (b = 0; b < alpha; b++)
|
|
dest[b] = (affect[b] && new_alpha > 127) ? src2[b] : src1[b];
|
|
|
|
dest[alpha] = (affect[alpha] && new_alpha > 127) ? OPAQUE_OPACITY : src1[alpha];
|
|
|
|
m++;
|
|
|
|
src1 += bytes;
|
|
src2 += bytes;
|
|
dest += bytes;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (length --)
|
|
{
|
|
new_alpha = INT_MULT(src2[alpha], opacity, tmp);
|
|
|
|
for (b = 0; b < alpha; b++)
|
|
dest[b] = (affect[b] && new_alpha > 127) ? src2[b] : src1[b];
|
|
|
|
dest[alpha] = (affect[alpha] && new_alpha > 127) ? OPAQUE_OPACITY : src1[alpha];
|
|
|
|
src1 += bytes;
|
|
src2 += bytes;
|
|
dest += bytes;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
combine_inten_a_and_indexed_a_pixels (const unsigned char *src1,
|
|
const unsigned char *src2,
|
|
unsigned char *dest,
|
|
const unsigned char *mask,
|
|
const unsigned char *cmap,
|
|
int opacity,
|
|
int length,
|
|
int bytes)
|
|
{
|
|
int b, alpha;
|
|
unsigned char new_alpha;
|
|
int src2_bytes;
|
|
int index;
|
|
long tmp;
|
|
|
|
alpha = 1;
|
|
src2_bytes = 2;
|
|
|
|
if (mask)
|
|
{
|
|
const unsigned char *m = mask;
|
|
while (length --)
|
|
{
|
|
new_alpha = INT_MULT3(src2[alpha], *m, opacity, tmp);
|
|
|
|
index = src2[0] * 3;
|
|
|
|
for (b = 0; b < bytes-1; b++)
|
|
dest[b] = (new_alpha > 127) ? cmap[index + b] : src1[b];
|
|
|
|
dest[b] = (new_alpha > 127) ? OPAQUE_OPACITY : src1[b]; /* alpha channel is opaque */
|
|
|
|
m++;
|
|
|
|
src1 += bytes;
|
|
src2 += src2_bytes;
|
|
dest += bytes;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (length --)
|
|
{
|
|
new_alpha = INT_MULT(src2[alpha], opacity, tmp);
|
|
|
|
index = src2[0] * 3;
|
|
|
|
for (b = 0; b < bytes-1; b++)
|
|
dest[b] = (new_alpha > 127) ? cmap[index + b] : src1[b];
|
|
|
|
dest[b] = (new_alpha > 127) ? OPAQUE_OPACITY : src1[b]; /* alpha channel is opaque */
|
|
|
|
/* m++; /Per */
|
|
|
|
src1 += bytes;
|
|
src2 += src2_bytes;
|
|
dest += bytes;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
combine_inten_and_inten_pixels (const unsigned char *src1,
|
|
const unsigned char *src2,
|
|
unsigned char *dest,
|
|
const unsigned char *mask,
|
|
int opacity,
|
|
const int *affect,
|
|
int length,
|
|
int bytes)
|
|
{
|
|
int b;
|
|
unsigned char new_alpha;
|
|
const unsigned char * m;
|
|
int tmp;
|
|
if (mask)
|
|
{
|
|
m = mask;
|
|
while (length --)
|
|
{
|
|
new_alpha = INT_MULT(*m, opacity, tmp);
|
|
|
|
for (b = 0; b < bytes; b++)
|
|
dest[b] = (affect[b]) ?
|
|
INT_BLEND(src2[b], src1[b], new_alpha, tmp) :
|
|
src1[b];
|
|
|
|
m++;
|
|
|
|
src1 += bytes;
|
|
src2 += bytes;
|
|
dest += bytes;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (length --)
|
|
{
|
|
|
|
for (b = 0; b < bytes; b++)
|
|
dest[b] = (affect[b]) ?
|
|
INT_BLEND(src2[b], src1[b], opacity, tmp) :
|
|
src1[b];
|
|
|
|
src1 += bytes;
|
|
src2 += bytes;
|
|
dest += bytes;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
combine_inten_and_inten_a_pixels (const unsigned char *src1,
|
|
const unsigned char *src2,
|
|
unsigned char *dest,
|
|
const unsigned char *mask,
|
|
int opacity,
|
|
const int *affect,
|
|
int length,
|
|
int bytes)
|
|
{
|
|
int alpha, b;
|
|
int src2_bytes;
|
|
unsigned char new_alpha;
|
|
const unsigned char * m;
|
|
register long t1;
|
|
alpha = bytes;
|
|
src2_bytes = bytes + 1;
|
|
|
|
if (mask)
|
|
{
|
|
m = mask;
|
|
while (length --)
|
|
{
|
|
new_alpha = INT_MULT3(src2[alpha], *m, opacity, t1);
|
|
|
|
for (b = 0; b < bytes; b++)
|
|
dest[b] = (affect[b]) ?
|
|
INT_BLEND(src2[b], src1[b], new_alpha, t1) :
|
|
src1[b];
|
|
|
|
m++;
|
|
src1 += bytes;
|
|
src2 += src2_bytes;
|
|
dest += bytes;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bytes == 3 && affect[0] && affect[1] && affect[2])
|
|
while (length --)
|
|
{
|
|
new_alpha = INT_MULT(src2[alpha],opacity,t1);
|
|
dest[0] = INT_BLEND(src2[0] , src1[0] , new_alpha, t1);
|
|
dest[1] = INT_BLEND(src2[1] , src1[1] , new_alpha, t1);
|
|
dest[2] = INT_BLEND(src2[2] , src1[2] , new_alpha, t1);
|
|
src1 += bytes;
|
|
src2 += src2_bytes;
|
|
dest += bytes;
|
|
}
|
|
else
|
|
while (length --)
|
|
{
|
|
new_alpha = INT_MULT(src2[alpha],opacity,t1);
|
|
for (b = 0; b < bytes; b++)
|
|
dest[b] = (affect[b]) ?
|
|
INT_BLEND(src2[b] , src1[b] , new_alpha, t1) :
|
|
src1[b];
|
|
|
|
src1 += bytes;
|
|
src2 += src2_bytes;
|
|
dest += bytes;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*orig #define alphify(src2_alpha,new_alpha) \
|
|
if (new_alpha == 0 || src2_alpha == 0) \
|
|
{ \
|
|
for (b = 0; b < alpha; b++) \
|
|
dest[b] = src1 [b]; \
|
|
} \
|
|
else if (src2_alpha == new_alpha){ \
|
|
for (b = 0; b < alpha; b++) \
|
|
dest [b] = affect [b] ? src2 [b] : src1 [b]; \
|
|
} else { \
|
|
ratio = (float) src2_alpha / new_alpha; \
|
|
compl_ratio = 1.0 - ratio; \
|
|
\
|
|
for (b = 0; b < alpha; b++) \
|
|
dest[b] = affect[b] ? \
|
|
(unsigned char) (src2[b] * ratio + src1[b] * compl_ratio + EPSILON) : src1[b]; \
|
|
}*/
|
|
|
|
/*shortened #define alphify(src2_alpha,new_alpha) \
|
|
if (src2_alpha != 0 && new_alpha != 0) \
|
|
{ \
|
|
if (src2_alpha == new_alpha){ \
|
|
for (b = 0; b < alpha; b++) \
|
|
dest [b] = affect [b] ? src2 [b] : src1 [b]; \
|
|
} else { \
|
|
ratio = (float) src2_alpha / new_alpha; \
|
|
compl_ratio = 1.0 - ratio; \
|
|
\
|
|
for (b = 0; b < alpha; b++) \
|
|
dest[b] = affect[b] ? \
|
|
(unsigned char) (src2[b] * ratio + src1[b] * compl_ratio + EPSILON) : src1[b];\
|
|
} \
|
|
}*/
|
|
|
|
#define alphify(src2_alpha,new_alpha) \
|
|
if (src2_alpha != 0 && new_alpha != 0) \
|
|
{ \
|
|
b = alpha; \
|
|
if (src2_alpha == new_alpha){ \
|
|
do { \
|
|
b--; dest [b] = affect [b] ? src2 [b] : src1 [b];} while (b); \
|
|
} else { \
|
|
ratio = (float) src2_alpha / new_alpha; \
|
|
compl_ratio = 1.0 - ratio; \
|
|
\
|
|
do { b--; \
|
|
dest[b] = affect[b] ? \
|
|
(unsigned char) (src2[b] * ratio + src1[b] * compl_ratio + EPSILON) : src1[b];\
|
|
} while (b); \
|
|
} \
|
|
}
|
|
|
|
/*special #define alphify4(src2_alpha,new_alpha) \
|
|
if (src2_alpha != 0 && new_alpha != 0) \
|
|
{ \
|
|
if (src2_alpha == new_alpha){ \
|
|
dest [0] = affect [0] ? src2 [0] : src1 [0]; \
|
|
dest [1] = affect [1] ? src2 [1] : src1 [1]; \
|
|
dest [2] = affect [2] ? src2 [2] : src1 [2]; \
|
|
} else { \
|
|
ratio = (float) src2_alpha / new_alpha; \
|
|
compl_ratio = 1.0 - ratio; \
|
|
\
|
|
dest[0] = affect[0] ? \
|
|
(unsigned char) (src2[0] * ratio + src1[0] * compl_ratio + EPSILON) : src1[0]; \
|
|
dest[1] = affect[1] ? \
|
|
(unsigned char) (src2[1] * ratio + src1[1] * compl_ratio + EPSILON) : src1[1]; \
|
|
dest[2] = affect[2] ? \
|
|
(unsigned char) (src2[2] * ratio + src1[2] * compl_ratio + EPSILON) : src1[2]; \
|
|
} \
|
|
}*/
|
|
|
|
void
|
|
combine_inten_a_and_inten_pixels (const unsigned char *src1,
|
|
const unsigned char *src2,
|
|
unsigned char *dest,
|
|
const unsigned char *mask,
|
|
int opacity,
|
|
const int *affect,
|
|
int mode_affect, /* how does the combination mode affect alpha? */
|
|
int length,
|
|
int bytes) /* 4 or 2 depending on RGBA or GRAYA */
|
|
{
|
|
int alpha, b;
|
|
int src2_bytes;
|
|
unsigned char src2_alpha;
|
|
unsigned char new_alpha;
|
|
const unsigned char * m;
|
|
float ratio, compl_ratio;
|
|
long tmp;
|
|
|
|
src2_bytes = bytes - 1;
|
|
alpha = bytes - 1;
|
|
|
|
if (mask)
|
|
{
|
|
m = mask;
|
|
if (opacity == OPAQUE_OPACITY) /* HAS MASK, FULL OPACITY */
|
|
{
|
|
while (length--)
|
|
{
|
|
src2_alpha = *m;
|
|
new_alpha = src1[alpha] +
|
|
INT_MULT((255 - src1[alpha]), src2_alpha, tmp);
|
|
alphify (src2_alpha, new_alpha);
|
|
|
|
if (mode_affect)
|
|
{
|
|
dest[alpha] = (affect[alpha]) ? new_alpha : src1[alpha];
|
|
}
|
|
else
|
|
{
|
|
dest[alpha] = (src1[alpha]) ? src1[alpha] :
|
|
(affect[alpha] ? new_alpha : src1[alpha]);
|
|
}
|
|
|
|
m++;
|
|
src1 += bytes;
|
|
src2 += src2_bytes;
|
|
dest += bytes;
|
|
}
|
|
}
|
|
else /* HAS MASK, SEMI-OPACITY */
|
|
{
|
|
while (length--)
|
|
{
|
|
src2_alpha = INT_MULT(*m, opacity, tmp);
|
|
new_alpha = src1[alpha] +
|
|
INT_MULT((255 - src1[alpha]), src2_alpha, tmp);
|
|
alphify (src2_alpha, new_alpha);
|
|
|
|
if (mode_affect)
|
|
{
|
|
dest[alpha] = (affect[alpha]) ? new_alpha : src1[alpha];
|
|
}
|
|
else
|
|
{
|
|
dest[alpha] = (src1[alpha]) ? src1[alpha] :
|
|
(affect[alpha] ? new_alpha : src1[alpha]);
|
|
}
|
|
|
|
m++;
|
|
src1 += bytes;
|
|
src2 += src2_bytes;
|
|
dest += bytes;
|
|
}
|
|
}
|
|
}
|
|
else /* NO MASK */
|
|
{
|
|
while (length --)
|
|
{
|
|
src2_alpha = opacity;
|
|
new_alpha = src1[alpha] +
|
|
INT_MULT((255 - src1[alpha]), src2_alpha, tmp);
|
|
alphify (src2_alpha, new_alpha);
|
|
|
|
if (mode_affect)
|
|
dest[alpha] = (affect[alpha]) ? new_alpha : src1[alpha];
|
|
else
|
|
dest[alpha] = (src1[alpha]) ? src1[alpha] : (affect[alpha] ? new_alpha : src1[alpha]);
|
|
|
|
src1 += bytes;
|
|
src2 += src2_bytes;
|
|
dest += bytes;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
combine_inten_a_and_inten_a_pixels (const unsigned char *src1,
|
|
const unsigned char *src2,
|
|
unsigned char *dest,
|
|
const unsigned char *mask,
|
|
int opacity,
|
|
const int *affect,
|
|
int mode_affect, /* how does the combination mode affect alpha? */
|
|
int length,
|
|
int bytes) /* 4 or 2 depending on RGBA or GRAYA */
|
|
{
|
|
int alpha, b;
|
|
unsigned char src2_alpha;
|
|
unsigned char new_alpha;
|
|
const unsigned char * m;
|
|
float ratio, compl_ratio;
|
|
long tmp;
|
|
alpha = bytes - 1;
|
|
|
|
if (mask)
|
|
{
|
|
m = mask;
|
|
|
|
if (opacity == OPAQUE_OPACITY) /* HAS MASK, FULL OPACITY */
|
|
{
|
|
const int* mask_ip;
|
|
int i,j;
|
|
|
|
if (length >= sizeof(int))
|
|
{
|
|
/* HEAD */
|
|
i = (((int)m) & (sizeof(int)-1));
|
|
if (i != 0)
|
|
{
|
|
i = sizeof(int) - i;
|
|
length -= i;
|
|
while (i--)
|
|
{
|
|
/* GUTS */
|
|
src2_alpha = INT_MULT(src2[alpha], *m, tmp);
|
|
new_alpha = src1[alpha] +
|
|
INT_MULT((255 - src1[alpha]), src2_alpha, tmp);
|
|
|
|
alphify (src2_alpha, new_alpha);
|
|
|
|
if (mode_affect)
|
|
{
|
|
dest[alpha] = (affect[alpha]) ? new_alpha : src1[alpha];
|
|
}
|
|
else
|
|
{
|
|
dest[alpha] = (src1[alpha]) ? src1[alpha] :
|
|
(affect[alpha] ? new_alpha : src1[alpha]);
|
|
}
|
|
|
|
m++;
|
|
src1 += bytes;
|
|
src2 += bytes;
|
|
dest += bytes;
|
|
/* GUTS END */
|
|
}
|
|
}
|
|
|
|
/* BODY */
|
|
mask_ip = (int*)m;
|
|
i = length / sizeof(int);
|
|
length %= sizeof(int);
|
|
while (i--)
|
|
{
|
|
if (*mask_ip)
|
|
{
|
|
m = (const unsigned char*)mask_ip;
|
|
j = sizeof(int);
|
|
while (j--)
|
|
{
|
|
/* GUTS */
|
|
src2_alpha = INT_MULT(src2[alpha], *m, tmp);
|
|
new_alpha = src1[alpha] +
|
|
INT_MULT((255 - src1[alpha]), src2_alpha, tmp);
|
|
|
|
alphify (src2_alpha, new_alpha);
|
|
|
|
if (mode_affect)
|
|
{
|
|
dest[alpha] = (affect[alpha]) ? new_alpha : src1[alpha];
|
|
}
|
|
else
|
|
{
|
|
dest[alpha] = (src1[alpha]) ? src1[alpha] :
|
|
(affect[alpha] ? new_alpha : src1[alpha]);
|
|
}
|
|
|
|
m++;
|
|
src1 += bytes;
|
|
src2 += bytes;
|
|
dest += bytes;
|
|
/* GUTS END */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
j = bytes * sizeof(int);
|
|
src2 += j;
|
|
while (j--)
|
|
{
|
|
*(dest++) = *(src1++);
|
|
}
|
|
}
|
|
mask_ip++;
|
|
}
|
|
|
|
m = (const unsigned char*)mask_ip;
|
|
}
|
|
|
|
/* TAIL */
|
|
while (length--)
|
|
{
|
|
/* GUTS */
|
|
src2_alpha = INT_MULT(src2[alpha], *m, tmp);
|
|
new_alpha = src1[alpha] +
|
|
INT_MULT((255 - src1[alpha]), src2_alpha, tmp);
|
|
|
|
alphify (src2_alpha, new_alpha);
|
|
|
|
if (mode_affect)
|
|
{
|
|
dest[alpha] = (affect[alpha]) ? new_alpha : src1[alpha];
|
|
}
|
|
else
|
|
{
|
|
dest[alpha] = (src1[alpha]) ? src1[alpha] :
|
|
(affect[alpha] ? new_alpha : src1[alpha]);
|
|
}
|
|
|
|
m++;
|
|
src1 += bytes;
|
|
src2 += bytes;
|
|
dest += bytes;
|
|
/* GUTS END */
|
|
}
|
|
}
|
|
else /* HAS MASK, SEMI-OPACITY */
|
|
{
|
|
const int* mask_ip;
|
|
int i,j;
|
|
|
|
if (length >= sizeof(int))
|
|
{
|
|
/* HEAD */
|
|
i = (((int)m) & (sizeof(int)-1));
|
|
if (i != 0)
|
|
{
|
|
i = sizeof(int) - i;
|
|
length -= i;
|
|
while (i--)
|
|
{
|
|
/* GUTS */
|
|
src2_alpha = INT_MULT3(src2[alpha], *m, opacity, tmp);
|
|
new_alpha = src1[alpha] +
|
|
INT_MULT((255 - src1[alpha]), src2_alpha, tmp);
|
|
|
|
alphify (src2_alpha, new_alpha);
|
|
|
|
if (mode_affect)
|
|
{
|
|
dest[alpha] = (affect[alpha]) ? new_alpha : src1[alpha];
|
|
}
|
|
else
|
|
{
|
|
dest[alpha] = (src1[alpha]) ? src1[alpha] :
|
|
(affect[alpha] ? new_alpha : src1[alpha]);
|
|
}
|
|
|
|
m++;
|
|
src1 += bytes;
|
|
src2 += bytes;
|
|
dest += bytes;
|
|
/* GUTS END */
|
|
}
|
|
}
|
|
|
|
/* BODY */
|
|
mask_ip = (int*)m;
|
|
i = length / sizeof(int);
|
|
length %= sizeof(int);
|
|
while (i--)
|
|
{
|
|
if (*mask_ip)
|
|
{
|
|
m = (const unsigned char*)mask_ip;
|
|
j = sizeof(int);
|
|
while (j--)
|
|
{
|
|
/* GUTS */
|
|
src2_alpha = INT_MULT3(src2[alpha], *m, opacity, tmp);
|
|
new_alpha = src1[alpha] +
|
|
INT_MULT((255 - src1[alpha]), src2_alpha, tmp);
|
|
|
|
alphify (src2_alpha, new_alpha);
|
|
|
|
if (mode_affect)
|
|
{
|
|
dest[alpha] = (affect[alpha]) ? new_alpha : src1[alpha];
|
|
}
|
|
else
|
|
{
|
|
dest[alpha] = (src1[alpha]) ? src1[alpha] :
|
|
(affect[alpha] ? new_alpha : src1[alpha]);
|
|
}
|
|
|
|
m++;
|
|
src1 += bytes;
|
|
src2 += bytes;
|
|
dest += bytes;
|
|
/* GUTS END */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
j = bytes * sizeof(int);
|
|
src2 += j;
|
|
while (j--)
|
|
{
|
|
*(dest++) = *(src1++);
|
|
}
|
|
}
|
|
mask_ip++;
|
|
}
|
|
|
|
m = (const unsigned char*)mask_ip;
|
|
}
|
|
|
|
/* TAIL */
|
|
while (length--)
|
|
{
|
|
/* GUTS */
|
|
src2_alpha = INT_MULT3(src2[alpha], *m, opacity, tmp);
|
|
new_alpha = src1[alpha] +
|
|
INT_MULT((255 - src1[alpha]), src2_alpha, tmp);
|
|
|
|
alphify (src2_alpha, new_alpha);
|
|
|
|
if (mode_affect)
|
|
{
|
|
dest[alpha] = (affect[alpha]) ? new_alpha : src1[alpha];
|
|
}
|
|
else
|
|
{
|
|
dest[alpha] = (src1[alpha]) ? src1[alpha] :
|
|
(affect[alpha] ? new_alpha : src1[alpha]);
|
|
}
|
|
|
|
m++;
|
|
src1 += bytes;
|
|
src2 += bytes;
|
|
dest += bytes;
|
|
/* GUTS END */
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (opacity == OPAQUE_OPACITY) /* NO MASK, FULL OPACITY */
|
|
{
|
|
while (length --)
|
|
{
|
|
src2_alpha = src2[alpha];
|
|
new_alpha = src1[alpha] +
|
|
INT_MULT((255 - src1[alpha]), src2_alpha, tmp);
|
|
|
|
alphify (src2_alpha, new_alpha);
|
|
|
|
if (mode_affect)
|
|
{
|
|
dest[alpha] = (affect[alpha]) ? new_alpha : src1[alpha];
|
|
}
|
|
else
|
|
{
|
|
dest[alpha] = (src1[alpha]) ? src1[alpha] :
|
|
(affect[alpha] ? new_alpha : src1[alpha]);
|
|
}
|
|
|
|
src1 += bytes;
|
|
src2 += bytes;
|
|
dest += bytes;
|
|
}
|
|
}
|
|
else /* NO MASK, SEMI OPACITY */
|
|
{
|
|
while (length --)
|
|
{
|
|
src2_alpha = INT_MULT(src2[alpha], opacity, tmp);
|
|
new_alpha = src1[alpha] +
|
|
INT_MULT((255 - src1[alpha]), src2_alpha, tmp);
|
|
|
|
alphify (src2_alpha, new_alpha);
|
|
|
|
if (mode_affect)
|
|
{
|
|
dest[alpha] = (affect[alpha]) ? new_alpha : src1[alpha];
|
|
}
|
|
else
|
|
{
|
|
dest[alpha] = (src1[alpha]) ? src1[alpha] :
|
|
(affect[alpha] ? new_alpha : src1[alpha]);
|
|
}
|
|
|
|
src1 += bytes;
|
|
src2 += bytes;
|
|
dest += bytes;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#undef alphify
|
|
|
|
void
|
|
combine_inten_a_and_channel_mask_pixels (const unsigned char *src,
|
|
const unsigned char *channel,
|
|
unsigned char *dest,
|
|
const unsigned char *col,
|
|
int opacity,
|
|
int length,
|
|
int bytes)
|
|
{
|
|
int alpha, b;
|
|
unsigned char channel_alpha;
|
|
unsigned char new_alpha;
|
|
unsigned char compl_alpha;
|
|
int t, s;
|
|
|
|
alpha = bytes - 1;
|
|
while (length --)
|
|
{
|
|
channel_alpha = INT_MULT (255 - *channel, opacity, t);
|
|
if (channel_alpha)
|
|
{
|
|
new_alpha = src[alpha] + INT_MULT ((255 - src[alpha]), channel_alpha, t);
|
|
|
|
if (new_alpha != 255)
|
|
channel_alpha = (channel_alpha * 255) / new_alpha;
|
|
compl_alpha = 255 - channel_alpha;
|
|
|
|
for (b = 0; b < alpha; b++)
|
|
dest[b] = INT_MULT (col[b], channel_alpha, t) +
|
|
INT_MULT (src[b], compl_alpha, s);
|
|
dest[b] = new_alpha;
|
|
}
|
|
else
|
|
memcpy(dest, src, bytes);
|
|
|
|
/* advance pointers */
|
|
src+=bytes;
|
|
dest+=bytes;
|
|
channel++;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
combine_inten_a_and_channel_selection_pixels (const unsigned char *src,
|
|
const unsigned char *channel,
|
|
unsigned char *dest,
|
|
const unsigned char *col,
|
|
int opacity,
|
|
int length,
|
|
int bytes)
|
|
{
|
|
int alpha, b;
|
|
unsigned char channel_alpha;
|
|
unsigned char new_alpha;
|
|
unsigned char compl_alpha;
|
|
int t, s;
|
|
|
|
alpha = bytes - 1;
|
|
while (length --)
|
|
{
|
|
channel_alpha = INT_MULT (*channel, opacity, t);
|
|
if (channel_alpha)
|
|
{
|
|
new_alpha = src[alpha] + INT_MULT ((255 - src[alpha]), channel_alpha, t);
|
|
|
|
if (new_alpha != 255)
|
|
channel_alpha = (channel_alpha * 255) / new_alpha;
|
|
compl_alpha = 255 - channel_alpha;
|
|
|
|
for (b = 0; b < alpha; b++)
|
|
dest[b] = INT_MULT (col[b], channel_alpha, t) +
|
|
INT_MULT (src[b], compl_alpha, s);
|
|
dest[b] = new_alpha;
|
|
}
|
|
else
|
|
memcpy(dest, src, bytes);
|
|
|
|
/* advance pointers */
|
|
src+=bytes;
|
|
dest+=bytes;
|
|
channel++;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
behind_inten_pixels (const unsigned char *src1,
|
|
const unsigned char *src2,
|
|
unsigned char *dest,
|
|
const unsigned char *mask,
|
|
int opacity,
|
|
const int *affect,
|
|
int length,
|
|
int bytes1,
|
|
int bytes2,
|
|
int has_alpha1,
|
|
int has_alpha2)
|
|
{
|
|
int alpha, b;
|
|
unsigned char src1_alpha;
|
|
unsigned char src2_alpha;
|
|
unsigned char new_alpha;
|
|
const unsigned char * m;
|
|
float ratio, compl_ratio;
|
|
long tmp;
|
|
if (mask)
|
|
m = mask;
|
|
else
|
|
m = &no_mask;
|
|
|
|
/* the alpha channel */
|
|
alpha = bytes1 - 1;
|
|
|
|
while (length --)
|
|
{
|
|
src1_alpha = src1[alpha];
|
|
src2_alpha = INT_MULT3(src2[alpha], *m, opacity, tmp);
|
|
new_alpha = src2_alpha +
|
|
INT_MULT((255 - src2_alpha), src1_alpha, tmp);
|
|
if (new_alpha)
|
|
ratio = (float) src1_alpha / new_alpha;
|
|
else
|
|
ratio = 0.0;
|
|
compl_ratio = 1.0 - ratio;
|
|
|
|
for (b = 0; b < alpha; b++)
|
|
dest[b] = (affect[b]) ?
|
|
(unsigned char) (src1[b] * ratio + src2[b] * compl_ratio + EPSILON) :
|
|
src1[b];
|
|
|
|
dest[alpha] = (affect[alpha]) ? new_alpha : src1[alpha];
|
|
|
|
if (mask)
|
|
m++;
|
|
|
|
src1 += bytes1;
|
|
src2 += bytes2;
|
|
dest += bytes1;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
behind_indexed_pixels (const unsigned char *src1,
|
|
const unsigned char *src2,
|
|
unsigned char *dest,
|
|
const unsigned char *mask,
|
|
int opacity,
|
|
const int *affect,
|
|
int length,
|
|
int bytes1,
|
|
int bytes2,
|
|
int has_alpha1,
|
|
int has_alpha2)
|
|
{
|
|
int alpha, b;
|
|
unsigned char src1_alpha;
|
|
unsigned char src2_alpha;
|
|
unsigned char new_alpha;
|
|
const unsigned char * m;
|
|
long tmp;
|
|
|
|
if (mask)
|
|
m = mask;
|
|
else
|
|
m = &no_mask;
|
|
|
|
/* the alpha channel */
|
|
alpha = bytes1 - 1;
|
|
|
|
while (length --)
|
|
{
|
|
src1_alpha = src1[alpha];
|
|
src2_alpha = INT_MULT3(src2[alpha], *m, opacity, tmp);
|
|
new_alpha = (src2_alpha > 127) ? OPAQUE_OPACITY : TRANSPARENT_OPACITY;
|
|
|
|
for (b = 0; b < bytes1; b++)
|
|
dest[b] = (affect[b] && new_alpha == OPAQUE_OPACITY && (src1_alpha > 127)) ?
|
|
src2[b] : src1[b];
|
|
|
|
if (mask)
|
|
m++;
|
|
|
|
src1 += bytes1;
|
|
src2 += bytes2;
|
|
dest += bytes1;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
replace_inten_pixels (const unsigned char *src1,
|
|
const unsigned char *src2,
|
|
unsigned char *dest,
|
|
const unsigned char *mask,
|
|
int opacity,
|
|
const int *affect,
|
|
int length,
|
|
int bytes1,
|
|
int bytes2,
|
|
int has_alpha1,
|
|
int has_alpha2)
|
|
{
|
|
int bytes, b;
|
|
unsigned char mask_alpha;
|
|
const unsigned char * m;
|
|
int tmp;
|
|
if (mask)
|
|
m = mask;
|
|
else
|
|
m = &no_mask;
|
|
|
|
bytes = MINIMUM (bytes1, bytes2);
|
|
while (length --)
|
|
{
|
|
mask_alpha = INT_MULT(*m, opacity, tmp);
|
|
|
|
for (b = 0; b < bytes; b++)
|
|
dest[b] = (affect[b]) ?
|
|
INT_BLEND(src2[b], src1[b], mask_alpha, tmp) :
|
|
src1[b];
|
|
|
|
if (has_alpha1 && !has_alpha2)
|
|
dest[b] = src1[b];
|
|
|
|
if (mask)
|
|
m++;
|
|
|
|
src1 += bytes1;
|
|
src2 += bytes2;
|
|
dest += bytes1;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
replace_indexed_pixels (const unsigned char *src1,
|
|
const unsigned char *src2,
|
|
unsigned char *dest,
|
|
const unsigned char *mask,
|
|
int opacity,
|
|
const int *affect,
|
|
int length,
|
|
int bytes1,
|
|
int bytes2,
|
|
int has_alpha1,
|
|
int has_alpha2)
|
|
{
|
|
int bytes, b;
|
|
unsigned char mask_alpha;
|
|
const unsigned char * m;
|
|
int tmp;
|
|
if (mask)
|
|
m = mask;
|
|
else
|
|
m = &no_mask;
|
|
|
|
bytes = MINIMUM (bytes1, bytes2);
|
|
while (length --)
|
|
{
|
|
mask_alpha = INT_MULT(*m, opacity, tmp);
|
|
|
|
for (b = 0; b < bytes; b++)
|
|
dest[b] = (affect[b] && mask_alpha) ?
|
|
src2[b] : src1[b];
|
|
|
|
if (has_alpha1 && !has_alpha2)
|
|
dest[b] = src1[b];
|
|
|
|
if (mask)
|
|
m++;
|
|
|
|
src1 += bytes1;
|
|
src2 += bytes2;
|
|
dest += bytes1;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
erase_inten_pixels (const unsigned char *src1,
|
|
const unsigned char *src2,
|
|
unsigned char *dest,
|
|
const unsigned char *mask,
|
|
int opacity,
|
|
const int *affect,
|
|
int length,
|
|
int bytes)
|
|
{
|
|
int alpha, b;
|
|
unsigned char src2_alpha;
|
|
const unsigned char * m;
|
|
long tmp;
|
|
|
|
if (mask)
|
|
m = mask;
|
|
else
|
|
m = &no_mask;
|
|
|
|
alpha = bytes - 1;
|
|
while (length --)
|
|
{
|
|
for (b = 0; b < alpha; b++)
|
|
dest[b] = src1[b];
|
|
|
|
src2_alpha = INT_MULT3(src2[alpha], *m, opacity, tmp);
|
|
dest[alpha] = src1[alpha] - INT_MULT(src1[alpha], src2_alpha, tmp);
|
|
|
|
if (mask)
|
|
m++;
|
|
|
|
src1 += bytes;
|
|
src2 += bytes;
|
|
dest += bytes;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
erase_indexed_pixels (const unsigned char *src1,
|
|
const unsigned char *src2,
|
|
unsigned char *dest,
|
|
const unsigned char *mask,
|
|
int opacity,
|
|
const int *affect,
|
|
int length,
|
|
int bytes)
|
|
{
|
|
int alpha, b;
|
|
unsigned char src2_alpha;
|
|
const unsigned char * m;
|
|
long tmp;
|
|
if (mask)
|
|
m = mask;
|
|
else
|
|
m = &no_mask;
|
|
|
|
alpha = bytes - 1;
|
|
while (length --)
|
|
{
|
|
for (b = 0; b < alpha; b++)
|
|
dest[b] = src1[b];
|
|
|
|
src2_alpha = INT_MULT3(src2[alpha], *m, opacity, tmp);
|
|
dest[alpha] = (src2_alpha > 127) ? TRANSPARENT_OPACITY : src1[alpha];
|
|
|
|
if (mask)
|
|
m++;
|
|
|
|
src1 += bytes;
|
|
src2 += bytes;
|
|
dest += bytes;
|
|
}
|
|
}
|
|
|
|
void
|
|
anti_erase_inten_pixels (const unsigned char *src1,
|
|
const unsigned char *src2,
|
|
unsigned char *dest,
|
|
const unsigned char *mask,
|
|
int opacity,
|
|
const int *affect,
|
|
int length,
|
|
int bytes)
|
|
{
|
|
int alpha, b;
|
|
unsigned char src2_alpha;
|
|
const unsigned char * m;
|
|
long tmp;
|
|
|
|
if (mask)
|
|
m = mask;
|
|
else
|
|
m = &no_mask;
|
|
|
|
alpha = bytes - 1;
|
|
while (length --)
|
|
{
|
|
for (b = 0; b < alpha; b++)
|
|
dest[b] = src1[b];
|
|
|
|
src2_alpha = INT_MULT3(src2[alpha], *m, opacity, tmp);
|
|
dest[alpha] = src1[alpha] + INT_MULT((255 - src1[alpha]), src2_alpha, tmp);
|
|
|
|
if (mask)
|
|
m++;
|
|
|
|
src1 += bytes;
|
|
src2 += bytes;
|
|
dest += bytes;
|
|
}
|
|
}
|
|
|
|
void
|
|
anti_erase_indexed_pixels (const unsigned char *src1,
|
|
const unsigned char *src2,
|
|
unsigned char *dest,
|
|
const unsigned char *mask,
|
|
int opacity,
|
|
const int *affect,
|
|
int length,
|
|
int bytes)
|
|
{
|
|
int alpha, b;
|
|
unsigned char src2_alpha;
|
|
const unsigned char * m;
|
|
long tmp;
|
|
if (mask)
|
|
m = mask;
|
|
else
|
|
m = &no_mask;
|
|
|
|
alpha = bytes - 1;
|
|
while (length --)
|
|
{
|
|
for (b = 0; b < alpha; b++)
|
|
dest[b] = src1[b];
|
|
|
|
src2_alpha = INT_MULT3(src2[alpha], *m, opacity, tmp);
|
|
dest[alpha] = (src2_alpha > 127) ? OPAQUE_OPACITY : src1[alpha];
|
|
|
|
if (mask)
|
|
m++;
|
|
|
|
src1 += bytes;
|
|
src2 += bytes;
|
|
dest += bytes;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
extract_from_inten_pixels (unsigned char *src,
|
|
unsigned char *dest,
|
|
const unsigned char *mask,
|
|
const unsigned char *bg,
|
|
int cut,
|
|
int length,
|
|
int bytes,
|
|
int has_alpha)
|
|
{
|
|
int b, alpha;
|
|
int dest_bytes;
|
|
const unsigned char * m;
|
|
int tmp;
|
|
if (mask)
|
|
m = mask;
|
|
else
|
|
m = &no_mask;
|
|
|
|
alpha = (has_alpha) ? bytes - 1 : bytes;
|
|
dest_bytes = (has_alpha) ? bytes : bytes + 1;
|
|
while (length --)
|
|
{
|
|
for (b = 0; b < alpha; b++)
|
|
dest[b] = src[b];
|
|
|
|
if (has_alpha)
|
|
{
|
|
dest[alpha] = INT_MULT(*m, src[alpha], tmp);
|
|
if (cut)
|
|
src[alpha] = INT_MULT((255 - *m), src[alpha], tmp);
|
|
}
|
|
else
|
|
{
|
|
dest[alpha] = *m;
|
|
if (cut)
|
|
for (b = 0; b < bytes; b++)
|
|
src[b] = INT_BLEND(bg[b], src[b], *m, tmp);
|
|
}
|
|
|
|
if (mask)
|
|
m++;
|
|
|
|
src += bytes;
|
|
dest += dest_bytes;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
extract_from_indexed_pixels (unsigned char *src,
|
|
unsigned char *dest,
|
|
const unsigned char *mask,
|
|
const unsigned char *cmap,
|
|
const unsigned char *bg,
|
|
int cut,
|
|
int length,
|
|
int bytes,
|
|
int has_alpha)
|
|
{
|
|
int b;
|
|
int index;
|
|
const unsigned char * m;
|
|
int t;
|
|
|
|
if (mask)
|
|
m = mask;
|
|
else
|
|
m = &no_mask;
|
|
|
|
while (length --)
|
|
{
|
|
index = src[0] * 3;
|
|
for (b = 0; b < 3; b++)
|
|
dest[b] = cmap[index + b];
|
|
|
|
if (has_alpha)
|
|
{
|
|
dest[3] = INT_MULT (*m, src[1], t);
|
|
if (cut)
|
|
src[1] = INT_MULT ((255 - *m), src[1], t);
|
|
}
|
|
else
|
|
{
|
|
dest[3] = *m;
|
|
if (cut)
|
|
src[0] = (*m > 127) ? bg[0] : src[0];
|
|
}
|
|
|
|
if (mask)
|
|
m++;
|
|
|
|
src += bytes;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
map_to_color (int src_type,
|
|
const unsigned char *cmap,
|
|
const unsigned char *src,
|
|
unsigned char *rgb)
|
|
{
|
|
switch (src_type)
|
|
{
|
|
case 0: /* RGB */
|
|
/* Straight copy */
|
|
*rgb++ = *src++;
|
|
*rgb++ = *src++;
|
|
*rgb = *src;
|
|
break;
|
|
case 1: /* GRAY */
|
|
*rgb++ = *src;
|
|
*rgb++ = *src;
|
|
*rgb = *src;
|
|
break;
|
|
case 2: /* INDEXED */
|
|
{
|
|
int index = *src * 3;
|
|
*rgb++ = cmap [index++];
|
|
*rgb++ = cmap [index++];
|
|
*rgb = cmap [index++];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
map_rgb_to_indexed (const unsigned char *cmap,
|
|
int num_cols,
|
|
GimpImage* gimage,
|
|
int r,
|
|
int g,
|
|
int b)
|
|
{
|
|
unsigned int pixel;
|
|
int hash_index;
|
|
int cmap_index;
|
|
|
|
pixel = (r << 16) | (g << 8) | b;
|
|
hash_index = pixel % HASH_TABLE_SIZE;
|
|
|
|
/* Hash table lookup hit */
|
|
if (color_hash_table[hash_index].gimage == gimage &&
|
|
color_hash_table[hash_index].pixel == pixel)
|
|
{
|
|
cmap_index = color_hash_table[hash_index].index;
|
|
color_hash_hits++;
|
|
}
|
|
/* Hash table lookup miss */
|
|
else
|
|
{
|
|
const unsigned char *col;
|
|
int diff, sum, max;
|
|
int i;
|
|
|
|
max = MAXDIFF;
|
|
cmap_index = 0;
|
|
|
|
col = cmap;
|
|
for (i = 0; i < num_cols; i++)
|
|
{
|
|
diff = r - *col++;
|
|
sum = diff * diff;
|
|
diff = g - *col++;
|
|
sum += diff * diff;
|
|
diff = b - *col++;
|
|
sum += diff * diff;
|
|
|
|
if (sum < max)
|
|
{
|
|
cmap_index = i;
|
|
max = sum;
|
|
}
|
|
}
|
|
|
|
/* update the hash table */
|
|
color_hash_table[hash_index].pixel = pixel;
|
|
color_hash_table[hash_index].index = cmap_index;
|
|
color_hash_table[hash_index].gimage = gimage;
|
|
color_hash_misses++;
|
|
}
|
|
|
|
return cmap_index;
|
|
}
|
|
|
|
|
|
|
|
/**************************************************/
|
|
/* REGION FUNCTIONS */
|
|
/**************************************************/
|
|
|
|
void
|
|
color_region (PixelRegion *dest,
|
|
const unsigned char *col)
|
|
{
|
|
int h;
|
|
unsigned char * s;
|
|
void * pr;
|
|
|
|
for (pr = pixel_regions_register (1, dest); pr != NULL; pr = pixel_regions_process (pr))
|
|
{
|
|
h = dest->h;
|
|
s = dest->data;
|
|
|
|
if (dest->w*dest->bytes == dest->rowstride)
|
|
{
|
|
/* do it all in one function call if we can */
|
|
/* this hasn't been tested to see if it is a
|
|
signifigant speed gain yet */
|
|
color_pixels (s, col, dest->w*h, dest->bytes);
|
|
}
|
|
else
|
|
{
|
|
while (h--)
|
|
{
|
|
color_pixels (s, col, dest->w, dest->bytes);
|
|
s += dest->rowstride;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
blend_region (PixelRegion *src1,
|
|
PixelRegion *src2,
|
|
PixelRegion *dest,
|
|
int blend)
|
|
{
|
|
int h;
|
|
unsigned char *s1, *s2, * d;
|
|
void * pr;
|
|
|
|
for (pr = pixel_regions_register (3, src1, src2, dest); pr != NULL; pr = pixel_regions_process (pr))
|
|
{
|
|
s1 = src1->data;
|
|
s2 = src2->data;
|
|
d = dest->data;
|
|
h = src1->h;
|
|
|
|
while (h --)
|
|
{
|
|
blend_pixels (s1, s2, d, blend, src1->w, src1->bytes, FALSE);
|
|
s1 += src1->rowstride;
|
|
s2 += src2->rowstride;
|
|
d += dest->rowstride;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
shade_region (PixelRegion *src,
|
|
PixelRegion *dest,
|
|
unsigned char *col,
|
|
int blend)
|
|
{
|
|
int h;
|
|
unsigned char * s, * d;
|
|
|
|
s = src->data;
|
|
d = dest->data;
|
|
h = src->h;
|
|
|
|
while (h --)
|
|
{
|
|
/* blend_pixels (s, d, col, blend, src->w, src->bytes);*/
|
|
s += src->rowstride;
|
|
d += dest->rowstride;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void
|
|
copy_region (PixelRegion *src,
|
|
PixelRegion *dest)
|
|
{
|
|
int h;
|
|
int pixelwidth;
|
|
unsigned char * s, * d;
|
|
void * pr;
|
|
|
|
#ifdef COWSHOW
|
|
fputc('[',stderr);
|
|
#endif
|
|
for (pr = pixel_regions_register (2, src, dest);
|
|
pr != NULL;
|
|
pr = pixel_regions_process (pr))
|
|
{
|
|
if (src->tiles && dest->tiles &&
|
|
src->curtile && dest->curtile &&
|
|
src->offx == 0 && dest->offx == 0 &&
|
|
src->offy == 0 && dest->offy == 0 &&
|
|
src->w == tile_ewidth(src->curtile) &&
|
|
dest->w == tile_ewidth(dest->curtile) &&
|
|
src->h == tile_eheight(src->curtile) &&
|
|
dest->h == tile_eheight(dest->curtile))
|
|
{
|
|
#ifdef COWSHOW
|
|
fputc('!',stderr);
|
|
#endif
|
|
tile_manager_map_over_tile (dest->tiles, dest->curtile, src->curtile);
|
|
}
|
|
else
|
|
{
|
|
#ifdef COWSHOW
|
|
fputc('.',stderr);
|
|
#endif
|
|
pixelwidth = src->w * src->bytes;
|
|
s = src->data;
|
|
d = dest->data;
|
|
h = src->h;
|
|
|
|
while (h --)
|
|
{
|
|
memcpy (d, s, pixelwidth);
|
|
s += src->rowstride;
|
|
d += dest->rowstride;
|
|
}
|
|
}
|
|
}
|
|
#ifdef COWSHOW
|
|
fputc(']',stderr);
|
|
fputc('\n',stderr);
|
|
#endif
|
|
}
|
|
|
|
|
|
void
|
|
add_alpha_region (PixelRegion *src,
|
|
PixelRegion *dest)
|
|
{
|
|
int h;
|
|
unsigned char * s, * d;
|
|
void * pr;
|
|
|
|
for (pr = pixel_regions_register (2, src, dest); pr != NULL; pr = pixel_regions_process (pr))
|
|
{
|
|
s = src->data;
|
|
d = dest->data;
|
|
h = src->h;
|
|
|
|
while (h --)
|
|
{
|
|
add_alpha_pixels (s, d, src->w, src->bytes);
|
|
s += src->rowstride;
|
|
d += dest->rowstride;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
flatten_region (PixelRegion *src,
|
|
PixelRegion *dest,
|
|
unsigned char *bg)
|
|
{
|
|
int h;
|
|
unsigned char * s, * d;
|
|
|
|
s = src->data;
|
|
d = dest->data;
|
|
h = src->h;
|
|
|
|
while (h --)
|
|
{
|
|
flatten_pixels (s, d, bg, src->w, src->bytes);
|
|
s += src->rowstride;
|
|
d += dest->rowstride;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
extract_alpha_region (PixelRegion *src,
|
|
PixelRegion *mask,
|
|
PixelRegion *dest)
|
|
{
|
|
int h;
|
|
unsigned char * s, * m, * d;
|
|
void * pr;
|
|
|
|
for (pr = pixel_regions_register (3, src, mask, dest); pr != NULL; pr = pixel_regions_process (pr))
|
|
{
|
|
s = src->data;
|
|
d = dest->data;
|
|
if (mask)
|
|
m = mask->data;
|
|
else
|
|
m = NULL;
|
|
|
|
h = src->h;
|
|
while (h --)
|
|
{
|
|
extract_alpha_pixels (s, m, d, src->w, src->bytes);
|
|
s += src->rowstride;
|
|
d += dest->rowstride;
|
|
if (mask)
|
|
m += mask->rowstride;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
extract_from_region (PixelRegion *src,
|
|
PixelRegion *dest,
|
|
PixelRegion *mask,
|
|
unsigned char *cmap,
|
|
unsigned char *bg,
|
|
int type,
|
|
int has_alpha,
|
|
int cut)
|
|
{
|
|
int h;
|
|
unsigned char * s, * d, * m;
|
|
void * pr;
|
|
|
|
for (pr = pixel_regions_register (3, src, dest, mask); pr != NULL; pr = pixel_regions_process (pr))
|
|
{
|
|
s = src->data;
|
|
d = dest->data;
|
|
m = (mask) ? mask->data : NULL;
|
|
h = src->h;
|
|
|
|
while (h --)
|
|
{
|
|
switch (type)
|
|
{
|
|
case 0: /* RGB */
|
|
case 1: /* GRAY */
|
|
extract_from_inten_pixels (s, d, m, bg, cut, src->w,
|
|
src->bytes, has_alpha);
|
|
break;
|
|
case 2: /* INDEXED */
|
|
extract_from_indexed_pixels (s, d, m, cmap, bg, cut, src->w,
|
|
src->bytes, has_alpha);
|
|
break;
|
|
}
|
|
|
|
s += src->rowstride;
|
|
d += dest->rowstride;
|
|
if (mask)
|
|
m += mask->rowstride;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
convolve_region (PixelRegion *srcR,
|
|
PixelRegion *destR,
|
|
int *matrix,
|
|
int size,
|
|
int divisor,
|
|
ConvolutionType mode)
|
|
{
|
|
/* Convolve the src image using the convolution matrix, writing to dest */
|
|
/* Convolve is not tile-enabled--use accordingly */
|
|
unsigned char *src, *s_row, * s;
|
|
unsigned char *dest, * d;
|
|
int * m;
|
|
int total [4];
|
|
int b, bytes;
|
|
int length;
|
|
int wraparound;
|
|
int margin; /* margin imposed by size of conv. matrix */
|
|
int i, j;
|
|
int x, y;
|
|
int offset;
|
|
|
|
/* If the mode is NEGATIVE_CONVOL, the offset should be 128 */
|
|
if (mode == NEGATIVE_CONVOL)
|
|
{
|
|
offset = 128;
|
|
mode = NORMAL_CONVOL;
|
|
}
|
|
else
|
|
offset = 0;
|
|
|
|
/* check for the boundary cases */
|
|
if (srcR->w < (size - 1) || srcR->h < (size - 1))
|
|
return;
|
|
|
|
/* Initialize some values */
|
|
bytes = srcR->bytes;
|
|
length = bytes * srcR->w;
|
|
margin = size / 2;
|
|
src = srcR->data;
|
|
dest = destR->data;
|
|
|
|
/* calculate the source wraparound value */
|
|
wraparound = srcR->rowstride - size * bytes;
|
|
|
|
/* copy the first (size / 2) scanlines of the src image... */
|
|
for (i = 0; i < margin; i++)
|
|
{
|
|
memcpy (dest, src, length);
|
|
src += srcR->rowstride;
|
|
dest += destR->rowstride;
|
|
}
|
|
|
|
src = srcR->data;
|
|
|
|
for (y = margin; y < srcR->h - margin; y++)
|
|
{
|
|
s_row = src;
|
|
s = s_row + srcR->rowstride*margin;
|
|
d = dest;
|
|
|
|
/* handle the first margin pixels... */
|
|
b = bytes * margin;
|
|
while (b --)
|
|
*d++ = *s++;
|
|
|
|
/* now, handle the center pixels */
|
|
x = srcR->w - margin*2;
|
|
while (x--)
|
|
{
|
|
s = s_row;
|
|
|
|
m = matrix;
|
|
total [0] = total [1] = total [2] = total [3] = 0;
|
|
i = size;
|
|
while (i --)
|
|
{
|
|
j = size;
|
|
while (j --)
|
|
{
|
|
for (b = 0; b < bytes; b++)
|
|
total [b] += *m * *s++;
|
|
m ++;
|
|
}
|
|
|
|
s += wraparound;
|
|
}
|
|
|
|
for (b = 0; b < bytes; b++)
|
|
{
|
|
total [b] = total [b] / divisor + offset;
|
|
|
|
if (total [b] < 0 && mode != NORMAL_CONVOL)
|
|
total [b] = - total [b];
|
|
|
|
if (total [b] < 0)
|
|
*d++ = 0;
|
|
else
|
|
*d++ = (total [b] > 255) ? 255 : (unsigned char) total [b];
|
|
}
|
|
|
|
s_row += bytes;
|
|
|
|
}
|
|
|
|
/* handle the last pixel... */
|
|
s = s_row + (srcR->rowstride + bytes) * margin;
|
|
b = bytes * margin;
|
|
while (b --)
|
|
*d++ = *s++;
|
|
|
|
/* set the memory pointers */
|
|
src += srcR->rowstride;
|
|
dest += destR->rowstride;
|
|
}
|
|
|
|
src += srcR->rowstride*margin;
|
|
|
|
/* copy the last (margin) scanlines of the src image... */
|
|
for (i = 0; i < margin; i++)
|
|
{
|
|
memcpy (dest, src, length);
|
|
src += srcR->rowstride;
|
|
dest += destR->rowstride;
|
|
}
|
|
}
|
|
|
|
/* Convert from separated alpha to premultiplied alpha. Only works on
|
|
non-tiled regions! */
|
|
void
|
|
multiply_alpha_region (PixelRegion *srcR)
|
|
{
|
|
unsigned char *src, *s;
|
|
int x, y;
|
|
int width, height;
|
|
int b, bytes;
|
|
double alpha_val;
|
|
|
|
width = srcR->w;
|
|
height = srcR->h;
|
|
bytes = srcR->bytes;
|
|
|
|
src = srcR->data;
|
|
|
|
for (y = 0; y < height; y++)
|
|
{
|
|
s = src;
|
|
for (x = 0; x < width; x++)
|
|
{
|
|
alpha_val = s[bytes - 1] * (1.0 / 255.0);
|
|
for (b = 0; b < bytes - 1; b++)
|
|
s[b] = 0.5 + s[b] * alpha_val;
|
|
s += bytes;
|
|
}
|
|
src += srcR->rowstride;
|
|
}
|
|
}
|
|
|
|
/* Convert from premultiplied alpha to separated alpha. Only works on
|
|
non-tiled regions! */
|
|
void
|
|
separate_alpha_region (PixelRegion *srcR)
|
|
{
|
|
unsigned char *src, *s;
|
|
int x, y;
|
|
int width, height;
|
|
int b, bytes;
|
|
double alpha_recip;
|
|
int new_val;
|
|
|
|
width = srcR->w;
|
|
height = srcR->h;
|
|
bytes = srcR->bytes;
|
|
|
|
src = srcR->data;
|
|
|
|
for (y = 0; y < height; y++)
|
|
{
|
|
s = src;
|
|
for (x = 0; x < width; x++)
|
|
{
|
|
/* predicate is equivalent to:
|
|
(((s[bytes - 1] - 1) & 255) + 2) & 256
|
|
*/
|
|
if (s[bytes - 1] != 0 && s[bytes - 1] != 255)
|
|
{
|
|
alpha_recip = 255.0 / s[bytes - 1];
|
|
for (b = 0; b < bytes - 1; b++)
|
|
{
|
|
new_val = 0.5 + s[b] * alpha_recip;
|
|
new_val = MIN (new_val, 255);
|
|
s[b] = new_val;
|
|
}
|
|
}
|
|
s += bytes;
|
|
}
|
|
src += srcR->rowstride;
|
|
}
|
|
}
|
|
|
|
void
|
|
gaussian_blur_region (PixelRegion *srcR,
|
|
double radius_x,
|
|
double radius_y)
|
|
{
|
|
double std_dev;
|
|
long width, height;
|
|
int bytes;
|
|
unsigned char *src, *sp;
|
|
unsigned char *dest, *dp;
|
|
unsigned char *data;
|
|
int *buf, *b;
|
|
int pixels;
|
|
int total;
|
|
int i, row, col;
|
|
int start, end;
|
|
int *curve;
|
|
int *sum;
|
|
int val;
|
|
int length;
|
|
int alpha;
|
|
int initial_p, initial_m;
|
|
|
|
if (radius_x == 0.0 && radius_y == 0.0) return; /* zero blur is a no-op */
|
|
|
|
/* allocate the result buffer */
|
|
length = MAXIMUM (srcR->w, srcR->h) * srcR->bytes;
|
|
data = paint_funcs_get_buffer (length * 2);
|
|
src = data;
|
|
dest = data + length;
|
|
|
|
width = srcR->w;
|
|
height = srcR->h;
|
|
bytes = srcR->bytes;
|
|
alpha = bytes - 1;
|
|
|
|
buf = g_malloc (sizeof (int) * MAXIMUM (width, height) * 2);
|
|
|
|
if (radius_y != 0.0)
|
|
{
|
|
std_dev = sqrt (-(radius_y * radius_y) / (2 * log (1.0 / 255.0)));
|
|
curve = make_curve (std_dev, &length);
|
|
sum = g_malloc (sizeof (int) * (2 * length + 1));
|
|
sum[0] = 0;
|
|
|
|
for (i = 1; i <= length*2; i++)
|
|
sum[i] = curve[i-length-1] + sum[i-1];
|
|
sum += length;
|
|
|
|
total = sum[length] - sum[-length];
|
|
|
|
for (col = 0; col < width; col++)
|
|
{
|
|
pixel_region_get_col (srcR, col + srcR->x, srcR->y, height, src, 1);
|
|
sp = src + alpha;
|
|
|
|
initial_p = sp[0];
|
|
initial_m = sp[(height-1) * bytes];
|
|
|
|
/* Determine a run-length encoded version of the column */
|
|
run_length_encode (sp, buf, height, bytes);
|
|
|
|
for (row = 0; row < height; row++)
|
|
{
|
|
start = (row < length) ? -row : -length;
|
|
end = (height <= (row + length)) ? (height - row - 1) : length;
|
|
|
|
val = 0;
|
|
i = start;
|
|
b = buf + (row + i) * 2;
|
|
|
|
if (start != -length)
|
|
val += initial_p * (sum[start] - sum[-length]);
|
|
|
|
while (i < end)
|
|
{
|
|
pixels = b[0];
|
|
i += pixels;
|
|
if (i > end)
|
|
i = end;
|
|
val += b[1] * (sum[i] - sum[start]);
|
|
b += (pixels * 2);
|
|
start = i;
|
|
}
|
|
|
|
if (end != length)
|
|
val += initial_m * (sum[length] - sum[end]);
|
|
|
|
sp[row * bytes] = val / total;
|
|
}
|
|
|
|
pixel_region_set_col (srcR, col + srcR->x, srcR->y, height, src);
|
|
}
|
|
|
|
g_free (sum - length);
|
|
g_free (curve - length);
|
|
}
|
|
|
|
if (radius_x != 0.0)
|
|
{
|
|
std_dev = sqrt (-(radius_x * radius_x) / (2 * log (1.0 / 255.0)));
|
|
curve = make_curve (std_dev, &length);
|
|
sum = g_malloc (sizeof (int) * (2 * length + 1));
|
|
sum[0] = 0;
|
|
|
|
for (i = 1; i <= length*2; i++)
|
|
sum[i] = curve[i-length-1] + sum[i-1];
|
|
sum += length;
|
|
|
|
total = sum[length] - sum[-length];
|
|
|
|
for (row = 0; row < height; row++)
|
|
{
|
|
pixel_region_get_row (srcR, srcR->x, row + srcR->y, width, src, 1);
|
|
sp = src + alpha;
|
|
dp = dest + alpha;
|
|
|
|
initial_p = sp[0];
|
|
initial_m = sp[(width-1) * bytes];
|
|
|
|
/* Determine a run-length encoded version of the row */
|
|
run_length_encode (sp, buf, width, bytes);
|
|
|
|
for (col = 0; col < width; col++)
|
|
{
|
|
start = (col < length) ? -col : -length;
|
|
end = (width <= (col + length)) ? (width - col - 1) : length;
|
|
|
|
val = 0;
|
|
i = start;
|
|
b = buf + (col + i) * 2;
|
|
|
|
if (start != -length)
|
|
val += initial_p * (sum[start] - sum[-length]);
|
|
|
|
while (i < end)
|
|
{
|
|
pixels = b[0];
|
|
i += pixels;
|
|
if (i > end)
|
|
i = end;
|
|
val += b[1] * (sum[i] - sum[start]);
|
|
b += (pixels * 2);
|
|
start = i;
|
|
}
|
|
|
|
if (end != length)
|
|
val += initial_m * (sum[length] - sum[end]);
|
|
|
|
val = val / total;
|
|
|
|
dp[col * bytes] = val;
|
|
}
|
|
|
|
pixel_region_set_row (srcR, srcR->x, row + srcR->y, width, dest);
|
|
}
|
|
|
|
g_free (sum - length);
|
|
g_free (curve - length);
|
|
}
|
|
|
|
g_free (buf);
|
|
}
|
|
|
|
|
|
/* non-interpolating scale_region. [adam]
|
|
*/
|
|
void
|
|
scale_region_no_resample (PixelRegion *srcPR,
|
|
PixelRegion *destPR)
|
|
{
|
|
int * x_src_offsets;
|
|
int * y_src_offsets;
|
|
unsigned char * src;
|
|
unsigned char * dest;
|
|
int width, height, orig_width, orig_height;
|
|
int last_src_y;
|
|
int row_bytes;
|
|
int x,y,b;
|
|
char bytes;
|
|
|
|
orig_width = srcPR->w;
|
|
orig_height = srcPR->h;
|
|
|
|
width = destPR->w;
|
|
height = destPR->h;
|
|
|
|
bytes = srcPR->bytes;
|
|
|
|
/* the data pointers... */
|
|
x_src_offsets = (int *) g_malloc (width * bytes * sizeof(int));
|
|
y_src_offsets = (int *) g_malloc (height * sizeof(int));
|
|
src = (unsigned char *) g_malloc (orig_width * bytes);
|
|
dest = (unsigned char *) g_malloc (width * bytes);
|
|
|
|
/* pre-calc the scale tables */
|
|
for (b = 0; b < bytes; b++)
|
|
{
|
|
for (x = 0; x < width; x++)
|
|
{
|
|
x_src_offsets [b + x * bytes] = b + bytes * ((x * orig_width + orig_width / 2) / width);
|
|
}
|
|
}
|
|
for (y = 0; y < height; y++)
|
|
{
|
|
y_src_offsets [y] = (y * orig_height + orig_height / 2) / height;
|
|
}
|
|
|
|
/* do the scaling */
|
|
row_bytes = width * bytes;
|
|
last_src_y = -1;
|
|
for (y = 0; y < height; y++)
|
|
{
|
|
/* if the source of this line was the same as the source
|
|
* of the last line, there's no point in re-rescaling.
|
|
*/
|
|
if (y_src_offsets[y] != last_src_y)
|
|
{
|
|
pixel_region_get_row (srcPR, 0, y_src_offsets[y], orig_width, src, 1);
|
|
for (x = 0; x < row_bytes ; x++)
|
|
{
|
|
dest[x] = src[x_src_offsets[x]];
|
|
}
|
|
last_src_y = y_src_offsets[y];
|
|
}
|
|
|
|
pixel_region_set_row (destPR, 0, y, width, dest);
|
|
}
|
|
|
|
g_free (x_src_offsets);
|
|
g_free (y_src_offsets);
|
|
g_free (src);
|
|
g_free (dest);
|
|
}
|
|
|
|
|
|
void
|
|
scale_region (PixelRegion *srcPR,
|
|
PixelRegion *destPR)
|
|
{
|
|
unsigned char * src_m1, * src, * src_p1, * src_p2;
|
|
unsigned char * s_m1, * s, * s_p1, * s_p2;
|
|
unsigned char * dest, * d;
|
|
double * row, * r;
|
|
int src_row, src_col;
|
|
int bytes, b;
|
|
int width, height;
|
|
int orig_width, orig_height;
|
|
double x_rat, y_rat;
|
|
double x_cum, y_cum;
|
|
double x_last, y_last;
|
|
double * x_frac, y_frac, tot_frac;
|
|
float dx, dy;
|
|
int i, j;
|
|
int frac;
|
|
int advance_dest_x, advance_dest_y;
|
|
int minus_x, plus_x, plus2_x;
|
|
ScaleType scale_type;
|
|
|
|
orig_width = srcPR->w;
|
|
orig_height = srcPR->h;
|
|
|
|
width = destPR->w;
|
|
height = destPR->h;
|
|
|
|
/* Some calculations... */
|
|
bytes = destPR->bytes;
|
|
|
|
/* the data pointers... */
|
|
src_m1 = (unsigned char *) g_malloc (orig_width * bytes);
|
|
src = (unsigned char *) g_malloc (orig_width * bytes);
|
|
src_p1 = (unsigned char *) g_malloc (orig_width * bytes);
|
|
src_p2 = (unsigned char *) g_malloc (orig_width * bytes);
|
|
dest = (unsigned char *) g_malloc (width * bytes);
|
|
|
|
/* find the ratios of old x to new x and old y to new y */
|
|
x_rat = (double) orig_width / (double) width;
|
|
y_rat = (double) orig_height / (double) height;
|
|
|
|
/* determine the scale type */
|
|
if (x_rat < 1.0 && y_rat < 1.0)
|
|
scale_type = MagnifyX_MagnifyY;
|
|
else if (x_rat < 1.0 && y_rat >= 1.0)
|
|
scale_type = MagnifyX_MinifyY;
|
|
else if (x_rat >= 1.0 && y_rat < 1.0)
|
|
scale_type = MinifyX_MagnifyY;
|
|
else
|
|
scale_type = MinifyX_MinifyY;
|
|
|
|
/* allocate an array to help with the calculations */
|
|
row = (double *) g_malloc (sizeof (double) * width * bytes);
|
|
x_frac = (double *) g_malloc (sizeof (double) * (width + orig_width));
|
|
|
|
/* initialize the pre-calculated pixel fraction array */
|
|
src_col = 0;
|
|
x_cum = (double) src_col;
|
|
x_last = x_cum;
|
|
|
|
for (i = 0; i < width + orig_width; i++)
|
|
{
|
|
if (x_cum + x_rat <= (src_col + 1 + EPSILON))
|
|
{
|
|
x_cum += x_rat;
|
|
x_frac[i] = x_cum - x_last;
|
|
}
|
|
else
|
|
{
|
|
src_col ++;
|
|
x_frac[i] = src_col - x_last;
|
|
}
|
|
x_last += x_frac[i];
|
|
}
|
|
|
|
/* clear the "row" array */
|
|
memset (row, 0, sizeof (double) * width * bytes);
|
|
|
|
/* counters... */
|
|
src_row = 0;
|
|
y_cum = (double) src_row;
|
|
y_last = y_cum;
|
|
|
|
/* Get the first src row */
|
|
pixel_region_get_row (srcPR, 0, src_row, orig_width, src, 1);
|
|
/* Get the next two if possible */
|
|
if (src_row < (orig_height - 1))
|
|
pixel_region_get_row (srcPR, 0, (src_row + 1), orig_width, src_p1, 1);
|
|
if ((src_row + 1) < (orig_height - 1))
|
|
pixel_region_get_row (srcPR, 0, (src_row + 2), orig_width, src_p2, 1);
|
|
|
|
/* Scale the selected region */
|
|
i = height;
|
|
while (i)
|
|
{
|
|
src_col = 0;
|
|
x_cum = (double) src_col;
|
|
|
|
/* determine the fraction of the src pixel we are using for y */
|
|
if (y_cum + y_rat <= (src_row + 1 + EPSILON))
|
|
{
|
|
y_cum += y_rat;
|
|
dy = y_cum - src_row;
|
|
y_frac = y_cum - y_last;
|
|
advance_dest_y = TRUE;
|
|
}
|
|
else
|
|
{
|
|
y_frac = (src_row + 1) - y_last;
|
|
dy = 1.0;
|
|
advance_dest_y = FALSE;
|
|
}
|
|
|
|
y_last += y_frac;
|
|
|
|
s = src;
|
|
s_m1 = (src_row > 0) ? src_m1 : src;
|
|
s_p1 = (src_row < (orig_height - 1)) ? src_p1 : src;
|
|
s_p2 = ((src_row + 1) < (orig_height - 1)) ? src_p2 : s_p1;
|
|
|
|
r = row;
|
|
|
|
frac = 0;
|
|
j = width;
|
|
|
|
while (j)
|
|
{
|
|
if (x_cum + x_rat <= (src_col + 1 + EPSILON))
|
|
{
|
|
x_cum += x_rat;
|
|
dx = x_cum - src_col;
|
|
advance_dest_x = TRUE;
|
|
}
|
|
else
|
|
{
|
|
dx = 1.0;
|
|
advance_dest_x = FALSE;
|
|
}
|
|
|
|
tot_frac = x_frac[frac++] * y_frac;
|
|
|
|
minus_x = (src_col > 0) ? -bytes : 0;
|
|
plus_x = (src_col < (orig_width - 1)) ? bytes : 0;
|
|
plus2_x = ((src_col + 1) < (orig_width - 1)) ? bytes * 2 : plus_x;
|
|
|
|
if (cubic_interpolation)
|
|
switch (scale_type)
|
|
{
|
|
case MagnifyX_MagnifyY:
|
|
for (b = 0; b < bytes; b++)
|
|
r[b] += cubic (dy, cubic (dx, s_m1[b+minus_x], s_m1[b], s_m1[b+plus_x], s_m1[b+plus2_x]),
|
|
cubic (dx, s[b+minus_x], s[b], s[b+plus_x], s[b+plus2_x]),
|
|
cubic (dx, s_p1[b+minus_x], s_p1[b], s_p1[b+plus_x], s_p1[b+plus2_x]),
|
|
cubic (dx, s_p2[b+minus_x], s_p2[b], s_p2[b+plus_x], s_p2[b+plus2_x])) * tot_frac;
|
|
break;
|
|
case MagnifyX_MinifyY:
|
|
for (b = 0; b < bytes; b++)
|
|
r[b] += cubic (dx, s[b+minus_x], s[b], s[b+plus_x], s[b+plus2_x]) * tot_frac;
|
|
break;
|
|
case MinifyX_MagnifyY:
|
|
for (b = 0; b < bytes; b++)
|
|
r[b] += cubic (dy, s_m1[b], s[b], s_p1[b], s_p2[b]) * tot_frac;
|
|
break;
|
|
case MinifyX_MinifyY:
|
|
for (b = 0; b < bytes; b++)
|
|
r[b] += s[b] * tot_frac;
|
|
break;
|
|
}
|
|
else
|
|
switch (scale_type)
|
|
{
|
|
case MagnifyX_MagnifyY:
|
|
for (b = 0; b < bytes; b++)
|
|
r[b] += ((1 - dy) * ((1 - dx) * s[b] + dx * s[b+plus_x]) +
|
|
dy * ((1 - dx) * s_p1[b] + dx * s_p1[b+plus_x])) * tot_frac;
|
|
break;
|
|
case MagnifyX_MinifyY:
|
|
for (b = 0; b < bytes; b++)
|
|
r[b] += (s[b] * (1 - dx) + s[b+plus_x] * dx) * tot_frac;
|
|
break;
|
|
case MinifyX_MagnifyY:
|
|
for (b = 0; b < bytes; b++)
|
|
r[b] += (s[b] * (1 - dy) + s_p1[b] * dy) * tot_frac;
|
|
break;
|
|
case MinifyX_MinifyY:
|
|
for (b = 0; b < bytes; b++)
|
|
r[b] += s[b] * tot_frac;
|
|
break;
|
|
}
|
|
|
|
if (advance_dest_x)
|
|
{
|
|
r += bytes;
|
|
j--;
|
|
}
|
|
else
|
|
{
|
|
s_m1 += bytes;
|
|
s += bytes;
|
|
s_p1 += bytes;
|
|
s_p2 += bytes;
|
|
src_col++;
|
|
}
|
|
}
|
|
|
|
if (advance_dest_y)
|
|
{
|
|
tot_frac = 1.0 / (x_rat * y_rat);
|
|
|
|
/* copy "row" to "dest" */
|
|
d = dest;
|
|
r = row;
|
|
|
|
j = width;
|
|
while (j--)
|
|
{
|
|
b = bytes;
|
|
while (b--)
|
|
*d++ = (unsigned char) (*r++ * tot_frac + 0.5);
|
|
}
|
|
|
|
/* set the pixel region span */
|
|
pixel_region_set_row (destPR, 0, (height - i), width, dest);
|
|
|
|
/* clear the "row" array */
|
|
memset (row, 0, sizeof (double) * width * bytes);
|
|
|
|
i--;
|
|
}
|
|
else
|
|
{
|
|
/* Shuffle pointers */
|
|
s = src_m1;
|
|
src_m1 = src;
|
|
src = src_p1;
|
|
src_p1 = src_p2;
|
|
src_p2 = s;
|
|
|
|
src_row++;
|
|
if ((src_row + 1) < (orig_height - 1))
|
|
pixel_region_get_row (srcPR, 0, (src_row + 2), orig_width, src_p2, 1);
|
|
}
|
|
}
|
|
|
|
/* free up temporary arrays */
|
|
g_free (row);
|
|
g_free (x_frac);
|
|
g_free (src_m1);
|
|
g_free (src);
|
|
g_free (src_p1);
|
|
g_free (src_p2);
|
|
g_free (dest);
|
|
}
|
|
|
|
|
|
void
|
|
subsample_region (PixelRegion *srcPR,
|
|
PixelRegion *destPR,
|
|
int subsample)
|
|
{
|
|
unsigned char * src, * s;
|
|
unsigned char * dest, * d;
|
|
double * row, * r;
|
|
int destwidth;
|
|
int src_row, src_col;
|
|
int bytes, b;
|
|
int width, height;
|
|
int orig_width, orig_height;
|
|
double x_rat, y_rat;
|
|
double x_cum, y_cum;
|
|
double x_last, y_last;
|
|
double * x_frac, y_frac, tot_frac;
|
|
int i, j;
|
|
int frac;
|
|
int advance_dest;
|
|
|
|
orig_width = srcPR->w / subsample;
|
|
orig_height = srcPR->h / subsample;
|
|
width = destPR->w;
|
|
height = destPR->h;
|
|
|
|
/* Some calculations... */
|
|
bytes = destPR->bytes;
|
|
destwidth = destPR->rowstride;
|
|
|
|
/* the data pointers... */
|
|
src = (unsigned char *) g_malloc (orig_width * bytes);
|
|
dest = destPR->data;
|
|
|
|
/* find the ratios of old x to new x and old y to new y */
|
|
x_rat = (double) orig_width / (double) width;
|
|
y_rat = (double) orig_height / (double) height;
|
|
|
|
/* allocate an array to help with the calculations */
|
|
row = (double *) g_malloc (sizeof (double) * width * bytes);
|
|
x_frac = (double *) g_malloc (sizeof (double) * (width + orig_width));
|
|
|
|
/* initialize the pre-calculated pixel fraction array */
|
|
src_col = 0;
|
|
x_cum = (double) src_col;
|
|
x_last = x_cum;
|
|
|
|
for (i = 0; i < width + orig_width; i++)
|
|
{
|
|
if (x_cum + x_rat <= (src_col + 1 + EPSILON))
|
|
{
|
|
x_cum += x_rat;
|
|
x_frac[i] = x_cum - x_last;
|
|
}
|
|
else
|
|
{
|
|
src_col ++;
|
|
x_frac[i] = src_col - x_last;
|
|
}
|
|
x_last += x_frac[i];
|
|
}
|
|
|
|
/* clear the "row" array */
|
|
memset (row, 0, sizeof (double) * width * bytes);
|
|
|
|
/* counters... */
|
|
src_row = 0;
|
|
y_cum = (double) src_row;
|
|
y_last = y_cum;
|
|
|
|
pixel_region_get_row (srcPR, 0, src_row * subsample, orig_width * subsample, src, subsample);
|
|
|
|
/* Scale the selected region */
|
|
for (i = 0; i < height; )
|
|
{
|
|
src_col = 0;
|
|
x_cum = (double) src_col;
|
|
|
|
/* determine the fraction of the src pixel we are using for y */
|
|
if (y_cum + y_rat <= (src_row + 1 + EPSILON))
|
|
{
|
|
y_cum += y_rat;
|
|
y_frac = y_cum - y_last;
|
|
advance_dest = TRUE;
|
|
}
|
|
else
|
|
{
|
|
src_row ++;
|
|
y_frac = src_row - y_last;
|
|
advance_dest = FALSE;
|
|
}
|
|
|
|
y_last += y_frac;
|
|
|
|
s = src;
|
|
r = row;
|
|
|
|
frac = 0;
|
|
j = width;
|
|
|
|
while (j)
|
|
{
|
|
tot_frac = x_frac[frac++] * y_frac;
|
|
|
|
for (b = 0; b < bytes; b++)
|
|
r[b] += s[b] * tot_frac;
|
|
|
|
/* increment the destination */
|
|
if (x_cum + x_rat <= (src_col + 1 + EPSILON))
|
|
{
|
|
r += bytes;
|
|
x_cum += x_rat;
|
|
j--;
|
|
}
|
|
|
|
/* increment the source */
|
|
else
|
|
{
|
|
s += bytes;
|
|
src_col++;
|
|
}
|
|
}
|
|
|
|
if (advance_dest)
|
|
{
|
|
tot_frac = 1.0 / (x_rat * y_rat);
|
|
|
|
/* copy "row" to "dest" */
|
|
d = dest;
|
|
r = row;
|
|
|
|
j = width;
|
|
while (j--)
|
|
{
|
|
b = bytes;
|
|
while (b--)
|
|
*d++ = (unsigned char) (*r++ * tot_frac + 0.5);
|
|
}
|
|
|
|
dest += destwidth;
|
|
|
|
/* clear the "row" array */
|
|
memset (row, 0, sizeof (double) * destwidth);
|
|
|
|
i++;
|
|
}
|
|
else
|
|
pixel_region_get_row (srcPR, 0, src_row * subsample, orig_width * subsample, src, subsample);
|
|
}
|
|
|
|
/* free up temporary arrays */
|
|
g_free (row);
|
|
g_free (x_frac);
|
|
g_free (src);
|
|
}
|
|
|
|
|
|
float
|
|
shapeburst_region (PixelRegion *srcPR,
|
|
PixelRegion *distPR)
|
|
{
|
|
Tile *tile;
|
|
unsigned char *tile_data;
|
|
float max_iterations;
|
|
float *distp_cur;
|
|
float *distp_prev;
|
|
float *tmp;
|
|
float min_prev;
|
|
float float_tmp;
|
|
int min;
|
|
int min_left;
|
|
int length;
|
|
int i, j, k;
|
|
int src;
|
|
int fraction;
|
|
int prev_frac;
|
|
int x, y;
|
|
int end;
|
|
int boundary;
|
|
int inc;
|
|
|
|
src = 0;
|
|
|
|
max_iterations = 0.0;
|
|
|
|
length = distPR->w + 1;
|
|
distp_prev = (float *) paint_funcs_get_buffer (sizeof (float) * length * 2);
|
|
for (i = 0; i < length; i++)
|
|
distp_prev[i] = 0.0;
|
|
|
|
distp_prev += 1;
|
|
distp_cur = distp_prev + length;
|
|
|
|
for (i = 0; i < srcPR->h; i++)
|
|
{
|
|
/* set the current dist row to 0's */
|
|
memset(distp_cur - 1, 0, sizeof(float) * (length - 1));
|
|
|
|
for (j = 0; j < srcPR->w; j++)
|
|
{
|
|
min_prev = MINIMUM (distp_cur[j-1], distp_prev[j]);
|
|
min_left = MINIMUM ((srcPR->w - j - 1), (srcPR->h - i - 1));
|
|
min = (int) MINIMUM (min_left, min_prev);
|
|
fraction = 255;
|
|
|
|
/* This might need to be changed to 0 instead of k = (min) ? (min - 1) : 0 */
|
|
for (k = (min) ? (min - 1) : 0; k <= min; k++)
|
|
{
|
|
x = j;
|
|
y = i + k;
|
|
end = y - k;
|
|
|
|
while (y >= end)
|
|
{
|
|
tile = tile_manager_get_tile (srcPR->tiles, x, y, TRUE, FALSE);
|
|
tile_data = tile_data_pointer (tile, x%TILE_WIDTH, y%TILE_HEIGHT);
|
|
boundary = MINIMUM ((y % TILE_HEIGHT), (tile_ewidth(tile) - (x % TILE_WIDTH) - 1));
|
|
boundary = MINIMUM (boundary, (y - end)) + 1;
|
|
inc = 1 - tile_ewidth (tile);
|
|
|
|
while (boundary--)
|
|
{
|
|
src = *tile_data;
|
|
if (src == 0)
|
|
{
|
|
min = k;
|
|
y = -1;
|
|
break;
|
|
}
|
|
if (src < fraction)
|
|
fraction = src;
|
|
|
|
x++;
|
|
y--;
|
|
tile_data += inc;
|
|
}
|
|
|
|
tile_release (tile, FALSE);
|
|
}
|
|
}
|
|
|
|
if (src != 0)
|
|
{
|
|
/* If min_left != min_prev use the previous fraction
|
|
* if it is less than the one found
|
|
*/
|
|
if (min_left != min)
|
|
{
|
|
prev_frac = (int) (255 * (min_prev - min));
|
|
if (prev_frac == 255)
|
|
prev_frac = 0;
|
|
fraction = MINIMUM (fraction, prev_frac);
|
|
}
|
|
min++;
|
|
}
|
|
|
|
float_tmp = distp_cur[j] = min + fraction / 256.0;
|
|
|
|
if (float_tmp > max_iterations)
|
|
max_iterations = float_tmp;
|
|
}
|
|
|
|
/* set the dist row */
|
|
pixel_region_set_row (distPR, distPR->x, distPR->y + i, distPR->w, (unsigned char *) distp_cur);
|
|
|
|
/* swap pointers around */
|
|
tmp = distp_prev;
|
|
distp_prev = distp_cur;
|
|
distp_cur = tmp;
|
|
}
|
|
|
|
return max_iterations;
|
|
}
|
|
|
|
static void
|
|
rotate_pointers(void **p, guint32 n)
|
|
{
|
|
guint32 i;
|
|
void *tmp;
|
|
tmp = p[0];
|
|
for (i = 0; i < n-1; i++)
|
|
{
|
|
p[i] = p[i+1];
|
|
}
|
|
p[i] = tmp;
|
|
}
|
|
|
|
static void
|
|
compute_border(gint16 *circ, guint16 xradius, guint16 yradius)
|
|
{
|
|
gint32 i;
|
|
gint32 diameter = xradius*2 +1;
|
|
gdouble tmp;
|
|
for (i = 0; i < diameter; i++)
|
|
{
|
|
if (i > xradius)
|
|
tmp = (i - xradius) - .5;
|
|
else if (i < xradius)
|
|
tmp = (xradius - i) - .5;
|
|
else
|
|
tmp = 0.0;
|
|
circ[i] = RINT(yradius/(double)xradius *
|
|
sqrt((xradius)*(xradius) - (tmp)*(tmp)));
|
|
}
|
|
}
|
|
|
|
void
|
|
fatten_region(PixelRegion *src, gint16 xradius, gint16 yradius)
|
|
{
|
|
/*
|
|
Any bugs in this fuction are probably also in thin_region
|
|
Blame all bugs in this function on jaycox@earthlink.net
|
|
*/
|
|
register gint32 i, j, x, y;
|
|
guchar **buf; /* caches the region's pixel data */
|
|
guchar *out; /* holds the new scan line we are computing */
|
|
guchar **max; /* caches the largest values for each column */
|
|
gint16 *circ; /* holds the y coords of the filter's mask */
|
|
gint16 last_max, last_index;
|
|
|
|
guchar *buffer;
|
|
|
|
if (xradius <= 0 || yradius <= 0)
|
|
return;
|
|
|
|
max = (guchar **)g_malloc ((src->w + 2*xradius) * sizeof(void *));
|
|
buf = (guchar **)g_malloc((yradius + 1) * sizeof(void *));
|
|
for (i = 0; i < yradius+1; i++)
|
|
{
|
|
buf[i] = (guchar *)g_malloc(src->w * sizeof(guchar));
|
|
}
|
|
buffer = g_malloc((src->w + 2*xradius)*(yradius + 1) * sizeof(guchar));
|
|
for (i = 0; i < src->w + 2*xradius; i++)
|
|
{
|
|
if (i < xradius)
|
|
max[i] = buffer;
|
|
else if (i < src->w + xradius)
|
|
max[i] = &buffer[(yradius+1)*(i - xradius)];
|
|
else
|
|
max[i] = &buffer[(yradius+1)*(src->w + xradius - 1)];
|
|
|
|
for (j = 0 ; j < xradius + 1; j++)
|
|
max[i][j] = 0;
|
|
}
|
|
max += xradius;
|
|
out = (guchar *)g_malloc (src->w * sizeof(guchar));
|
|
|
|
circ = (short *)g_malloc ((2*xradius + 1) * sizeof(gint16));
|
|
compute_border (circ, xradius, yradius);
|
|
circ += xradius;
|
|
|
|
memset (buf[0], 0, src->w);
|
|
for (i = 0; i < yradius && i < src->h; i++) /* load top of image */
|
|
pixel_region_get_row (src, src->x, src->y + i, src->w, buf[i+1], 1);
|
|
|
|
for (x = 0; x < src->w; x++) /* set up max for top of image */
|
|
{
|
|
max[x][0] = buf[0][x];
|
|
for (j = 1; j < yradius+1; j++)
|
|
if (max[x][j] < buf[j][x])
|
|
max[x][j] = buf[j][x];
|
|
else
|
|
max[x][j] = max[x][j-1];
|
|
}
|
|
for (y = 0; y < src->h; y++)
|
|
{
|
|
rotate_pointers((void **)buf, yradius+1);
|
|
if (y < src->h - (yradius))
|
|
pixel_region_get_row (src, src->x, src->y + y + yradius, src->w,
|
|
buf[yradius], 1);
|
|
else
|
|
memset (buf[yradius], 0, src->w);
|
|
for (x = 0 ; x < src->w; x++) /* update max array */
|
|
{
|
|
for (i = yradius; i > 0; i--)
|
|
{
|
|
max[x][i] = (MAX (MAX (max[x][i - 1], buf[i-1][x]), buf[i][x]));
|
|
}
|
|
max[x][0] = buf[0][x];
|
|
}
|
|
last_max = max[0][circ[-1]];
|
|
last_index = 1;
|
|
for (x = 0 ; x < src->w; x++) /* render scan line */
|
|
{
|
|
last_index--;
|
|
if (last_index >= 0)
|
|
{
|
|
if (last_max == 255)
|
|
out[x] = 255;
|
|
else
|
|
{
|
|
last_max = 0;
|
|
for (i = xradius; i >= 0; i--)
|
|
if (last_max < max[x+i][circ[i]])
|
|
{
|
|
last_max = max[x+i][circ[i]];
|
|
last_index = i;
|
|
}
|
|
out[x] = last_max;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
last_index = xradius;
|
|
last_max = max[x+xradius][circ[xradius]];
|
|
for (i = xradius-1; i >= -xradius; i--)
|
|
if (last_max < max[x+i][circ[i]])
|
|
{
|
|
last_max = max[x+i][circ[i]];
|
|
last_index = i;
|
|
}
|
|
out[x] = last_max;
|
|
}
|
|
}
|
|
pixel_region_set_row (src, src->x, src->y + y, src->w, out);
|
|
}
|
|
circ -= xradius;
|
|
max -= xradius;
|
|
g_free (circ);
|
|
g_free (buffer);
|
|
g_free (max);
|
|
for (i = 0; i < yradius + 1; i++)
|
|
g_free (buf[i]);
|
|
g_free (buf);
|
|
g_free (out);
|
|
}
|
|
|
|
void
|
|
thin_region(PixelRegion *src, gint16 xradius, gint16 yradius, int edge_lock)
|
|
{
|
|
/*
|
|
pretty much the same as fatten_region only different
|
|
blame all bugs in this function on jaycox@earthlink.net
|
|
*/
|
|
/* If edge_lock is true we assume that pixels outside the region
|
|
we are passed are identical to the edge pixels.
|
|
If edge_lock is false, we assume that pixels outside the region are 0
|
|
*/
|
|
register gint32 i, j, x, y;
|
|
guchar **buf; /* caches the the region's pixels */
|
|
guchar *out; /* holds the new scan line we are computing */
|
|
guchar **max; /* caches the smallest values for each column */
|
|
gint16 *circ; /* holds the y coords of the filter's mask */
|
|
gint16 last_max, last_index;
|
|
|
|
guchar *buffer;
|
|
|
|
if (xradius <= 0 || yradius <= 0)
|
|
return;
|
|
|
|
max = (guchar **)g_malloc ((src->w+2*xradius) * sizeof(void *));
|
|
buf = (guchar **)g_malloc ((yradius+1) * sizeof(void *));
|
|
for (i = 0; i < yradius+1; i++)
|
|
{
|
|
buf[i] = (guchar *)g_malloc (src->w * sizeof(guchar));
|
|
}
|
|
buffer = g_malloc ((src->w+2*xradius + 1)*(yradius+1));
|
|
for (i = 0; i < src->w+2*xradius; i++)
|
|
{
|
|
if (i < xradius)
|
|
if (edge_lock)
|
|
max[i] = buffer;
|
|
else
|
|
max[i] = &buffer[(yradius+1)*(src->w + xradius)];
|
|
else if (i < src->w + xradius)
|
|
max[i] = &buffer[(yradius+1)*(i - xradius)];
|
|
else
|
|
if (edge_lock)
|
|
max[i] = &buffer[(yradius+1)*(src->w + xradius - 1)];
|
|
else
|
|
max[i] = &buffer[(yradius+1)*(src->w + xradius)];
|
|
for (j = 0 ; j < xradius+1; j++)
|
|
max[i][j] = 255;
|
|
}
|
|
if (!edge_lock)
|
|
for (j = 0 ; j < xradius+1; j++)
|
|
max[0][j] = 0;
|
|
max += xradius;
|
|
out = (guchar *)g_malloc(src->w);
|
|
|
|
circ = (short *)g_malloc((2*xradius + 1)*sizeof(gint16));
|
|
compute_border(circ, xradius, yradius);
|
|
circ += xradius;
|
|
|
|
for (i = 0; i < yradius && i < src->h; i++) /* load top of image */
|
|
pixel_region_get_row (src, src->x, src->y + i, src->w, buf[i+1], 1);
|
|
if (edge_lock)
|
|
memcpy (buf[0], buf[1], src->w);
|
|
else
|
|
memset(buf[0], 0, src->w);
|
|
for (x = 0; x < src->w; x++) /* set up max for top of image */
|
|
{
|
|
max[x][0] = buf[0][x];
|
|
for (j = 1; j < yradius+1; j++)
|
|
if (max[x][j] > buf[j][x])
|
|
max[x][j] = buf[j][x];
|
|
else
|
|
max[x][j] = max[x][j-1];
|
|
}
|
|
for (y = 0; y < src->h; y++)
|
|
{
|
|
rotate_pointers ((void **)buf, yradius+1);
|
|
if (y < src->h - (yradius))
|
|
pixel_region_get_row (src, src->x, src->y + y + yradius, src->w,
|
|
buf[yradius], 1);
|
|
else if (edge_lock)
|
|
memcpy (buf[yradius], buf[yradius -1], src->w);
|
|
else
|
|
memset (buf[yradius], 0, src->w);
|
|
for (x = 0 ; x < src->w; x++) /* update max array */
|
|
{
|
|
for (i = yradius; i > 0; i--)
|
|
{
|
|
max[x][i] = (MIN (MIN (max[x][i - 1], buf[i-1][x]), buf[i][x]));
|
|
}
|
|
max[x][0] = buf[0][x];
|
|
}
|
|
last_max = max[0][circ[-1]];
|
|
last_index = 0;
|
|
for (x = 0 ; x < src->w; x++) /* render scan line */
|
|
{
|
|
last_index--;
|
|
if (last_index >= 0)
|
|
{
|
|
if (last_max == 0)
|
|
out[x] = 0;
|
|
else
|
|
{
|
|
last_max = 255;
|
|
for (i = xradius; i >= 0; i--)
|
|
if (last_max > max[x+i][circ[i]])
|
|
{
|
|
last_max = max[x+i][circ[i]];
|
|
last_index = i;
|
|
}
|
|
out[x] = last_max;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
last_index = xradius;
|
|
last_max = max[x+xradius][circ[xradius]];
|
|
for (i = xradius-1; i >= -xradius; i--)
|
|
if (last_max > max[x+i][circ[i]])
|
|
{
|
|
last_max = max[x+i][circ[i]];
|
|
last_index = i;
|
|
}
|
|
out[x] = last_max;
|
|
}
|
|
}
|
|
pixel_region_set_row (src, src->x, src->y + y, src->w, out);
|
|
}
|
|
/* undo the offsets to the pointers so we can free the malloced memmory */
|
|
circ -= xradius;
|
|
max -= xradius;
|
|
/* free the memmory */
|
|
g_free (circ);
|
|
g_free (buffer);
|
|
g_free (max);
|
|
for (i = 0; i < yradius + 1; i++)
|
|
g_free (buf[i]);
|
|
g_free (buf);
|
|
g_free (out);
|
|
}
|
|
|
|
static void
|
|
compute_transition(guchar *transition, guchar **buf, gint32 width)
|
|
{
|
|
register gint32 x = 0;
|
|
if (width == 1)
|
|
{
|
|
if (buf[1][x] > 127 && (buf[0][x] < 128 || buf[2][x] < 128))
|
|
transition[x] = 255;
|
|
else
|
|
transition[x] = 0;
|
|
return;
|
|
}
|
|
if (buf[1][x] > 127)
|
|
{
|
|
if ( buf[0][x] < 128 || buf[0][x+1] < 128 ||
|
|
buf[1][x+1] < 128 ||
|
|
buf[2][x] < 128 || buf[2][x+1] < 128 )
|
|
transition[x] = 255;
|
|
else
|
|
transition[x] = 0;
|
|
}
|
|
else
|
|
transition[x] = 0;
|
|
for (x = 1; x < width - 1; x++)
|
|
{
|
|
if (buf[1][x] >= 128)
|
|
{
|
|
if (buf[0][x-1] < 128 || buf[0][x] < 128 || buf[0][x+1] < 128 ||
|
|
buf[1][x-1] < 128 || buf[1][x+1] < 128 ||
|
|
buf[2][x-1] < 128 || buf[2][x] < 128 || buf[2][x+1] < 128)
|
|
transition[x] = 255;
|
|
else
|
|
transition[x] = 0;
|
|
}
|
|
else
|
|
transition[x] = 0;
|
|
}
|
|
if (buf[1][x] >= 128)
|
|
{
|
|
if ( buf[0][x-1] < 128 || buf[0][x] < 128 ||
|
|
buf[1][x-1] < 128 ||
|
|
buf[2][x-1] < 128 || buf[2][x] < 128)
|
|
transition[x] = 255;
|
|
else
|
|
transition[x] = 0;
|
|
}
|
|
else
|
|
transition[x] = 0;
|
|
}
|
|
|
|
void
|
|
border_region(PixelRegion *src, gint16 xradius, gint16 yradius)
|
|
{
|
|
/*
|
|
This function has no bugs, but if you imagine some you can
|
|
blame them on jaycox@earthlink.net
|
|
*/
|
|
register gint32 i, j, x, y;
|
|
guchar **buf, *out;
|
|
gint16 *max;
|
|
guchar **density;
|
|
guchar **transition;
|
|
guchar last_max;
|
|
gint16 last_index;
|
|
|
|
if (xradius < 0 || yradius < 0)
|
|
{
|
|
g_warning ("border_region: negative radius specified.");
|
|
return;
|
|
}
|
|
if (xradius == 0 || yradius == 0)
|
|
{
|
|
unsigned char color[] = "\0\0\0\0";
|
|
color_region(src, color);
|
|
return;
|
|
}
|
|
if (xradius == 1 && yradius == 1) /* optimize this case specifically */
|
|
{
|
|
guchar *transition;
|
|
guchar *source[3];
|
|
for (i = 0; i < 3; i++)
|
|
source[i] = (guchar *)g_malloc ((src->w)*sizeof(guchar));
|
|
|
|
transition = (guchar *)g_malloc ((src->w)*sizeof(guchar));
|
|
|
|
pixel_region_get_row (src, src->x, src->y + 0, src->w, source[0], 1);
|
|
memcpy (source[1], source[0], src->w);
|
|
if (src->h > 1)
|
|
pixel_region_get_row (src, src->x, src->y + 1, src->w, source[2], 1);
|
|
else
|
|
memcpy (source[2], source[1], src->w);
|
|
|
|
compute_transition (transition, source, src->w);
|
|
pixel_region_set_row (src, src->x, src->y , src->w, transition);
|
|
|
|
for (y = 1; y < src->h; y++)
|
|
{
|
|
rotate_pointers ((void **)source, 3);
|
|
if (y +1 < src->h)
|
|
pixel_region_get_row (src, src->x, src->y +y +1, src->w, source[2], 1);
|
|
else
|
|
memcpy(source[2], source[1], src->w);
|
|
compute_transition (transition, source, src->w);
|
|
pixel_region_set_row (src, src->x, src->y + y, src->w, transition);
|
|
|
|
}
|
|
for (i = 0; i < 3; i++)
|
|
g_free(source[i]);
|
|
g_free(transition);
|
|
return;
|
|
} /* end of if (xradius == 1 && yradius == 1) */
|
|
max = (gint16 *)g_malloc ((src->w+2*xradius)*sizeof(gint16 *));
|
|
for (i = 0; i < (src->w+2*xradius); i++)
|
|
max[i] = yradius+2;
|
|
max += xradius;
|
|
|
|
buf = (guchar **)g_malloc ((3)*sizeof(void *));
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
buf[i] = (guchar *)g_malloc ((src->w)*sizeof(guchar));
|
|
}
|
|
transition = (guchar **)g_malloc ((yradius+1)*sizeof(void*));
|
|
for (i = 0; i < yradius +1; i++)
|
|
{
|
|
transition[i] = (guchar *)g_malloc (src->w+2*xradius);
|
|
memset(transition[i], 0, src->w+2*xradius);
|
|
transition[i] += xradius;
|
|
}
|
|
out = (guchar *)g_malloc ((src->w)*sizeof(guchar));
|
|
density = (guchar **)g_malloc ((2*xradius + 1)*sizeof(void *));
|
|
density += xradius;
|
|
|
|
for (x = 0; x < (xradius+1); x++) /* allocate density[][] */
|
|
{
|
|
density[ x] = (guchar *)g_malloc (2*yradius +1);
|
|
density[ x] += yradius;
|
|
density[-x] = density[x];
|
|
}
|
|
for (x = 0; x < (xradius+1); x++) /* compute density[][] */
|
|
{
|
|
register double tmpx, tmpy, dist;
|
|
guchar a;
|
|
if (x > 0)
|
|
tmpx = x - 0.5;
|
|
else if (x < 0)
|
|
tmpx = x + 0.5;
|
|
else
|
|
tmpx = 0.0;
|
|
for (y = 0; y < (yradius+1); y++)
|
|
{
|
|
if (y > 0)
|
|
tmpy = y - 0.5;
|
|
else if (y < 0)
|
|
tmpy = y + 0.5;
|
|
else
|
|
tmpy = 0.0;
|
|
dist = (tmpy*tmpy)/(yradius*yradius) + (tmpx*tmpx)/(xradius*xradius);
|
|
if (dist < 1.0)
|
|
a = 255*(1.0 - sqrt (dist));
|
|
else
|
|
a = 0;
|
|
density[ x][ y] = a;
|
|
density[ x][-y] = a;
|
|
density[-x][ y] = a;
|
|
density[-x][-y] = a;
|
|
}
|
|
}
|
|
pixel_region_get_row (src, src->x, src->y + 0, src->w, buf[0], 1);
|
|
memcpy (buf[1], buf[0], src->w);
|
|
if (src->h > 1)
|
|
pixel_region_get_row (src, src->x, src->y + 1, src->w, buf[2], 1);
|
|
else
|
|
memcpy (buf[2], buf[1], src->w);
|
|
compute_transition (transition[1], buf, src->w);
|
|
|
|
for (y = 1; y < yradius && y + 1< src->h; y++) /* set up top of image */
|
|
{
|
|
rotate_pointers ((void **)buf, 3);
|
|
pixel_region_get_row (src, src->x, src->y + y + 1, src->w, buf[2], 1);
|
|
compute_transition (transition[y + 1], buf, src->w);
|
|
}
|
|
for (x = 0; x < src->w; x++) /* set up max[] for top of image */
|
|
{
|
|
max[x] = -(yradius+7);
|
|
for (j = 1; j < yradius+1; j++)
|
|
if (transition[j][x])
|
|
{
|
|
max[x] = j;
|
|
break;
|
|
}
|
|
}
|
|
for (y = 0; y < src->h; y++) /* main calculation loop */
|
|
{
|
|
rotate_pointers ((void **)buf, 3);
|
|
rotate_pointers ((void **)transition, yradius + 1);
|
|
if (y < src->h - (yradius+1))
|
|
{
|
|
pixel_region_get_row (src, src->x, src->y + y + yradius + 1, src->w,
|
|
buf[2], 1);
|
|
compute_transition (transition[yradius], buf, src->w);
|
|
}
|
|
else
|
|
memcpy (transition[yradius], transition[yradius - 1], src->w);
|
|
|
|
for (x = 0; x < src->w; x++) /* update max array */
|
|
{
|
|
if (max[x] < 1)
|
|
{
|
|
if (max[x] <= -yradius)
|
|
{
|
|
if (transition[yradius][x])
|
|
max[x] = yradius;
|
|
else
|
|
max[x]--;
|
|
}
|
|
else
|
|
if (transition[-max[x]][x])
|
|
max[x] = -max[x];
|
|
else if (transition[-max[x]+1][x])
|
|
max[x] = -max[x]+1;
|
|
else
|
|
max[x]--;
|
|
}
|
|
else
|
|
max[x]--;
|
|
if (max[x] < -yradius - 1)
|
|
max[x] = -yradius -1;
|
|
}
|
|
last_max = max[0][density[-1]];
|
|
last_index = 1;
|
|
for (x = 0 ; x < src->w; x++) /* render scan line */
|
|
{
|
|
last_index--;
|
|
if (last_index >= 0)
|
|
{
|
|
last_max = 0;
|
|
for (i = xradius; i >= 0; i--)
|
|
if (max[x+i] <= yradius && max[x+i] >= -yradius &&
|
|
density[i][max[x+i]] > last_max)
|
|
{
|
|
last_max = density[i][max[x+i]];
|
|
last_index = i;
|
|
}
|
|
out[x] = last_max;
|
|
}
|
|
else
|
|
{
|
|
last_max = 0;
|
|
for (i = xradius; i >= -xradius; i--)
|
|
if (max[x+i] <= yradius && max[x+i] >= -yradius &&
|
|
density[i][max[x+i]] > last_max)
|
|
{
|
|
last_max = density[i][max[x+i]];
|
|
last_index = i;
|
|
}
|
|
out[x] = last_max;
|
|
}
|
|
if (last_max == 0)
|
|
{
|
|
for (i = x+1; i < src->w; i++)
|
|
{
|
|
if (max[i] >= -yradius)
|
|
break;
|
|
}
|
|
if (i - x > xradius)
|
|
{
|
|
for (; x < i - xradius; x++)
|
|
out[x] = 0;
|
|
x--;
|
|
}
|
|
last_index = xradius;
|
|
}
|
|
}
|
|
pixel_region_set_row (src, src->x, src->y + y, src->w, out);
|
|
}
|
|
g_free(out);
|
|
|
|
for (i = 0; i < 3; i++)
|
|
g_free(buf[i]);
|
|
|
|
g_free (buf);
|
|
max -= xradius;
|
|
g_free (max);
|
|
|
|
for (i = 0; i < yradius +1; i++)
|
|
{
|
|
transition[i] -= xradius;
|
|
g_free (transition[i]);
|
|
}
|
|
g_free (transition);
|
|
|
|
for (i = 0; i < xradius +1 ; i++)
|
|
{
|
|
density[i]-= yradius;
|
|
g_free(density[i]);
|
|
}
|
|
density -= xradius;
|
|
g_free(density);
|
|
}
|
|
|
|
void
|
|
swap_region (PixelRegion *src,
|
|
PixelRegion *dest)
|
|
{
|
|
int h;
|
|
int length;
|
|
unsigned char * s, * d;
|
|
void * pr;
|
|
|
|
for (pr = pixel_regions_register (2, src, dest); pr != NULL; pr = pixel_regions_process (pr))
|
|
{
|
|
s = src->data;
|
|
h = src->h;
|
|
d = dest->data;
|
|
length = src->w * src->bytes;
|
|
|
|
while (h --)
|
|
{
|
|
swap_pixels (s, d, length);
|
|
s += src->rowstride;
|
|
d += dest->rowstride;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
apply_mask_to_sub_region (int *opacityp,
|
|
PixelRegion *src,
|
|
PixelRegion *mask)
|
|
{
|
|
int h;
|
|
unsigned char * s, * m;
|
|
int opacity = *opacityp;
|
|
|
|
s = src->data;
|
|
m = mask->data;
|
|
h = src->h;
|
|
|
|
while (h --)
|
|
{
|
|
apply_mask_to_alpha_channel (s, m, opacity, src->w, src->bytes);
|
|
s += src->rowstride;
|
|
m += mask->rowstride;
|
|
}
|
|
}
|
|
|
|
void
|
|
apply_mask_to_region (PixelRegion *src,
|
|
PixelRegion *mask,
|
|
int opacity)
|
|
{
|
|
pixel_regions_process_parallel ((p_func)apply_mask_to_sub_region,
|
|
&opacity, 2, src, mask);
|
|
}
|
|
|
|
|
|
static void
|
|
combine_mask_and_sub_region (int *opacityp,
|
|
PixelRegion *src,
|
|
PixelRegion *mask)
|
|
{
|
|
int h;
|
|
unsigned char * s, * m;
|
|
int opacity = *opacityp;
|
|
|
|
s = src->data;
|
|
m = mask->data;
|
|
h = src->h;
|
|
|
|
while (h --)
|
|
{
|
|
combine_mask_and_alpha_channel (s, m, opacity, src->w, src->bytes);
|
|
s += src->rowstride;
|
|
m += mask->rowstride;
|
|
}
|
|
}
|
|
|
|
void
|
|
combine_mask_and_region (PixelRegion *src,
|
|
PixelRegion *mask,
|
|
int opacity)
|
|
{
|
|
|
|
pixel_regions_process_parallel ((p_func)combine_mask_and_sub_region,
|
|
&opacity, 2, src, mask);
|
|
}
|
|
|
|
|
|
void
|
|
copy_gray_to_region (PixelRegion *src,
|
|
PixelRegion *dest)
|
|
{
|
|
int h;
|
|
unsigned char * s, * d;
|
|
void * pr;
|
|
|
|
for (pr = pixel_regions_register (2, src, dest); pr != NULL; pr = pixel_regions_process (pr))
|
|
{
|
|
s = src->data;
|
|
d = dest->data;
|
|
h = src->h;
|
|
|
|
while (h --)
|
|
{
|
|
copy_gray_to_inten_a_pixels (s, d, src->w, dest->bytes);
|
|
s += src->rowstride;
|
|
d += dest->rowstride;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
struct initial_regions_struct
|
|
{
|
|
int opacity;
|
|
LayerModeEffects mode;
|
|
int *affect;
|
|
int type;
|
|
unsigned char *data;
|
|
};
|
|
|
|
void
|
|
initial_sub_region(struct initial_regions_struct *st,
|
|
PixelRegion *src,
|
|
PixelRegion *dest,
|
|
PixelRegion *mask)
|
|
{
|
|
int h;
|
|
unsigned char * s, * d, * m;
|
|
unsigned char buf[512];
|
|
unsigned char *data;
|
|
int opacity;
|
|
LayerModeEffects mode;
|
|
int *affect;
|
|
int type;
|
|
|
|
data = st->data;
|
|
opacity = st->opacity;
|
|
mode = st->mode;
|
|
affect = st->affect;
|
|
type = st->type;
|
|
|
|
if (src->w * (src->bytes + 1) > 512)
|
|
fprintf(stderr, "initial_sub_region:: error :: src->w * (src->bytes + 1) > 512");
|
|
|
|
s = src->data;
|
|
d = dest->data;
|
|
m = (mask) ? mask->data : NULL;
|
|
|
|
for (h = 0; h < src->h; h++)
|
|
{
|
|
/* based on the type of the initial image... */
|
|
switch (type)
|
|
{
|
|
case INITIAL_CHANNEL_MASK:
|
|
case INITIAL_CHANNEL_SELECTION:
|
|
initial_channel_pixels (s, d, src->w, dest->bytes);
|
|
break;
|
|
|
|
case INITIAL_INDEXED:
|
|
initial_indexed_pixels (s, d, data, src->w);
|
|
break;
|
|
|
|
case INITIAL_INDEXED_ALPHA:
|
|
initial_indexed_a_pixels (s, d, m, data, opacity, src->w);
|
|
break;
|
|
|
|
case INITIAL_INTENSITY:
|
|
if (mode == DISSOLVE_MODE)
|
|
{
|
|
dissolve_pixels (s, buf, src->x, src->y + h, opacity, src->w, src->bytes,
|
|
src->bytes + 1, 0);
|
|
initial_inten_pixels (buf, d, m, opacity, affect,
|
|
src->w, src->bytes);
|
|
}
|
|
else
|
|
initial_inten_pixels (s, d, m, opacity, affect, src->w, src->bytes);
|
|
break;
|
|
|
|
case INITIAL_INTENSITY_ALPHA:
|
|
if (mode == DISSOLVE_MODE)
|
|
{
|
|
dissolve_pixels (s, buf, src->x, src->y + h, opacity, src->w, src->bytes,
|
|
src->bytes, 1);
|
|
initial_inten_a_pixels (buf, d, m, opacity, affect,
|
|
src->w, src->bytes);
|
|
}
|
|
else
|
|
initial_inten_a_pixels (s, d, m, opacity, affect, src->w, src->bytes);
|
|
break;
|
|
}
|
|
|
|
s += src->rowstride;
|
|
d += dest->rowstride;
|
|
if (mask)
|
|
m += mask->rowstride;
|
|
}
|
|
}
|
|
|
|
void
|
|
initial_region (PixelRegion *src,
|
|
PixelRegion *dest,
|
|
PixelRegion *mask,
|
|
unsigned char *data,
|
|
int opacity,
|
|
LayerModeEffects mode,
|
|
int *affect,
|
|
int type)
|
|
{
|
|
struct initial_regions_struct st;
|
|
|
|
st.opacity = opacity;
|
|
st.mode = mode;
|
|
st.affect = affect;
|
|
st.type = type;
|
|
st.data = data;
|
|
|
|
pixel_regions_process_parallel ((p_func)initial_sub_region, &st, 3,
|
|
src, dest, mask);
|
|
}
|
|
|
|
struct combine_regions_struct
|
|
{
|
|
int opacity;
|
|
LayerModeEffects mode;
|
|
int *affect;
|
|
int type;
|
|
unsigned char *data;
|
|
int has_alpha1, has_alpha2;
|
|
gboolean opacity_quickskip_possible;
|
|
gboolean transparency_quickskip_possible;
|
|
};
|
|
|
|
|
|
|
|
void
|
|
combine_sub_region(struct combine_regions_struct *st,
|
|
PixelRegion *src1, PixelRegion *src2,
|
|
PixelRegion *dest, PixelRegion *mask)
|
|
{
|
|
unsigned char *data;
|
|
int opacity;
|
|
LayerModeEffects mode;
|
|
int *affect;
|
|
int type;
|
|
int h;
|
|
int has_alpha1, has_alpha2;
|
|
int combine = 0;
|
|
int mode_affect;
|
|
unsigned char * s, * s1, * s2;
|
|
unsigned char * d, * m;
|
|
unsigned char buf[512];
|
|
gboolean opacity_quickskip_possible;
|
|
gboolean transparency_quickskip_possible;
|
|
TileRowHint hint;
|
|
|
|
opacity = st->opacity;
|
|
mode = st->mode;
|
|
affect = st->affect;
|
|
type = st->type;
|
|
data = st->data;
|
|
has_alpha1 = st->has_alpha1;
|
|
has_alpha2 = st->has_alpha2;
|
|
|
|
opacity_quickskip_possible = (st->opacity_quickskip_possible &&
|
|
src2->tiles);
|
|
transparency_quickskip_possible = (st->transparency_quickskip_possible &&
|
|
src2->tiles);
|
|
|
|
s1 = src1->data;
|
|
s2 = src2->data;
|
|
d = dest->data;
|
|
m = (mask) ? mask->data : NULL;
|
|
|
|
if (src1->w > 128)
|
|
g_error("combine_sub_region::src1->w = %d\n", src1->w);
|
|
|
|
if (transparency_quickskip_possible || opacity_quickskip_possible)
|
|
{
|
|
#ifdef HINTS_SANITY
|
|
if (src1->h != src2->h)
|
|
g_error("HEIGHTS SUCK!!");
|
|
if (src1->offy != dest->offy)
|
|
g_error("SRC1 OFFSET != DEST OFFSET");
|
|
#endif
|
|
update_tile_rowhints (src2->curtile,
|
|
src2->offy, src2->offy + (src1->h - 1));
|
|
}
|
|
/* else it's probably a brush-composite */
|
|
|
|
for (h = 0; h < src1->h; h++)
|
|
{
|
|
hint = TILEROWHINT_UNDEFINED;
|
|
|
|
if (transparency_quickskip_possible)
|
|
{
|
|
hint = tile_get_rowhint (src2->curtile, (src2->offy + h));
|
|
|
|
if (hint == TILEROWHINT_TRANSPARENT)
|
|
{
|
|
goto next_row;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (opacity_quickskip_possible)
|
|
{
|
|
hint = tile_get_rowhint (src2->curtile, (src2->offy + h));
|
|
}
|
|
}
|
|
|
|
s = buf;
|
|
|
|
/* apply the paint mode based on the combination type & mode */
|
|
switch (type)
|
|
{
|
|
case COMBINE_INTEN_A_INDEXED_A:
|
|
case COMBINE_INTEN_A_CHANNEL_MASK:
|
|
case COMBINE_INTEN_A_CHANNEL_SELECTION:
|
|
combine = type;
|
|
break;
|
|
|
|
case COMBINE_INDEXED_INDEXED:
|
|
case COMBINE_INDEXED_INDEXED_A:
|
|
case COMBINE_INDEXED_A_INDEXED_A:
|
|
/* Now, apply the paint mode--for indexed images */
|
|
combine = apply_indexed_layer_mode (s1, s2, &s, mode,
|
|
has_alpha1, has_alpha2);
|
|
break;
|
|
|
|
case COMBINE_INTEN_INTEN_A:
|
|
case COMBINE_INTEN_A_INTEN:
|
|
case COMBINE_INTEN_INTEN:
|
|
case COMBINE_INTEN_A_INTEN_A:
|
|
/* Now, apply the paint mode */
|
|
combine = apply_layer_mode (s1, s2, &s, src1->x, src1->y + h,
|
|
opacity, src1->w, mode,
|
|
src1->bytes, src2->bytes,
|
|
has_alpha1, has_alpha2, &mode_affect);
|
|
break;
|
|
|
|
default:
|
|
g_warning ("combine_sub_region: unhandled combine-type.");
|
|
break;
|
|
}
|
|
|
|
/* based on the type of the initial image... */
|
|
switch (combine)
|
|
{
|
|
case COMBINE_INDEXED_INDEXED:
|
|
combine_indexed_and_indexed_pixels (s1, s2, d, m, opacity,
|
|
affect, src1->w,
|
|
src1->bytes);
|
|
break;
|
|
|
|
case COMBINE_INDEXED_INDEXED_A:
|
|
combine_indexed_and_indexed_a_pixels (s1, s2, d, m, opacity,
|
|
affect, src1->w,
|
|
src1->bytes);
|
|
break;
|
|
|
|
case COMBINE_INDEXED_A_INDEXED_A:
|
|
combine_indexed_a_and_indexed_a_pixels (s1, s2, d, m, opacity,
|
|
affect, src1->w,
|
|
src1->bytes);
|
|
break;
|
|
|
|
case COMBINE_INTEN_A_INDEXED_A:
|
|
/* assume the data passed to this procedure is the
|
|
* indexed layer's colormap
|
|
*/
|
|
combine_inten_a_and_indexed_a_pixels (s1, s2, d, m, data, opacity,
|
|
src1->w, dest->bytes);
|
|
break;
|
|
|
|
case COMBINE_INTEN_A_CHANNEL_MASK:
|
|
/* assume the data passed to this procedure is the
|
|
* indexed layer's colormap
|
|
*/
|
|
combine_inten_a_and_channel_mask_pixels (s1, s2, d, data, opacity,
|
|
src1->w, dest->bytes);
|
|
break;
|
|
|
|
case COMBINE_INTEN_A_CHANNEL_SELECTION:
|
|
combine_inten_a_and_channel_selection_pixels (s1, s2, d, data,
|
|
opacity,
|
|
src1->w,
|
|
src1->bytes);
|
|
break;
|
|
|
|
case COMBINE_INTEN_INTEN:
|
|
if ((hint == TILEROWHINT_OPAQUE) &&
|
|
opacity_quickskip_possible)
|
|
{
|
|
memcpy (d, s, dest->w * dest->bytes);
|
|
}
|
|
else
|
|
combine_inten_and_inten_pixels (s1, s, d, m, opacity,
|
|
affect, src1->w, src1->bytes);
|
|
break;
|
|
|
|
case COMBINE_INTEN_INTEN_A:
|
|
combine_inten_and_inten_a_pixels (s1, s, d, m, opacity,
|
|
affect, src1->w, src1->bytes);
|
|
break;
|
|
|
|
case COMBINE_INTEN_A_INTEN:
|
|
combine_inten_a_and_inten_pixels (s1, s, d, m, opacity,
|
|
affect, mode_affect, src1->w,
|
|
src1->bytes);
|
|
break;
|
|
|
|
case COMBINE_INTEN_A_INTEN_A:
|
|
if ((hint == TILEROWHINT_OPAQUE) &&
|
|
opacity_quickskip_possible)
|
|
{
|
|
memcpy (d, s, dest->w * dest->bytes);
|
|
}
|
|
else
|
|
combine_inten_a_and_inten_a_pixels (s1, s, d, m, opacity,
|
|
affect, mode_affect,
|
|
src1->w, src1->bytes);
|
|
break;
|
|
|
|
case BEHIND_INTEN:
|
|
behind_inten_pixels (s1, s, d, m, opacity,
|
|
affect, src1->w, src1->bytes,
|
|
src2->bytes, has_alpha1, has_alpha2);
|
|
break;
|
|
|
|
case BEHIND_INDEXED:
|
|
behind_indexed_pixels (s1, s, d, m, opacity,
|
|
affect, src1->w, src1->bytes,
|
|
src2->bytes, has_alpha1, has_alpha2);
|
|
break;
|
|
|
|
case REPLACE_INTEN:
|
|
replace_inten_pixels (s1, s, d, m, opacity,
|
|
affect, src1->w, src1->bytes,
|
|
src2->bytes, has_alpha1, has_alpha2);
|
|
break;
|
|
|
|
case REPLACE_INDEXED:
|
|
replace_indexed_pixels (s1, s, d, m, opacity,
|
|
affect, src1->w, src1->bytes,
|
|
src2->bytes, has_alpha1, has_alpha2);
|
|
break;
|
|
|
|
case ERASE_INTEN:
|
|
erase_inten_pixels (s1, s, d, m, opacity,
|
|
affect, src1->w, src1->bytes);
|
|
break;
|
|
|
|
case ERASE_INDEXED:
|
|
erase_indexed_pixels (s1, s, d, m, opacity,
|
|
affect, src1->w, src1->bytes);
|
|
break;
|
|
|
|
case ANTI_ERASE_INTEN:
|
|
anti_erase_inten_pixels (s1, s, d, m, opacity,
|
|
affect, src1->w, src1->bytes);
|
|
break;
|
|
|
|
case ANTI_ERASE_INDEXED:
|
|
anti_erase_indexed_pixels (s1, s, d, m, opacity,
|
|
affect, src1->w, src1->bytes);
|
|
break;
|
|
|
|
case NO_COMBINATION:
|
|
g_warning("NO_COMBINATION");
|
|
break;
|
|
|
|
default:
|
|
g_warning("UNKNOWN COMBINATION");
|
|
break;
|
|
}
|
|
|
|
next_row:
|
|
s1 += src1->rowstride;
|
|
s2 += src2->rowstride;
|
|
d += dest->rowstride;
|
|
if (mask)
|
|
m += mask->rowstride;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
combine_regions (PixelRegion *src1,
|
|
PixelRegion *src2,
|
|
PixelRegion *dest,
|
|
PixelRegion *mask,
|
|
unsigned char *data,
|
|
int opacity,
|
|
LayerModeEffects mode,
|
|
int *affect,
|
|
int type)
|
|
{
|
|
int has_alpha1, has_alpha2;
|
|
int i;
|
|
struct combine_regions_struct st;
|
|
|
|
/* Determine which sources have alpha channels */
|
|
switch (type)
|
|
{
|
|
case COMBINE_INTEN_INTEN:
|
|
case COMBINE_INDEXED_INDEXED:
|
|
has_alpha1 = has_alpha2 = 0;
|
|
break;
|
|
case COMBINE_INTEN_A_INTEN:
|
|
has_alpha1 = 1;
|
|
has_alpha2 = 0;
|
|
break;
|
|
case COMBINE_INTEN_INTEN_A:
|
|
case COMBINE_INDEXED_INDEXED_A:
|
|
has_alpha1 = 0;
|
|
has_alpha2 = 1;
|
|
break;
|
|
case COMBINE_INTEN_A_INTEN_A:
|
|
case COMBINE_INDEXED_A_INDEXED_A:
|
|
has_alpha1 = has_alpha2 = 1;
|
|
break;
|
|
default:
|
|
has_alpha1 = has_alpha2 = 0;
|
|
}
|
|
|
|
st.opacity = opacity;
|
|
st.mode = mode;
|
|
st.affect = affect;
|
|
st.type = type;
|
|
st.data = data;
|
|
st.has_alpha1 = has_alpha1;
|
|
st.has_alpha2 = has_alpha2;
|
|
|
|
/* cheap and easy when the row of src2 is completely opaque/transparent
|
|
and the wind is otherwise blowing in the right direction.
|
|
*/
|
|
|
|
/* First check - we can't do an opacity quickskip if the drawable
|
|
has a mask, or non-full opacity, or the layer mode dictates
|
|
that we might gain transparency.
|
|
*/
|
|
st.opacity_quickskip_possible = ((!mask) && (opacity==255) &&
|
|
(!layer_modes[mode].decrease_opacity) &&
|
|
(layer_modes[mode].affect_alpha &&
|
|
has_alpha1 &&
|
|
affect[src1->bytes-1]) );
|
|
|
|
/* Second check - if any single colour channel can't be affected,
|
|
we can't use the opacity quickskip.
|
|
*/
|
|
if (st.opacity_quickskip_possible)
|
|
{
|
|
for (i=0; i<src1->bytes-1; i++)
|
|
{
|
|
if (!affect[i])
|
|
{
|
|
st.opacity_quickskip_possible = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* transparency quickskip is only possible if the layer mode
|
|
dictates that we cannot possibly gain opacity, or the 'overall'
|
|
opacity of the layer is set to zero anyway.
|
|
*/
|
|
st.transparency_quickskip_possible = ((!layer_modes[mode].increase_opacity)
|
|
|| (opacity==0));
|
|
|
|
/* Start the actual processing.
|
|
*/
|
|
pixel_regions_process_parallel ((p_func)combine_sub_region, &st, 4,
|
|
src1, src2, dest, mask);
|
|
}
|
|
|
|
void
|
|
combine_regions_replace (PixelRegion *src1,
|
|
PixelRegion *src2,
|
|
PixelRegion *dest,
|
|
PixelRegion *mask,
|
|
unsigned char *data,
|
|
int opacity,
|
|
int *affect,
|
|
int type)
|
|
{
|
|
int h;
|
|
unsigned char * s1, * s2;
|
|
unsigned char * d, * m;
|
|
void * pr;
|
|
|
|
for (pr = pixel_regions_register (4, src1, src2, dest, mask); pr != NULL; pr = pixel_regions_process (pr))
|
|
{
|
|
s1 = src1->data;
|
|
s2 = src2->data;
|
|
d = dest->data;
|
|
m = mask->data;
|
|
|
|
for (h = 0; h < src1->h; h++)
|
|
{
|
|
|
|
/* Now, apply the paint mode */
|
|
apply_layer_mode_replace (s1, s2, d, m, src1->x, src1->y + h, opacity, src1->w,
|
|
src1->bytes, src2->bytes, affect);
|
|
|
|
s1 += src1->rowstride;
|
|
s2 += src2->rowstride;
|
|
d += dest->rowstride;
|
|
m += mask->rowstride;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*********************************
|
|
* color conversion routines *
|
|
*********************************/
|
|
|
|
void
|
|
rgb_to_hsv (int *r,
|
|
int *g,
|
|
int *b)
|
|
{
|
|
int red, green, blue;
|
|
float h, s, v;
|
|
int min, max;
|
|
int delta;
|
|
|
|
h = 0.0;
|
|
|
|
red = *r;
|
|
green = *g;
|
|
blue = *b;
|
|
|
|
if (red > green)
|
|
{
|
|
if (red > blue)
|
|
max = red;
|
|
else
|
|
max = blue;
|
|
|
|
if (green < blue)
|
|
min = green;
|
|
else
|
|
min = blue;
|
|
}
|
|
else
|
|
{
|
|
if (green > blue)
|
|
max = green;
|
|
else
|
|
max = blue;
|
|
|
|
if (red < blue)
|
|
min = red;
|
|
else
|
|
min = blue;
|
|
}
|
|
|
|
v = max;
|
|
|
|
if (max != 0)
|
|
s = ((max - min) * 255) / (float) max;
|
|
else
|
|
s = 0;
|
|
|
|
if (s == 0)
|
|
h = 0;
|
|
else
|
|
{
|
|
delta = max - min;
|
|
if (red == max)
|
|
h = (green - blue) / (float) delta;
|
|
else if (green == max)
|
|
h = 2 + (blue - red) / (float) delta;
|
|
else if (blue == max)
|
|
h = 4 + (red - green) / (float) delta;
|
|
h *= 42.5;
|
|
|
|
if (h < 0)
|
|
h += 255;
|
|
if (h > 255)
|
|
h -= 255;
|
|
}
|
|
|
|
*r = h;
|
|
*g = s;
|
|
*b = v;
|
|
}
|
|
|
|
|
|
void
|
|
hsv_to_rgb (int *h,
|
|
int *s,
|
|
int *v)
|
|
{
|
|
float hue, saturation, value;
|
|
float f, p, q, t;
|
|
|
|
if (*s == 0)
|
|
{
|
|
*h = *v;
|
|
*s = *v;
|
|
*v = *v;
|
|
}
|
|
else
|
|
{
|
|
hue = *h * 6.0 / 255.0;
|
|
saturation = *s / 255.0;
|
|
value = *v / 255.0;
|
|
|
|
f = hue - (int) hue;
|
|
p = value * (1.0 - saturation);
|
|
q = value * (1.0 - (saturation * f));
|
|
t = value * (1.0 - (saturation * (1.0 - f)));
|
|
|
|
switch ((int) hue)
|
|
{
|
|
case 0:
|
|
*h = value * 255;
|
|
*s = t * 255;
|
|
*v = p * 255;
|
|
break;
|
|
case 1:
|
|
*h = q * 255;
|
|
*s = value * 255;
|
|
*v = p * 255;
|
|
break;
|
|
case 2:
|
|
*h = p * 255;
|
|
*s = value * 255;
|
|
*v = t * 255;
|
|
break;
|
|
case 3:
|
|
*h = p * 255;
|
|
*s = q * 255;
|
|
*v = value * 255;
|
|
break;
|
|
case 4:
|
|
*h = t * 255;
|
|
*s = p * 255;
|
|
*v = value * 255;
|
|
break;
|
|
case 5:
|
|
*h = value * 255;
|
|
*s = p * 255;
|
|
*v = q * 255;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
rgb_to_hls (int *r,
|
|
int *g,
|
|
int *b)
|
|
{
|
|
int red, green, blue;
|
|
float h, l, s;
|
|
int min, max;
|
|
int delta;
|
|
|
|
red = *r;
|
|
green = *g;
|
|
blue = *b;
|
|
|
|
if (red > green)
|
|
{
|
|
if (red > blue)
|
|
max = red;
|
|
else
|
|
max = blue;
|
|
|
|
if (green < blue)
|
|
min = green;
|
|
else
|
|
min = blue;
|
|
}
|
|
else
|
|
{
|
|
if (green > blue)
|
|
max = green;
|
|
else
|
|
max = blue;
|
|
|
|
if (red < blue)
|
|
min = red;
|
|
else
|
|
min = blue;
|
|
}
|
|
|
|
l = (max + min) / 2.0;
|
|
|
|
if (max == min)
|
|
{
|
|
s = 0.0;
|
|
h = 0.0;
|
|
}
|
|
else
|
|
{
|
|
delta = (max - min);
|
|
|
|
if (l < 128)
|
|
s = 255 * (float) delta / (float) (max + min);
|
|
else
|
|
s = 255 * (float) delta / (float) (511 - max - min);
|
|
|
|
if (red == max)
|
|
h = (green - blue) / (float) delta;
|
|
else if (green == max)
|
|
h = 2 + (blue - red) / (float) delta;
|
|
else
|
|
h = 4 + (red - green) / (float) delta;
|
|
|
|
h = h * 42.5;
|
|
|
|
if (h < 0)
|
|
h += 255;
|
|
else if (h > 255)
|
|
h -= 255;
|
|
}
|
|
|
|
*r = h;
|
|
*g = l;
|
|
*b = s;
|
|
}
|
|
|
|
|
|
/* Just compute the luminosity component. */
|
|
int
|
|
rgb_to_l (int red,
|
|
int green,
|
|
int blue)
|
|
{
|
|
int min, max;
|
|
|
|
if (red > green)
|
|
{
|
|
if (red > blue)
|
|
max = red;
|
|
else
|
|
max = blue;
|
|
|
|
if (green < blue)
|
|
min = green;
|
|
else
|
|
min = blue;
|
|
}
|
|
else
|
|
{
|
|
if (green > blue)
|
|
max = green;
|
|
else
|
|
max = blue;
|
|
|
|
if (red < blue)
|
|
min = red;
|
|
else
|
|
min = blue;
|
|
}
|
|
|
|
return (max + min) / 2.0;
|
|
}
|
|
|
|
|
|
static int
|
|
hls_value (float n1,
|
|
float n2,
|
|
float hue)
|
|
{
|
|
float value;
|
|
|
|
if (hue > 255)
|
|
hue -= 255;
|
|
else if (hue < 0)
|
|
hue += 255;
|
|
if (hue < 42.5)
|
|
value = n1 + (n2 - n1) * (hue / 42.5);
|
|
else if (hue < 127.5)
|
|
value = n2;
|
|
else if (hue < 170)
|
|
value = n1 + (n2 - n1) * ((170 - hue) / 42.5);
|
|
else
|
|
value = n1;
|
|
|
|
return (int) (value * 255);
|
|
}
|
|
|
|
|
|
void
|
|
hls_to_rgb (int *h,
|
|
int *l,
|
|
int *s)
|
|
{
|
|
float hue, lightness, saturation;
|
|
float m1, m2;
|
|
|
|
hue = *h;
|
|
lightness = *l;
|
|
saturation = *s;
|
|
|
|
if (saturation == 0)
|
|
{
|
|
/* achromatic case */
|
|
*h = lightness;
|
|
*l = lightness;
|
|
*s = lightness;
|
|
}
|
|
else
|
|
{
|
|
if (lightness < 128)
|
|
m2 = (lightness * (255 + saturation)) / 65025.0;
|
|
else
|
|
m2 = (lightness + saturation - (lightness * saturation)/255.0) / 255.0;
|
|
|
|
m1 = (lightness / 127.5) - m2;
|
|
|
|
/* chromatic case */
|
|
*h = hls_value (m1, m2, hue + 85);
|
|
*l = hls_value (m1, m2, hue);
|
|
*s = hls_value (m1, m2, hue - 85);
|
|
}
|
|
}
|
|
|
|
|
|
/************************************/
|
|
/* apply layer modes */
|
|
/************************************/
|
|
|
|
int
|
|
apply_layer_mode (unsigned char *src1,
|
|
unsigned char *src2,
|
|
unsigned char **dest,
|
|
int x,
|
|
int y,
|
|
int opacity,
|
|
int length,
|
|
LayerModeEffects mode,
|
|
int bytes1, /* bytes */
|
|
int bytes2, /* bytes */
|
|
int has_alpha1, /* has alpha */
|
|
int has_alpha2, /* has alpha */
|
|
int *mode_affect)
|
|
{
|
|
int combine;
|
|
|
|
if (!has_alpha1 && !has_alpha2)
|
|
combine = COMBINE_INTEN_INTEN;
|
|
else if (!has_alpha1 && has_alpha2)
|
|
combine = COMBINE_INTEN_INTEN_A;
|
|
else if (has_alpha1 && !has_alpha2)
|
|
combine = COMBINE_INTEN_A_INTEN;
|
|
else
|
|
combine = COMBINE_INTEN_A_INTEN_A;
|
|
|
|
/* assumes we're applying src2 TO src1 */
|
|
switch (mode)
|
|
{
|
|
case NORMAL_MODE:
|
|
*dest = src2;
|
|
break;
|
|
|
|
case DISSOLVE_MODE:
|
|
/* Since dissolve requires an alpha channels... */
|
|
if (! has_alpha2)
|
|
add_alpha_pixels (src2, *dest, length, bytes2);
|
|
|
|
dissolve_pixels (src2, *dest, x, y, opacity, length, bytes2,
|
|
((has_alpha2) ? bytes2 : bytes2 + 1), has_alpha2);
|
|
combine = (has_alpha1) ? COMBINE_INTEN_A_INTEN_A : COMBINE_INTEN_INTEN_A;
|
|
break;
|
|
|
|
case MULTIPLY_MODE:
|
|
multiply_pixels (src1, src2, *dest, length, bytes1, bytes2, has_alpha1, has_alpha2);
|
|
break;
|
|
|
|
case DIVIDE_MODE:
|
|
divide_pixels (src1, src2, *dest, length, bytes1, bytes2, has_alpha1, has_alpha2);
|
|
break;
|
|
|
|
case SCREEN_MODE:
|
|
screen_pixels (src1, src2, *dest, length, bytes1, bytes2, has_alpha1, has_alpha2);
|
|
break;
|
|
|
|
case OVERLAY_MODE:
|
|
overlay_pixels (src1, src2, *dest, length, bytes1, bytes2, has_alpha1, has_alpha2);
|
|
break;
|
|
|
|
case DIFFERENCE_MODE:
|
|
difference_pixels (src1, src2, *dest, length, bytes1, bytes2, has_alpha1, has_alpha2);
|
|
break;
|
|
|
|
case ADDITION_MODE:
|
|
add_pixels (src1, src2, *dest, length, bytes1, bytes2, has_alpha1, has_alpha2);
|
|
break;
|
|
|
|
case SUBTRACT_MODE:
|
|
subtract_pixels (src1, src2, *dest, length, bytes1, bytes2, has_alpha1, has_alpha2);
|
|
break;
|
|
|
|
case DARKEN_ONLY_MODE:
|
|
darken_pixels (src1, src2, *dest, length, bytes1, bytes2, has_alpha1, has_alpha2);
|
|
break;
|
|
|
|
case LIGHTEN_ONLY_MODE:
|
|
lighten_pixels (src1, src2, *dest, length, bytes1, bytes2, has_alpha1, has_alpha2);
|
|
break;
|
|
|
|
case HUE_MODE: case SATURATION_MODE: case VALUE_MODE:
|
|
/* only works on RGB color images */
|
|
if (bytes1 > 2)
|
|
hsv_only_pixels (src1, src2, *dest, mode, length, bytes1, bytes2, has_alpha1, has_alpha2);
|
|
else
|
|
*dest = src2;
|
|
break;
|
|
|
|
case COLOR_MODE:
|
|
/* only works on RGB color images */
|
|
if (bytes1 > 2)
|
|
color_only_pixels (src1, src2, *dest, mode, length, bytes1, bytes2, has_alpha1, has_alpha2);
|
|
else
|
|
*dest = src2;
|
|
break;
|
|
|
|
case BEHIND_MODE:
|
|
*dest = src2;
|
|
if (has_alpha1)
|
|
combine = BEHIND_INTEN;
|
|
else
|
|
combine = NO_COMBINATION;
|
|
break;
|
|
|
|
case REPLACE_MODE:
|
|
*dest = src2;
|
|
combine = REPLACE_INTEN;
|
|
break;
|
|
|
|
case ERASE_MODE:
|
|
*dest = src2;
|
|
/* If both sources have alpha channels, call erase function.
|
|
* Otherwise, just combine in the normal manner
|
|
*/
|
|
combine = (has_alpha1 && has_alpha2) ? ERASE_INTEN : combine;
|
|
break;
|
|
|
|
case ANTI_ERASE_MODE:
|
|
*dest = src2;
|
|
combine = (has_alpha1 && has_alpha2) ? ANTI_ERASE_INTEN : combine;
|
|
break;
|
|
|
|
default :
|
|
break;
|
|
}
|
|
|
|
/* Determine whether the alpha channel of the destination can be affected
|
|
* by the specified mode--This keeps consistency with varying opacities
|
|
*/
|
|
*mode_affect = layer_modes[mode].affect_alpha;
|
|
|
|
return combine;
|
|
}
|
|
|
|
|
|
int
|
|
apply_indexed_layer_mode (unsigned char *src1,
|
|
unsigned char *src2,
|
|
unsigned char **dest,
|
|
LayerModeEffects mode,
|
|
int has_alpha1, /* has alpha */
|
|
int has_alpha2) /* has alpha */
|
|
{
|
|
int combine;
|
|
|
|
if (!has_alpha1 && !has_alpha2)
|
|
combine = COMBINE_INDEXED_INDEXED;
|
|
else if (!has_alpha1 && has_alpha2)
|
|
combine = COMBINE_INDEXED_INDEXED_A;
|
|
else if (has_alpha1 && has_alpha2)
|
|
combine = COMBINE_INDEXED_A_INDEXED_A;
|
|
else
|
|
combine = NO_COMBINATION;
|
|
|
|
/* assumes we're applying src2 TO src1 */
|
|
switch (mode)
|
|
{
|
|
case REPLACE_MODE:
|
|
*dest = src2;
|
|
combine = REPLACE_INDEXED;
|
|
break;
|
|
|
|
case BEHIND_MODE:
|
|
*dest = src2;
|
|
if (has_alpha1)
|
|
combine = BEHIND_INDEXED;
|
|
else
|
|
combine = NO_COMBINATION;
|
|
break;
|
|
|
|
case ERASE_MODE:
|
|
*dest = src2;
|
|
/* If both sources have alpha channels, call erase function.
|
|
* Otherwise, just combine in the normal manner
|
|
*/
|
|
combine = (has_alpha1 && has_alpha2) ? ERASE_INDEXED : combine;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return combine;
|
|
}
|
|
|
|
static void
|
|
apply_layer_mode_replace (unsigned char *src1,
|
|
unsigned char *src2,
|
|
unsigned char *dest,
|
|
unsigned char *mask,
|
|
int x,
|
|
int y,
|
|
int opacity,
|
|
int length,
|
|
int bytes1, /* bytes */
|
|
int bytes2, /* bytes */
|
|
int *affect)
|
|
{
|
|
replace_pixels (src1, src2, dest, mask, length, opacity, affect, bytes1, bytes2);
|
|
}
|