mirror of https://github.com/aseprite/aseprite.git
546 lines
11 KiB
C++
546 lines
11 KiB
C++
/* ASE - Allegro Sprite Editor
|
|
* Copyright (C) 2001-2009 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 "config.h"
|
|
|
|
#include <allegro.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "jinete/jlist.h"
|
|
|
|
#include "core/cfg.h"
|
|
#include "core/dirs.h"
|
|
#include "effect/convmatr.h"
|
|
#include "effect/effect.h"
|
|
#include "modules/palettes.h"
|
|
#include "modules/tools.h"
|
|
#include "raster/image.h"
|
|
#include "raster/palette.h"
|
|
#include "util/filetoks.h"
|
|
|
|
/* TODO warning: this number could be dangerous for big filters */
|
|
#define PRECISION (256)
|
|
|
|
static struct { /* TODO warning: not thread safe */
|
|
JList matrices;
|
|
ConvMatr *convmatr;
|
|
tiled_t tiled;
|
|
unsigned char **lines;
|
|
} data;
|
|
|
|
void init_convolution_matrix()
|
|
{
|
|
data.matrices = jlist_new();
|
|
data.convmatr = NULL;
|
|
data.tiled = TILED_NONE;
|
|
data.lines = NULL;
|
|
|
|
reload_matrices_stock();
|
|
}
|
|
|
|
void exit_convolution_matrix()
|
|
{
|
|
clean_matrices_stock();
|
|
jlist_free(data.matrices);
|
|
|
|
if (data.lines != NULL)
|
|
jfree(data.lines);
|
|
}
|
|
|
|
ConvMatr *convmatr_new(int w, int h)
|
|
{
|
|
ConvMatr *convmatr;
|
|
int c, size;
|
|
|
|
convmatr = (ConvMatr *)jnew(ConvMatr, 1);
|
|
if (!convmatr)
|
|
return NULL;
|
|
|
|
convmatr->name = NULL;
|
|
convmatr->w = w;
|
|
convmatr->h = h;
|
|
convmatr->cx = convmatr->w/2;
|
|
convmatr->cy = convmatr->h/2;
|
|
|
|
size = convmatr->w * convmatr->h;
|
|
|
|
convmatr->data = (int*)jmalloc(sizeof(int) * size);
|
|
if (!convmatr->data) {
|
|
convmatr_free(convmatr);
|
|
return NULL;
|
|
}
|
|
|
|
for (c=0; c<size; c++)
|
|
convmatr->data[c] = 0;
|
|
|
|
convmatr->div = PRECISION;
|
|
convmatr->bias = 0;
|
|
|
|
return convmatr;
|
|
}
|
|
|
|
ConvMatr *convmatr_new_string(const char *format)
|
|
{
|
|
/* TODO */
|
|
return NULL;
|
|
}
|
|
|
|
void convmatr_free(ConvMatr *convmatr)
|
|
{
|
|
if (convmatr->name)
|
|
jfree(convmatr->name);
|
|
|
|
if (convmatr->data)
|
|
jfree(convmatr->data);
|
|
|
|
jfree(convmatr);
|
|
}
|
|
|
|
void set_convmatr(ConvMatr *convmatr)
|
|
{
|
|
data.convmatr = convmatr;
|
|
data.tiled = get_tiled_mode();
|
|
|
|
if (data.lines != NULL)
|
|
jfree(data.lines);
|
|
|
|
data.lines = (unsigned char**)jmalloc(sizeof(unsigned char*) * convmatr->h);
|
|
}
|
|
|
|
ConvMatr* get_convmatr()
|
|
{
|
|
return data.convmatr;
|
|
}
|
|
|
|
ConvMatr* get_convmatr_by_name(const char *name)
|
|
{
|
|
ConvMatr* convmatr;
|
|
JLink link;
|
|
|
|
JI_LIST_FOR_EACH(data.matrices, link) {
|
|
convmatr = reinterpret_cast<ConvMatr*>(link->data);
|
|
if (strcmp(convmatr->name, name) == 0)
|
|
return convmatr;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void reload_matrices_stock()
|
|
{
|
|
#define READ_TOK() { \
|
|
if (!tok_read(f, buf, leavings, sizeof (leavings))) \
|
|
break; \
|
|
}
|
|
|
|
#define READ_INT(var) { \
|
|
READ_TOK(); \
|
|
var = ustrtol(buf, NULL, 10); \
|
|
}
|
|
|
|
const char *names[] = { "convmatr.usr",
|
|
"convmatr.gen",
|
|
"convmatr.def", NULL };
|
|
char *s, buf[256], leavings[4096];
|
|
int i, c, w, h, div, bias;
|
|
DIRS *dirs, *dir;
|
|
ConvMatr *convmatr;
|
|
FILE *f;
|
|
char *name;
|
|
|
|
clean_matrices_stock();
|
|
|
|
for (i=0; names[i]; i++) {
|
|
dirs = filename_in_datadir(names[i]);
|
|
|
|
for (dir=dirs; dir; dir=dir->next) {
|
|
/* open matrices stock file */
|
|
f = fopen(dir->path, "r");
|
|
if (!f)
|
|
continue;
|
|
|
|
tok_reset_line_num();
|
|
|
|
name = NULL;
|
|
convmatr = NULL;
|
|
strcpy(leavings, "");
|
|
|
|
/* read the matrix name */
|
|
while (tok_read (f, buf, leavings, sizeof (leavings))) {
|
|
/* name of the matrix */
|
|
name = jstrdup(buf);
|
|
|
|
/* width and height */
|
|
READ_INT(w);
|
|
READ_INT(h);
|
|
|
|
if ((w <= 0) || (w > 32) ||
|
|
(h <= 0) || (h > 32))
|
|
break;
|
|
|
|
/* create the matrix data */
|
|
convmatr = convmatr_new(w, h);
|
|
if (!convmatr)
|
|
break;
|
|
|
|
/* centre */
|
|
READ_INT(convmatr->cx);
|
|
READ_INT(convmatr->cy);
|
|
|
|
if ((convmatr->cx < 0) || (convmatr->cx >= w) ||
|
|
(convmatr->cy < 0) || (convmatr->cy >= h))
|
|
break;
|
|
|
|
/* data */
|
|
READ_TOK(); /* jump the `{' char */
|
|
if (*buf != '{')
|
|
break;
|
|
|
|
c = 0;
|
|
div = 0;
|
|
for (c=0; c<w*h; c++) {
|
|
READ_TOK();
|
|
convmatr->data[c] = ustrtod(buf, NULL) * PRECISION;
|
|
div += convmatr->data[c];
|
|
}
|
|
|
|
READ_TOK(); /* jump the `}' char */
|
|
if (*buf != '}')
|
|
break;
|
|
|
|
if (div > 0)
|
|
bias = 0;
|
|
else if (div == 0) {
|
|
div = PRECISION;
|
|
bias = 128;
|
|
}
|
|
else {
|
|
div = ABS(div);
|
|
bias = 255;
|
|
}
|
|
|
|
/* div */
|
|
READ_TOK();
|
|
if (ustricmp (buf, "auto") != 0)
|
|
div = ustrtod(buf, NULL) * PRECISION;
|
|
|
|
convmatr->div = div;
|
|
|
|
/* bias */
|
|
READ_TOK();
|
|
if (ustricmp (buf, "auto") != 0)
|
|
bias = ustrtod(buf, NULL);
|
|
|
|
convmatr->bias = bias;
|
|
|
|
/* target */
|
|
READ_TOK();
|
|
|
|
convmatr->default_target = 0;
|
|
for (s=buf; *s; s++) {
|
|
switch (*s) {
|
|
case 'r': convmatr->default_target |= TARGET_RED_CHANNEL; break;
|
|
case 'g': convmatr->default_target |= TARGET_GREEN_CHANNEL; break;
|
|
case 'b': convmatr->default_target |= TARGET_BLUE_CHANNEL; break;
|
|
case 'a': convmatr->default_target |= TARGET_ALPHA_CHANNEL; break;
|
|
}
|
|
}
|
|
|
|
if ((convmatr->default_target & (TARGET_RED_CHANNEL |
|
|
TARGET_GREEN_CHANNEL |
|
|
TARGET_BLUE_CHANNEL)) != 0) {
|
|
convmatr->default_target |= TARGET_GRAY_CHANNEL;
|
|
}
|
|
|
|
/* name */
|
|
convmatr->name = name;
|
|
|
|
/* insert the new matrix in the list */
|
|
jlist_append(data.matrices, convmatr);
|
|
|
|
name = NULL;
|
|
convmatr = NULL;
|
|
}
|
|
|
|
/* destroy the last invalid matrix in case of error */
|
|
if (name)
|
|
jfree(name);
|
|
|
|
if (convmatr)
|
|
convmatr_free(convmatr);
|
|
|
|
/* close the file */
|
|
fclose(f);
|
|
}
|
|
|
|
dirs_free(dirs);
|
|
}
|
|
}
|
|
|
|
void clean_matrices_stock()
|
|
{
|
|
JLink link;
|
|
|
|
JI_LIST_FOR_EACH(data.matrices, link)
|
|
convmatr_free(reinterpret_cast<ConvMatr*>(link->data));
|
|
|
|
jlist_clear(data.matrices);
|
|
}
|
|
|
|
JList get_convmatr_stock()
|
|
{
|
|
return data.matrices;
|
|
}
|
|
|
|
#define GET_CONVMATR_DATA(Traits, do_job) \
|
|
div = matrix->div; \
|
|
mdata = matrix->data; \
|
|
\
|
|
GET_MATRIX_DATA \
|
|
(Traits::pixel_t, \
|
|
src, src_address, \
|
|
matrix->w, matrix->h, \
|
|
matrix->cx, matrix->cy, \
|
|
data.tiled, \
|
|
if (*mdata) { \
|
|
color = *src_address; \
|
|
do_job; \
|
|
} \
|
|
mdata++; \
|
|
); \
|
|
\
|
|
color = image_getpixel_fast<Traits>(src, x, y); \
|
|
if (div == 0) { \
|
|
*(dst_address++) = color; \
|
|
continue; \
|
|
}
|
|
|
|
void apply_convolution_matrix4(Effect *effect)
|
|
{
|
|
ConvMatr *matrix = data.convmatr;
|
|
Image *src = effect->src;
|
|
Image *dst = effect->dst;
|
|
ase_uint32 *src_address;
|
|
ase_uint32 *dst_address;
|
|
int x, y, dx, dy, color;
|
|
int getx, gety, addx, addy;
|
|
int r, g, b, a;
|
|
int *mdata;
|
|
int w, div;
|
|
|
|
if (matrix) {
|
|
w = effect->x+effect->w;
|
|
y = effect->y+effect->row;
|
|
dst_address = ((ase_uint32 **)dst->line)[y]+effect->x;
|
|
|
|
for (x=effect->x; x<w; x++) {
|
|
/* avoid the unmask region */
|
|
if (effect->mask_address) {
|
|
if (!((*effect->mask_address) & (1<<effect->d.rem))) {
|
|
dst_address++;
|
|
_image_bitmap_next_bit(effect->d, effect->mask_address);
|
|
continue;
|
|
}
|
|
else
|
|
_image_bitmap_next_bit(effect->d, effect->mask_address);
|
|
}
|
|
|
|
r = g = b = a = 0;
|
|
|
|
GET_CONVMATR_DATA
|
|
(RgbTraits,
|
|
if (_rgba_geta(color) == 0)
|
|
div -= *mdata;
|
|
else {
|
|
r += _rgba_getr(color) * (*mdata);
|
|
g += _rgba_getg(color) * (*mdata);
|
|
b += _rgba_getb(color) * (*mdata);
|
|
a += _rgba_geta(color) * (*mdata);
|
|
}
|
|
);
|
|
|
|
if (effect->target & TARGET_RED_CHANNEL) {
|
|
r = r / div + matrix->bias;
|
|
r = MID(0, r, 255);
|
|
}
|
|
else
|
|
r = _rgba_getr(color);
|
|
|
|
if (effect->target & TARGET_GREEN_CHANNEL) {
|
|
g = g / div + matrix->bias;
|
|
g = MID(0, g, 255);
|
|
}
|
|
else
|
|
g = _rgba_getg(color);
|
|
|
|
if (effect->target & TARGET_BLUE_CHANNEL) {
|
|
b = b / div + matrix->bias;
|
|
b = MID(0, b, 255);
|
|
}
|
|
else
|
|
b = _rgba_getb(color);
|
|
|
|
if (effect->target & TARGET_ALPHA_CHANNEL) {
|
|
a = a / matrix->div + matrix->bias;
|
|
a = MID(0, a, 255);
|
|
}
|
|
else
|
|
a = _rgba_geta(color);
|
|
|
|
*(dst_address++) = _rgba(r, g, b, a);
|
|
}
|
|
}
|
|
}
|
|
|
|
void apply_convolution_matrix2(Effect *effect)
|
|
{
|
|
ConvMatr *matrix = data.convmatr;
|
|
Image *src = effect->src;
|
|
Image *dst = effect->dst;
|
|
ase_uint16 *src_address;
|
|
ase_uint16 *dst_address;
|
|
int x, y, dx, dy, color;
|
|
int getx, gety, addx, addy;
|
|
int k, a;
|
|
int *mdata;
|
|
int w, div;
|
|
|
|
if (matrix) {
|
|
w = effect->x+effect->w;
|
|
y = effect->y+effect->row;
|
|
dst_address = ((ase_uint16 **)dst->line)[y]+effect->x;
|
|
|
|
for (x=effect->x; x<w; x++) {
|
|
/* avoid the unmask region */
|
|
if (effect->mask_address) {
|
|
if (!((*effect->mask_address) & (1<<effect->d.rem))) {
|
|
dst_address++;
|
|
_image_bitmap_next_bit(effect->d, effect->mask_address);
|
|
continue;
|
|
}
|
|
else
|
|
_image_bitmap_next_bit(effect->d, effect->mask_address);
|
|
}
|
|
|
|
k = a = 0;
|
|
|
|
GET_CONVMATR_DATA
|
|
(GrayscaleTraits,
|
|
if (_graya_geta(color) == 0)
|
|
div -= *mdata;
|
|
else {
|
|
k += _graya_getv(color) * (*mdata);
|
|
a += _graya_geta(color) * (*mdata);
|
|
}
|
|
);
|
|
|
|
if (effect->target & TARGET_GRAY_CHANNEL) {
|
|
k = k / div + matrix->bias;
|
|
k = MID(0, k, 255);
|
|
}
|
|
else
|
|
k = _graya_getv(color);
|
|
|
|
if (effect->target & TARGET_ALPHA_CHANNEL) {
|
|
a = a / matrix->div + matrix->bias;
|
|
a = MID(0, a, 255);
|
|
}
|
|
else
|
|
a = _graya_geta(color);
|
|
|
|
*(dst_address++) = _graya(k, a);
|
|
}
|
|
}
|
|
}
|
|
|
|
void apply_convolution_matrix1(Effect *effect)
|
|
{
|
|
Palette *pal = get_current_palette();
|
|
ConvMatr *matrix = data.convmatr;
|
|
Image *src = effect->src;
|
|
Image *dst = effect->dst;
|
|
ase_uint8 *src_address;
|
|
ase_uint8 *dst_address;
|
|
int x, y, dx, dy, color;
|
|
int getx, gety, addx, addy;
|
|
int r, g, b, index;
|
|
int *mdata;
|
|
int w, div;
|
|
|
|
if (matrix) {
|
|
w = effect->x+effect->w;
|
|
y = effect->y+effect->row;
|
|
dst_address = ((ase_uint8 **)dst->line)[y]+effect->x;
|
|
|
|
for (x=effect->x; x<w; x++) {
|
|
/* avoid the unmask region */
|
|
if (effect->mask_address) {
|
|
if (!((*effect->mask_address) & (1<<effect->d.rem))) {
|
|
dst_address++;
|
|
_image_bitmap_next_bit(effect->d, effect->mask_address);
|
|
continue;
|
|
}
|
|
else
|
|
_image_bitmap_next_bit(effect->d, effect->mask_address);
|
|
}
|
|
|
|
r = g = b = index = 0;
|
|
|
|
GET_CONVMATR_DATA
|
|
(IndexedTraits,
|
|
r += _rgba_getr(pal->color[color]) * (*mdata);
|
|
g += _rgba_getg(pal->color[color]) * (*mdata);
|
|
b += _rgba_getb(pal->color[color]) * (*mdata);
|
|
index += color * (*mdata);
|
|
);
|
|
|
|
if (effect->target & TARGET_INDEX_CHANNEL) {
|
|
index = index / matrix->div + matrix->bias;
|
|
index = MID(0, index, 255);
|
|
|
|
*(dst_address++) = index;
|
|
}
|
|
else {
|
|
if (effect->target & TARGET_RED_CHANNEL) {
|
|
r = r / div + matrix->bias;
|
|
r = MID(0, r, 255);
|
|
}
|
|
else
|
|
r = _rgba_getr(pal->color[color]);
|
|
|
|
if (effect->target & TARGET_GREEN_CHANNEL) {
|
|
g = g / div + matrix->bias;
|
|
g = MID(0, g, 255);
|
|
}
|
|
else
|
|
g = _rgba_getg(pal->color[color]);
|
|
|
|
if (effect->target & TARGET_BLUE_CHANNEL) {
|
|
b = b / div + matrix->bias;
|
|
b = MID(0, b, 255);
|
|
}
|
|
else
|
|
b = _rgba_getb(pal->color[color]);
|
|
|
|
*(dst_address++) = orig_rgb_map->data[r>>3][g>>3][b>>3];
|
|
}
|
|
}
|
|
}
|
|
}
|