mirror of
https://github.com/aseprite/aseprite.git
synced 2025-04-10 12:44:53 +00:00
476 lines
14 KiB
C
476 lines
14 KiB
C
/* ASE - Allegro Sprite Editor
|
|
* Copyright (C) 2001-2011 David Capello
|
|
*
|
|
* 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 "filters/neighboring_pixels.h"
|
|
#include "modules/palettes.h"
|
|
#include "raster/palette.h"
|
|
#include "raster/rgbmap.h"
|
|
#include "raster/sprite.h"
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Ink Processing
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
#define DEFINE_INK_PROCESSING(addresses_define, \
|
|
addresses_initialize, \
|
|
addresses_increment, \
|
|
processing) \
|
|
addresses_define \
|
|
register int x; \
|
|
\
|
|
/* Use mask */ \
|
|
if (loop->useMask()) { \
|
|
Point maskOrigin(loop->getMaskOrigin()); \
|
|
\
|
|
if ((y < maskOrigin.y) || (y >= maskOrigin.y+loop->getMask()->h)) \
|
|
return; \
|
|
\
|
|
if (x1 < maskOrigin.x) \
|
|
x1 = maskOrigin.x; \
|
|
\
|
|
if (x2 > maskOrigin.x+loop->getMask()->w-1) \
|
|
x2 = maskOrigin.x+loop->getMask()->w-1; \
|
|
\
|
|
if (Image* bitmap = loop->getMask()->bitmap) { \
|
|
addresses_initialize; \
|
|
for (x=x1; x<=x2; ++x) { \
|
|
if (bitmap->getpixel(x-maskOrigin.x, y-maskOrigin.y)) \
|
|
processing; \
|
|
\
|
|
addresses_increment; \
|
|
} \
|
|
return; \
|
|
} \
|
|
} \
|
|
\
|
|
addresses_initialize; \
|
|
for (x=x1; x<=x2; ++x) { \
|
|
processing; \
|
|
addresses_increment; \
|
|
}
|
|
|
|
#define DEFINE_INK_PROCESSING_DST(Traits, processing) \
|
|
DEFINE_INK_PROCESSING(register Traits::address_t dst_address; , \
|
|
dst_address = ((Traits::address_t*)loop->getDstImage()->line)[y]+x1; , \
|
|
++dst_address , \
|
|
processing)
|
|
|
|
#define DEFINE_INK_PROCESSING_SRCDST(Traits, processing) \
|
|
DEFINE_INK_PROCESSING(register Traits::address_t src_address; \
|
|
register Traits::address_t dst_address; , \
|
|
src_address = ((Traits::address_t*)loop->getSrcImage()->line)[y]+x1; \
|
|
dst_address = ((Traits::address_t*)loop->getDstImage()->line)[y]+x1; , \
|
|
++src_address; \
|
|
++dst_address; , \
|
|
processing)
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Opaque Ink
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
static void ink_hline32_opaque(int x1, int y, int x2, IToolLoop* loop)
|
|
{
|
|
int c = loop->getPrimaryColor();
|
|
|
|
DEFINE_INK_PROCESSING_DST
|
|
(RgbTraits,
|
|
*dst_address = c );
|
|
}
|
|
|
|
static void ink_hline16_opaque(int x1, int y, int x2, IToolLoop* loop)
|
|
{
|
|
int c = loop->getPrimaryColor();
|
|
|
|
DEFINE_INK_PROCESSING_DST
|
|
(GrayscaleTraits,
|
|
*dst_address = c );
|
|
}
|
|
|
|
static void ink_hline8_opaque(int x1, int y, int x2, IToolLoop* loop)
|
|
{
|
|
int c = loop->getPrimaryColor();
|
|
|
|
DEFINE_INK_PROCESSING_DST
|
|
(IndexedTraits,
|
|
*dst_address = c );
|
|
|
|
/* memset(((uint8_t**)data->dst_image->line)[y]+x1, data->color, x2-x1+1); */
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Transparent Ink
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
static void ink_hline32_transparent(int x1, int y, int x2, IToolLoop* loop)
|
|
{
|
|
int color = loop->getPrimaryColor();
|
|
int opacity = loop->getOpacity();
|
|
|
|
DEFINE_INK_PROCESSING_SRCDST
|
|
(RgbTraits,
|
|
*dst_address = _rgba_blend_normal(*src_address, color, opacity));
|
|
}
|
|
|
|
static void ink_hline16_transparent(int x1, int y, int x2, IToolLoop* loop)
|
|
{
|
|
int color = loop->getPrimaryColor();
|
|
int opacity = loop->getOpacity();
|
|
|
|
DEFINE_INK_PROCESSING_SRCDST
|
|
(GrayscaleTraits,
|
|
*dst_address = _graya_blend_normal(*src_address, color, opacity));
|
|
}
|
|
|
|
static void ink_hline8_transparent(int x1, int y, int x2, IToolLoop* loop)
|
|
{
|
|
Palette* pal = get_current_palette();
|
|
RgbMap* rgbmap = loop->getSprite()->getRgbMap();
|
|
uint32_t c;
|
|
uint32_t tc = pal->getEntry(loop->getPrimaryColor());
|
|
int opacity = loop->getOpacity();
|
|
|
|
DEFINE_INK_PROCESSING_SRCDST
|
|
(IndexedTraits,
|
|
{
|
|
c = _rgba_blend_normal(pal->getEntry(*src_address), tc, opacity);
|
|
*dst_address = rgbmap->mapColor(_rgba_getr(c),
|
|
_rgba_getg(c),
|
|
_rgba_getb(c));
|
|
});
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Blur Ink
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
namespace {
|
|
struct BlurGetPixelsDelegateRgba
|
|
{
|
|
int count, r, g, b, a;
|
|
|
|
void reset() { count = r = g = b = a = 0; }
|
|
|
|
void operator()(RgbTraits::pixel_t color)
|
|
{
|
|
if (_rgba_geta(color) != 0) {
|
|
r += _rgba_getr(color);
|
|
g += _rgba_getg(color);
|
|
b += _rgba_getb(color);
|
|
a += _rgba_geta(color);
|
|
++count;
|
|
}
|
|
}
|
|
};
|
|
|
|
struct BlurGetPixelsDelegateGrayscale
|
|
{
|
|
int count, v, a;
|
|
|
|
void reset() { count = v = a = 0; }
|
|
|
|
void operator()(GrayscaleTraits::pixel_t color)
|
|
{
|
|
if (_graya_geta(color) > 0) {
|
|
v += _graya_getv(color);
|
|
a += _graya_geta(color);
|
|
++count;
|
|
}
|
|
}
|
|
};
|
|
|
|
struct BlurGetPixelsDelegateIndexed
|
|
{
|
|
const Palette* pal;
|
|
int count, r, g, b, a;
|
|
|
|
BlurGetPixelsDelegateIndexed(const Palette* pal) : pal(pal) { }
|
|
|
|
void reset() { count = r = g = b = a = 0; }
|
|
|
|
void operator()(IndexedTraits::pixel_t color)
|
|
{
|
|
a += (color == 0 ? 0: 255);
|
|
|
|
color = pal->getEntry(color);
|
|
r += _rgba_getr(color);
|
|
g += _rgba_getg(color);
|
|
b += _rgba_getb(color);
|
|
count++;
|
|
}
|
|
};
|
|
};
|
|
|
|
static void ink_hline32_blur(int x1, int y, int x2, IToolLoop* loop)
|
|
{
|
|
int opacity = loop->getOpacity();
|
|
TiledMode tiledMode = loop->getTiledMode();
|
|
const Image* src = loop->getSrcImage();
|
|
BlurGetPixelsDelegateRgba delegate;
|
|
|
|
DEFINE_INK_PROCESSING_SRCDST
|
|
(RgbTraits,
|
|
{
|
|
delegate.reset();
|
|
get_neighboring_pixels<RgbTraits>(src, x, y, 3, 3, 1, 1, tiledMode, delegate);
|
|
|
|
if (delegate.count > 0) {
|
|
delegate.r /= delegate.count;
|
|
delegate.g /= delegate.count;
|
|
delegate.b /= delegate.count;
|
|
delegate.a /= 9;
|
|
|
|
RgbTraits::pixel_t c = *src_address;
|
|
delegate.r = _rgba_getr(c) + (delegate.r-_rgba_getr(c)) * opacity / 255;
|
|
delegate.g = _rgba_getg(c) + (delegate.g-_rgba_getg(c)) * opacity / 255;
|
|
delegate.b = _rgba_getb(c) + (delegate.b-_rgba_getb(c)) * opacity / 255;
|
|
delegate.a = _rgba_geta(c) + (delegate.a-_rgba_geta(c)) * opacity / 255;
|
|
|
|
*dst_address = _rgba(delegate.r, delegate.g, delegate.b, delegate.a);
|
|
}
|
|
else {
|
|
*dst_address = *src_address;
|
|
}
|
|
});
|
|
}
|
|
|
|
static void ink_hline16_blur(int x1, int y, int x2, IToolLoop* loop)
|
|
{
|
|
int opacity = loop->getOpacity();
|
|
TiledMode tiledMode = loop->getTiledMode();
|
|
const Image* src = loop->getSrcImage();
|
|
BlurGetPixelsDelegateGrayscale delegate;
|
|
|
|
DEFINE_INK_PROCESSING_SRCDST
|
|
(GrayscaleTraits,
|
|
{
|
|
delegate.reset();
|
|
get_neighboring_pixels<GrayscaleTraits>(src, x, y, 3, 3, 1, 1, tiledMode, delegate);
|
|
|
|
if (delegate.count > 0) {
|
|
delegate.v /= delegate.count;
|
|
delegate.a /= 9;
|
|
|
|
GrayscaleTraits::pixel_t c = *src_address;
|
|
delegate.v = _graya_getv(c) + (delegate.v-_graya_getv(c)) * opacity / 255;
|
|
delegate.a = _graya_geta(c) + (delegate.a-_graya_geta(c)) * opacity / 255;
|
|
|
|
*dst_address = _graya(delegate.v, delegate.a);
|
|
}
|
|
else {
|
|
*dst_address = *src_address;
|
|
}
|
|
});
|
|
}
|
|
|
|
static void ink_hline8_blur(int x1, int y, int x2, IToolLoop* loop)
|
|
{
|
|
const Palette *pal = get_current_palette();
|
|
RgbMap* rgbmap = loop->getSprite()->getRgbMap();
|
|
int opacity = loop->getOpacity();
|
|
TiledMode tiledMode = loop->getTiledMode();
|
|
const Image* src = loop->getSrcImage();
|
|
BlurGetPixelsDelegateIndexed delegate(pal);
|
|
|
|
DEFINE_INK_PROCESSING_SRCDST
|
|
(IndexedTraits,
|
|
{
|
|
delegate.reset();
|
|
get_neighboring_pixels<IndexedTraits>(src, x, y, 3, 3, 1, 1, tiledMode, delegate);
|
|
|
|
if (delegate.count > 0 && delegate.a/9 >= 128) {
|
|
delegate.r /= delegate.count;
|
|
delegate.g /= delegate.count;
|
|
delegate.b /= delegate.count;
|
|
|
|
IndexedTraits::pixel_t c = *src_address;
|
|
delegate.r = _rgba_getr(c) + (delegate.r-_rgba_getr(c)) * opacity / 255;
|
|
delegate.g = _rgba_getg(c) + (delegate.g-_rgba_getg(c)) * opacity / 255;
|
|
delegate.b = _rgba_getb(c) + (delegate.b-_rgba_getb(c)) * opacity / 255;
|
|
|
|
*dst_address = rgbmap->mapColor(delegate.r, delegate.g, delegate.b);
|
|
}
|
|
else {
|
|
*dst_address = *src_address;
|
|
}
|
|
});
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Replace Ink
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
static void ink_hline32_replace(int x1, int y, int x2, IToolLoop* loop)
|
|
{
|
|
uint32_t color1 = loop->getPrimaryColor();
|
|
uint32_t color2 = loop->getSecondaryColor();
|
|
int opacity = loop->getOpacity();
|
|
|
|
DEFINE_INK_PROCESSING_SRCDST
|
|
(RgbTraits,
|
|
if (*src_address == color1) {
|
|
*dst_address = _rgba_blend_normal(*src_address, color2, opacity);
|
|
});
|
|
}
|
|
|
|
static void ink_hline16_replace(int x1, int y, int x2, IToolLoop* loop)
|
|
{
|
|
int color1 = loop->getPrimaryColor();
|
|
int color2 = loop->getSecondaryColor();
|
|
int opacity = loop->getOpacity();
|
|
|
|
DEFINE_INK_PROCESSING_SRCDST
|
|
(GrayscaleTraits,
|
|
if (*src_address == color1) {
|
|
*dst_address = _graya_blend_normal(*src_address, color2, opacity);
|
|
});
|
|
}
|
|
|
|
static void ink_hline8_replace(int x1, int y, int x2, IToolLoop* loop)
|
|
{
|
|
int color1 = loop->getPrimaryColor();
|
|
const Palette *pal = get_current_palette();
|
|
RgbMap* rgbmap = loop->getSprite()->getRgbMap();
|
|
uint32_t c;
|
|
uint32_t tc = pal->getEntry(loop->getSecondaryColor());
|
|
int opacity = loop->getOpacity();
|
|
|
|
DEFINE_INK_PROCESSING_SRCDST
|
|
(IndexedTraits,
|
|
if (*src_address == color1) {
|
|
c = _rgba_blend_normal(pal->getEntry(*src_address), tc, opacity);
|
|
*dst_address = rgbmap->mapColor(_rgba_getr(c),
|
|
_rgba_getg(c),
|
|
_rgba_getb(c));
|
|
});
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Jumble Ink
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
#define JUMBLE_XY_IN_UV() \
|
|
u = x + (rand() % 3)-1 - speed.x; \
|
|
v = y + (rand() % 3)-1 - speed.y; \
|
|
\
|
|
if (tiled & TILED_X_AXIS) { \
|
|
if (u < 0) \
|
|
u = loop->getSrcImage()->w - (-(u+1) % loop->getSrcImage()->w) - 1; \
|
|
else if (u >= loop->getSrcImage()->w) \
|
|
u %= loop->getSrcImage()->w; \
|
|
} \
|
|
else { \
|
|
u = MID(0, u, loop->getSrcImage()->w-1); \
|
|
} \
|
|
\
|
|
if (tiled & TILED_Y_AXIS) { \
|
|
if (v < 0) \
|
|
v = loop->getSrcImage()->h - (-(v+1) % loop->getSrcImage()->h) - 1; \
|
|
else if (v >= loop->getSrcImage()->h) \
|
|
v %= loop->getSrcImage()->h; \
|
|
} \
|
|
else { \
|
|
v = MID(0, v, loop->getSrcImage()->h-1); \
|
|
} \
|
|
color = image_getpixel(loop->getSrcImage(), u, v);
|
|
|
|
static void ink_hline32_jumble(int x1, int y, int x2, IToolLoop* loop)
|
|
{
|
|
int opacity = loop->getOpacity();
|
|
Point speed(loop->getSpeed() / 4);
|
|
TiledMode tiled = loop->getTiledMode();
|
|
int u, v, color;
|
|
|
|
DEFINE_INK_PROCESSING_SRCDST
|
|
(RgbTraits,
|
|
{
|
|
JUMBLE_XY_IN_UV();
|
|
*dst_address = _rgba_blend_merge(*src_address, color, opacity);
|
|
}
|
|
);
|
|
}
|
|
|
|
static void ink_hline16_jumble(int x1, int y, int x2, IToolLoop* loop)
|
|
{
|
|
int opacity = loop->getOpacity();
|
|
Point speed(loop->getSpeed() / 4);
|
|
TiledMode tiled = loop->getTiledMode();
|
|
int u, v, color;
|
|
|
|
DEFINE_INK_PROCESSING_SRCDST
|
|
(GrayscaleTraits,
|
|
{
|
|
JUMBLE_XY_IN_UV();
|
|
*dst_address = _graya_blend_merge(*src_address, color, opacity);
|
|
}
|
|
);
|
|
}
|
|
|
|
static void ink_hline8_jumble(int x1, int y, int x2, IToolLoop* loop)
|
|
{
|
|
const Palette *pal = get_current_palette();
|
|
const RgbMap* rgbmap = loop->getSprite()->getRgbMap();
|
|
uint32_t c, tc;
|
|
int opacity = loop->getOpacity();
|
|
Point speed(loop->getSpeed() / 4);
|
|
TiledMode tiled = loop->getTiledMode();
|
|
int u, v, color;
|
|
|
|
DEFINE_INK_PROCESSING_SRCDST
|
|
(IndexedTraits,
|
|
{
|
|
JUMBLE_XY_IN_UV();
|
|
|
|
tc = color != 0 ? pal->getEntry(color): 0;
|
|
c = _rgba_blend_merge(*src_address != 0 ? pal->getEntry(*src_address): 0,
|
|
tc, opacity);
|
|
|
|
if (_rgba_geta(c) >= 128)
|
|
*dst_address = rgbmap->mapColor(_rgba_getr(c),
|
|
_rgba_getg(c),
|
|
_rgba_getb(c));
|
|
else
|
|
*dst_address = 0;
|
|
}
|
|
);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
enum {
|
|
INK_OPAQUE,
|
|
INK_TRANSPARENT,
|
|
INK_BLUR,
|
|
INK_REPLACE,
|
|
INK_JUMBLE,
|
|
MAX_INKS
|
|
};
|
|
|
|
static AlgoHLine ink_processing[][3] =
|
|
{
|
|
#define DEF_INK(name) \
|
|
{ (AlgoHLine)ink_hline32_##name, \
|
|
(AlgoHLine)ink_hline16_##name, \
|
|
(AlgoHLine)ink_hline8_##name }
|
|
|
|
DEF_INK(opaque),
|
|
DEF_INK(transparent),
|
|
DEF_INK(blur),
|
|
DEF_INK(replace),
|
|
DEF_INK(jumble)
|
|
};
|