mirror of
https://github.com/aseprite/aseprite.git
synced 2025-01-30 06:32:42 +00:00
Move functions related to quantization and dithering algorithms to raster/quantization.h|cpp.
This commit is contained in:
parent
47a265745c
commit
4c8c7463ed
1
TODO.txt
1
TODO.txt
@ -4,6 +4,7 @@ For next release
|
||||
+ After creating or opening a sprite, it should be shown centered in the editor.
|
||||
+ Mini-look for sliders in palette editor (add ISliderBackground to draw RGB/HSV sliders
|
||||
with a customized background).
|
||||
+ Move "effect/images_ref" to "raster" and refactor it to a class.
|
||||
|
||||
Refactoring
|
||||
-----------
|
||||
|
@ -207,7 +207,7 @@ add_library(aseprite-library
|
||||
raster/palette.cpp
|
||||
raster/path.cpp
|
||||
raster/pen.cpp
|
||||
raster/quant.cpp
|
||||
raster/quantization.cpp
|
||||
raster/rgbmap.cpp
|
||||
raster/rotate.cpp
|
||||
raster/sprite.cpp
|
||||
@ -225,7 +225,6 @@ add_library(aseprite-library
|
||||
util/misc.cpp
|
||||
util/msk_file.cpp
|
||||
util/pic_file.cpp
|
||||
util/quantize.cpp
|
||||
util/render.cpp
|
||||
util/thmbnail.cpp
|
||||
widgets/color_bar.cpp
|
||||
|
@ -26,7 +26,6 @@
|
||||
#include "modules/gui.h"
|
||||
#include "modules/palettes.h"
|
||||
#include "raster/image.h"
|
||||
#include "raster/quant.h"
|
||||
#include "raster/sprite.h"
|
||||
#include "sprite_wrappers.h"
|
||||
#include "undoable.h"
|
||||
@ -34,7 +33,7 @@
|
||||
class ChangeImageTypeCommand : public Command
|
||||
{
|
||||
int m_imgtype;
|
||||
bool m_dithering;
|
||||
DitheringMethod m_dithering;
|
||||
public:
|
||||
ChangeImageTypeCommand();
|
||||
Command* clone() const { return new ChangeImageTypeCommand(*this); }
|
||||
|
@ -40,12 +40,12 @@
|
||||
#include "modules/palettes.h"
|
||||
#include "raster/image.h"
|
||||
#include "raster/palette.h"
|
||||
#include "raster/quantization.h"
|
||||
#include "raster/sprite.h"
|
||||
#include "raster/stock.h"
|
||||
#include "raster/undo.h"
|
||||
#include "sprite_wrappers.h"
|
||||
#include "ui_context.h"
|
||||
#include "util/quantize.h"
|
||||
#include "widgets/color_bar.h"
|
||||
#include "widgets/colview.h"
|
||||
#include "widgets/editor.h"
|
||||
@ -721,8 +721,7 @@ static void quantize_command(JWidget widget)
|
||||
return;
|
||||
}
|
||||
|
||||
palette = new Palette(0, 256);
|
||||
sprite_quantize_ex(sprite, palette);
|
||||
palette = quantization::create_palette_from_rgb(sprite);
|
||||
}
|
||||
|
||||
set_new_palette(palette, "Quantize Palette");
|
||||
|
@ -31,7 +31,7 @@
|
||||
#include "modules/gui.h"
|
||||
#include "modules/palettes.h"
|
||||
#include "raster/raster.h"
|
||||
#include "util/quantize.h"
|
||||
#include "raster/quantization.h"
|
||||
#include "widgets/statebar.h"
|
||||
|
||||
extern FileFormat format_ase;
|
||||
@ -584,9 +584,15 @@ void fop_operate(FileOp *fop)
|
||||
else
|
||||
fop->sprite->setFilename(fop->filename.c_str());
|
||||
|
||||
// Quantize a palette for RGB images
|
||||
if (fop->sprite->getImgType() == IMAGE_RGB)
|
||||
sprite_quantize(fop->sprite);
|
||||
// Creates a suitable palette for RGB images
|
||||
if (fop->sprite->getImgType() == IMAGE_RGB) {
|
||||
Palette* palette = quantization::create_palette_from_rgb(fop->sprite);
|
||||
|
||||
fop->sprite->resetPalettes();
|
||||
fop->sprite->setPalette(palette, false);
|
||||
|
||||
delete palette;
|
||||
}
|
||||
|
||||
fop->sprite->markAsSaved();
|
||||
}
|
||||
|
@ -16,15 +16,13 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef UTIL_QUANTIZE_H_INCLUDED
|
||||
#define UTIL_QUANTIZE_H_INCLUDED
|
||||
#ifndef RASTER_DITHERING_METHOD_H_INCLUDED
|
||||
#define RASTER_DITHERING_METHOD_H_INCLUDED
|
||||
|
||||
class Palette;
|
||||
class Sprite;
|
||||
class Stock;
|
||||
|
||||
void sprite_quantize(Sprite* sprite);
|
||||
void sprite_quantize_ex(const Sprite* sprite, Palette* palette);
|
||||
// Dithering methods
|
||||
enum DitheringMethod {
|
||||
DITHERING_NONE,
|
||||
DITHERING_ORDERED,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -1,275 +0,0 @@
|
||||
/* ASE - Allegro Sprite Editor
|
||||
* Copyright (C) 2001-2010 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 "gfx/hsv.h"
|
||||
#include "gfx/rgb.h"
|
||||
#include "raster/blend.h"
|
||||
#include "raster/image.h"
|
||||
#include "raster/quant.h"
|
||||
#include "raster/palette.h"
|
||||
#include "raster/rgbmap.h"
|
||||
|
||||
using namespace gfx;
|
||||
|
||||
Image *image_set_imgtype(const Image* image, int imgtype,
|
||||
int dithering_method,
|
||||
const RgbMap* rgbmap,
|
||||
const Palette* palette)
|
||||
{
|
||||
ase_uint32* rgb_address;
|
||||
ase_uint16* gray_address;
|
||||
ase_uint8* idx_address;
|
||||
ase_uint32 c;
|
||||
int i, r, g, b, size;
|
||||
Image *new_image;
|
||||
|
||||
// no convertion
|
||||
if (image->imgtype == imgtype)
|
||||
return NULL;
|
||||
// RGB -> Indexed with ordered dithering
|
||||
else if (image->imgtype == IMAGE_RGB &&
|
||||
imgtype == IMAGE_INDEXED &&
|
||||
dithering_method == DITHERING_ORDERED) {
|
||||
return image_rgb_to_indexed(image, 0, 0, rgbmap, palette);
|
||||
}
|
||||
|
||||
new_image = image_new(imgtype, image->w, image->h);
|
||||
if (!new_image)
|
||||
return NULL;
|
||||
|
||||
size = image->w*image->h;
|
||||
|
||||
switch (image->imgtype) {
|
||||
|
||||
case IMAGE_RGB:
|
||||
rgb_address = (ase_uint32*)image->dat;
|
||||
|
||||
switch (new_image->imgtype) {
|
||||
// RGB -> Grayscale
|
||||
case IMAGE_GRAYSCALE:
|
||||
gray_address = (ase_uint16*)new_image->dat;
|
||||
for (i=0; i<size; i++) {
|
||||
c = *rgb_address;
|
||||
|
||||
g = 255 * Hsv(Rgb(_rgba_getr(c),
|
||||
_rgba_getg(c),
|
||||
_rgba_getb(c))).valueInt() / 100;
|
||||
*gray_address = _graya(g, _rgba_geta(c));
|
||||
|
||||
rgb_address++;
|
||||
gray_address++;
|
||||
}
|
||||
break;
|
||||
// RGB -> Indexed
|
||||
case IMAGE_INDEXED:
|
||||
idx_address = new_image->dat;
|
||||
for (i=0; i<size; i++) {
|
||||
c = *rgb_address;
|
||||
r = _rgba_getr(c);
|
||||
g = _rgba_getg(c);
|
||||
b = _rgba_getb(c);
|
||||
if (_rgba_geta(c) == 0)
|
||||
*idx_address = 0;
|
||||
else
|
||||
*idx_address = rgbmap->mapColor(r, g, b);
|
||||
rgb_address++;
|
||||
idx_address++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case IMAGE_GRAYSCALE:
|
||||
gray_address = (ase_uint16*)image->dat;
|
||||
|
||||
switch (new_image->imgtype) {
|
||||
// Grayscale -> RGB
|
||||
case IMAGE_RGB:
|
||||
rgb_address = (ase_uint32*)new_image->dat;
|
||||
for (i=0; i<size; i++) {
|
||||
c = *gray_address;
|
||||
g = _graya_getv(c);
|
||||
*rgb_address = _rgba(g, g, g, _graya_geta(c));
|
||||
gray_address++;
|
||||
rgb_address++;
|
||||
}
|
||||
break;
|
||||
// Grayscale -> Indexed
|
||||
case IMAGE_INDEXED:
|
||||
idx_address = new_image->dat;
|
||||
for (i=0; i<size; i++) {
|
||||
c = *gray_address;
|
||||
if (_graya_geta(c) == 0)
|
||||
*idx_address = 0;
|
||||
else
|
||||
*idx_address = _graya_getv(c);
|
||||
gray_address++;
|
||||
idx_address++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case IMAGE_INDEXED:
|
||||
idx_address = image->dat;
|
||||
|
||||
switch (new_image->imgtype) {
|
||||
// Indexed -> RGB
|
||||
case IMAGE_RGB:
|
||||
rgb_address = (ase_uint32*)new_image->dat;
|
||||
for (i=0; i<size; i++) {
|
||||
c = *idx_address;
|
||||
if (c == 0)
|
||||
*rgb_address = 0;
|
||||
else
|
||||
*rgb_address = _rgba(_rgba_getr(palette->getEntry(c)),
|
||||
_rgba_getg(palette->getEntry(c)),
|
||||
_rgba_getb(palette->getEntry(c)), 255);
|
||||
idx_address++;
|
||||
rgb_address++;
|
||||
}
|
||||
break;
|
||||
// Indexed -> Grayscale
|
||||
case IMAGE_GRAYSCALE:
|
||||
gray_address = (ase_uint16*)new_image->dat;
|
||||
for (i=0; i<size; i++) {
|
||||
c = *idx_address;
|
||||
if (c == 0)
|
||||
*gray_address = 0;
|
||||
else {
|
||||
r = _rgba_getr(palette->getEntry(c));
|
||||
g = _rgba_getg(palette->getEntry(c));
|
||||
b = _rgba_getb(palette->getEntry(c));
|
||||
|
||||
g = 255 * Hsv(Rgb(r, g, b)).valueInt() / 100;
|
||||
*gray_address = _graya(g, 255);
|
||||
}
|
||||
idx_address++;
|
||||
gray_address++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return new_image;
|
||||
}
|
||||
|
||||
/* Based on Gary Oberbrunner: */
|
||||
/*----------------------------------------------------------------------
|
||||
* Color image quantizer, from Paul Heckbert's paper in
|
||||
* Computer Graphics, vol.16 #3, July 1982 (Siggraph proceedings),
|
||||
* pp. 297-304.
|
||||
* By Gary Oberbrunner, copyright c. 1988.
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/* Bayer-method ordered dither. The array line[] contains the
|
||||
* intensity values for the line being processed. As you can see, the
|
||||
* ordered dither is much simpler than the error dispersion dither.
|
||||
* It is also many times faster, but it is not as accurate and
|
||||
* produces cross-hatch * patterns on the output.
|
||||
*/
|
||||
|
||||
static int pattern[8][8] = {
|
||||
{ 0, 32, 8, 40, 2, 34, 10, 42 }, /* 8x8 Bayer ordered dithering */
|
||||
{ 48, 16, 56, 24, 50, 18, 58, 26 }, /* pattern. Each input pixel */
|
||||
{ 12, 44, 4, 36, 14, 46, 6, 38 }, /* is scaled to the 0..63 range */
|
||||
{ 60, 28, 52, 20, 62, 30, 54, 22 }, /* before looking in this table */
|
||||
{ 3, 35, 11, 43, 1, 33, 9, 41 }, /* to determine the action. */
|
||||
{ 51, 19, 59, 27, 49, 17, 57, 25 },
|
||||
{ 15, 47, 7, 39, 13, 45, 5, 37 },
|
||||
{ 63, 31, 55, 23, 61, 29, 53, 21 }
|
||||
};
|
||||
|
||||
#define DIST(r1,g1,b1,r2,g2,b2) (3 * ((r1)-(r2)) * ((r1)-(r2)) + \
|
||||
4 * ((g1)-(g2)) * ((g1)-(g2)) + \
|
||||
2 * ((b1)-(b2)) * ((b1)-(b2)))
|
||||
|
||||
Image* image_rgb_to_indexed(const Image* src_image,
|
||||
int offsetx, int offsety,
|
||||
const RgbMap* rgbmap,
|
||||
const Palette* palette)
|
||||
{
|
||||
int oppr, oppg, oppb, oppnrcm;
|
||||
Image *dst_image;
|
||||
int dither_const;
|
||||
int nr, ng, nb;
|
||||
int r, g, b, a;
|
||||
int nearestcm;
|
||||
int c, x, y;
|
||||
|
||||
dst_image = image_new(IMAGE_INDEXED, src_image->w, src_image->h);
|
||||
if (!dst_image)
|
||||
return NULL;
|
||||
|
||||
for (y=0; y<src_image->h; y++) {
|
||||
for (x=0; x<src_image->w; x++) {
|
||||
c = image_getpixel_fast<RgbTraits>(src_image, x, y);
|
||||
|
||||
r = _rgba_getr(c);
|
||||
g = _rgba_getg(c);
|
||||
b = _rgba_getb(c);
|
||||
a = _rgba_geta(c);
|
||||
|
||||
if (a != 0) {
|
||||
nearestcm = rgbmap->mapColor(r, g, b);
|
||||
/* rgb values for nearest color */
|
||||
nr = _rgba_getr(palette->getEntry(nearestcm));
|
||||
ng = _rgba_getg(palette->getEntry(nearestcm));
|
||||
nb = _rgba_getb(palette->getEntry(nearestcm));
|
||||
/* Color as far from rgb as nrngnb but in the other direction */
|
||||
oppr = MID(0, 2*r - nr, 255);
|
||||
oppg = MID(0, 2*g - ng, 255);
|
||||
oppb = MID(0, 2*b - nb, 255);
|
||||
/* Nearest match for opposite color: */
|
||||
oppnrcm = rgbmap->mapColor(oppr, oppg, oppb);
|
||||
/* If they're not the same, dither between them. */
|
||||
/* Dither constant is measured by where the true
|
||||
color lies between the two nearest approximations.
|
||||
Since the most nearly opposite color is not necessarily
|
||||
on the line from the nearest through the true color,
|
||||
some triangulation error can be introduced. In the worst
|
||||
case the r-nr distance can actually be less than the nr-oppr
|
||||
distance. */
|
||||
if (oppnrcm != nearestcm) {
|
||||
oppr = _rgba_getr(palette->getEntry(oppnrcm));
|
||||
oppg = _rgba_getg(palette->getEntry(oppnrcm));
|
||||
oppb = _rgba_getb(palette->getEntry(oppnrcm));
|
||||
|
||||
dither_const = DIST(nr, ng, nb, oppr, oppg, oppb);
|
||||
if (dither_const != 0) {
|
||||
dither_const = 64 * DIST(r, g, b, nr, ng, nb) / dither_const;
|
||||
dither_const = MIN(63, dither_const);
|
||||
|
||||
if (pattern[(x+offsetx) & 7][(y+offsety) & 7] < dither_const)
|
||||
nearestcm = oppnrcm;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
nearestcm = 0;
|
||||
|
||||
image_putpixel_fast<IndexedTraits>(dst_image, x, y, nearestcm);
|
||||
}
|
||||
}
|
||||
|
||||
return dst_image;
|
||||
}
|
@ -18,33 +18,30 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <allegro/color.h>
|
||||
|
||||
#include "console.h"
|
||||
#include "effect/effect.h"
|
||||
#include "effect/images_ref.h"
|
||||
|
||||
#include "gfx/hsv.h"
|
||||
#include "gfx/rgb.h"
|
||||
#include "raster/blend.h"
|
||||
#include "raster/image.h"
|
||||
#include "raster/palette.h"
|
||||
#include "raster/sprite.h"
|
||||
#include "util/quantize.h"
|
||||
#include "raster/quantization.h"
|
||||
#include "raster/rgbmap.h"
|
||||
|
||||
static int quantize_bitmaps(Image **image, int nimage, RGB *pal, int *bmp_i, int fill_other);
|
||||
using namespace gfx;
|
||||
|
||||
void sprite_quantize(Sprite *sprite)
|
||||
{
|
||||
Palette* palette = new Palette(0, 256); // TODO sprite_quantize_ex should change the number of colors of this palette
|
||||
|
||||
sprite_quantize_ex(sprite, palette);
|
||||
|
||||
// Just one palette
|
||||
sprite->resetPalettes();
|
||||
sprite->setPalette(palette, false);
|
||||
|
||||
delete palette;
|
||||
}
|
||||
|
||||
void sprite_quantize_ex(const Sprite *sprite, Palette *palette)
|
||||
// Converts a RGB image to indexed with ordered dithering method.
|
||||
static Image* ordered_dithering(const Image* src_image,
|
||||
int offsetx, int offsety,
|
||||
const RgbMap* rgbmap,
|
||||
const Palette* palette);
|
||||
|
||||
static int create_palette_from_bitmaps(Image **image, int nimage, RGB *pal, int *bmp_i, int fill_other);
|
||||
|
||||
Palette* quantization::create_palette_from_rgb(const Sprite* sprite)
|
||||
{
|
||||
Palette* palette = new Palette(0, 256);
|
||||
Image* flat_image;
|
||||
Image** image_array;
|
||||
ImageRef* p;
|
||||
@ -86,7 +83,7 @@ void sprite_quantize_ex(const Sprite *sprite, Palette *palette)
|
||||
for (c=0; c<nimage; c++)
|
||||
ibmp[c] = 128;
|
||||
|
||||
quantize_bitmaps(image_array, nimage, rgbpal, ibmp, true);
|
||||
create_palette_from_bitmaps(image_array, nimage, rgbpal, ibmp, true);
|
||||
|
||||
palette->fromAllegro(rgbpal);
|
||||
|
||||
@ -97,6 +94,254 @@ void sprite_quantize_ex(const Sprite *sprite, Palette *palette)
|
||||
image_free(flat_image);
|
||||
images_ref_free(images);
|
||||
}
|
||||
|
||||
return palette;
|
||||
}
|
||||
|
||||
Image* quantization::convert_imgtype(const Image* image, int imgtype,
|
||||
DitheringMethod ditheringMethod,
|
||||
const RgbMap* rgbmap,
|
||||
const Palette* palette)
|
||||
{
|
||||
ase_uint32* rgb_address;
|
||||
ase_uint16* gray_address;
|
||||
ase_uint8* idx_address;
|
||||
ase_uint32 c;
|
||||
int i, r, g, b, size;
|
||||
Image *new_image;
|
||||
|
||||
// no convertion
|
||||
if (image->imgtype == imgtype)
|
||||
return NULL;
|
||||
// RGB -> Indexed with ordered dithering
|
||||
else if (image->imgtype == IMAGE_RGB &&
|
||||
imgtype == IMAGE_INDEXED &&
|
||||
ditheringMethod == DITHERING_ORDERED) {
|
||||
return ordered_dithering(image, 0, 0, rgbmap, palette);
|
||||
}
|
||||
|
||||
new_image = image_new(imgtype, image->w, image->h);
|
||||
if (!new_image)
|
||||
return NULL;
|
||||
|
||||
size = image->w*image->h;
|
||||
|
||||
switch (image->imgtype) {
|
||||
|
||||
case IMAGE_RGB:
|
||||
rgb_address = (ase_uint32*)image->dat;
|
||||
|
||||
switch (new_image->imgtype) {
|
||||
// RGB -> Grayscale
|
||||
case IMAGE_GRAYSCALE:
|
||||
gray_address = (ase_uint16*)new_image->dat;
|
||||
for (i=0; i<size; i++) {
|
||||
c = *rgb_address;
|
||||
|
||||
g = 255 * Hsv(Rgb(_rgba_getr(c),
|
||||
_rgba_getg(c),
|
||||
_rgba_getb(c))).valueInt() / 100;
|
||||
*gray_address = _graya(g, _rgba_geta(c));
|
||||
|
||||
rgb_address++;
|
||||
gray_address++;
|
||||
}
|
||||
break;
|
||||
// RGB -> Indexed
|
||||
case IMAGE_INDEXED:
|
||||
idx_address = new_image->dat;
|
||||
for (i=0; i<size; i++) {
|
||||
c = *rgb_address;
|
||||
r = _rgba_getr(c);
|
||||
g = _rgba_getg(c);
|
||||
b = _rgba_getb(c);
|
||||
if (_rgba_geta(c) == 0)
|
||||
*idx_address = 0;
|
||||
else
|
||||
*idx_address = rgbmap->mapColor(r, g, b);
|
||||
rgb_address++;
|
||||
idx_address++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case IMAGE_GRAYSCALE:
|
||||
gray_address = (ase_uint16*)image->dat;
|
||||
|
||||
switch (new_image->imgtype) {
|
||||
// Grayscale -> RGB
|
||||
case IMAGE_RGB:
|
||||
rgb_address = (ase_uint32*)new_image->dat;
|
||||
for (i=0; i<size; i++) {
|
||||
c = *gray_address;
|
||||
g = _graya_getv(c);
|
||||
*rgb_address = _rgba(g, g, g, _graya_geta(c));
|
||||
gray_address++;
|
||||
rgb_address++;
|
||||
}
|
||||
break;
|
||||
// Grayscale -> Indexed
|
||||
case IMAGE_INDEXED:
|
||||
idx_address = new_image->dat;
|
||||
for (i=0; i<size; i++) {
|
||||
c = *gray_address;
|
||||
if (_graya_geta(c) == 0)
|
||||
*idx_address = 0;
|
||||
else
|
||||
*idx_address = _graya_getv(c);
|
||||
gray_address++;
|
||||
idx_address++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case IMAGE_INDEXED:
|
||||
idx_address = image->dat;
|
||||
|
||||
switch (new_image->imgtype) {
|
||||
// Indexed -> RGB
|
||||
case IMAGE_RGB:
|
||||
rgb_address = (ase_uint32*)new_image->dat;
|
||||
for (i=0; i<size; i++) {
|
||||
c = *idx_address;
|
||||
if (c == 0)
|
||||
*rgb_address = 0;
|
||||
else
|
||||
*rgb_address = _rgba(_rgba_getr(palette->getEntry(c)),
|
||||
_rgba_getg(palette->getEntry(c)),
|
||||
_rgba_getb(palette->getEntry(c)), 255);
|
||||
idx_address++;
|
||||
rgb_address++;
|
||||
}
|
||||
break;
|
||||
// Indexed -> Grayscale
|
||||
case IMAGE_GRAYSCALE:
|
||||
gray_address = (ase_uint16*)new_image->dat;
|
||||
for (i=0; i<size; i++) {
|
||||
c = *idx_address;
|
||||
if (c == 0)
|
||||
*gray_address = 0;
|
||||
else {
|
||||
r = _rgba_getr(palette->getEntry(c));
|
||||
g = _rgba_getg(palette->getEntry(c));
|
||||
b = _rgba_getb(palette->getEntry(c));
|
||||
|
||||
g = 255 * Hsv(Rgb(r, g, b)).valueInt() / 100;
|
||||
*gray_address = _graya(g, 255);
|
||||
}
|
||||
idx_address++;
|
||||
gray_address++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return new_image;
|
||||
}
|
||||
|
||||
/* Based on Gary Oberbrunner: */
|
||||
/*----------------------------------------------------------------------
|
||||
* Color image quantizer, from Paul Heckbert's paper in
|
||||
* Computer Graphics, vol.16 #3, July 1982 (Siggraph proceedings),
|
||||
* pp. 297-304.
|
||||
* By Gary Oberbrunner, copyright c. 1988.
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/* Bayer-method ordered dither. The array line[] contains the
|
||||
* intensity values for the line being processed. As you can see, the
|
||||
* ordered dither is much simpler than the error dispersion dither.
|
||||
* It is also many times faster, but it is not as accurate and
|
||||
* produces cross-hatch * patterns on the output.
|
||||
*/
|
||||
|
||||
static int pattern[8][8] = {
|
||||
{ 0, 32, 8, 40, 2, 34, 10, 42 }, /* 8x8 Bayer ordered dithering */
|
||||
{ 48, 16, 56, 24, 50, 18, 58, 26 }, /* pattern. Each input pixel */
|
||||
{ 12, 44, 4, 36, 14, 46, 6, 38 }, /* is scaled to the 0..63 range */
|
||||
{ 60, 28, 52, 20, 62, 30, 54, 22 }, /* before looking in this table */
|
||||
{ 3, 35, 11, 43, 1, 33, 9, 41 }, /* to determine the action. */
|
||||
{ 51, 19, 59, 27, 49, 17, 57, 25 },
|
||||
{ 15, 47, 7, 39, 13, 45, 5, 37 },
|
||||
{ 63, 31, 55, 23, 61, 29, 53, 21 }
|
||||
};
|
||||
|
||||
#define DIST(r1,g1,b1,r2,g2,b2) (3 * ((r1)-(r2)) * ((r1)-(r2)) + \
|
||||
4 * ((g1)-(g2)) * ((g1)-(g2)) + \
|
||||
2 * ((b1)-(b2)) * ((b1)-(b2)))
|
||||
|
||||
static Image* ordered_dithering(const Image* src_image,
|
||||
int offsetx, int offsety,
|
||||
const RgbMap* rgbmap,
|
||||
const Palette* palette)
|
||||
{
|
||||
int oppr, oppg, oppb, oppnrcm;
|
||||
Image *dst_image;
|
||||
int dither_const;
|
||||
int nr, ng, nb;
|
||||
int r, g, b, a;
|
||||
int nearestcm;
|
||||
int c, x, y;
|
||||
|
||||
dst_image = image_new(IMAGE_INDEXED, src_image->w, src_image->h);
|
||||
if (!dst_image)
|
||||
return NULL;
|
||||
|
||||
for (y=0; y<src_image->h; y++) {
|
||||
for (x=0; x<src_image->w; x++) {
|
||||
c = image_getpixel_fast<RgbTraits>(src_image, x, y);
|
||||
|
||||
r = _rgba_getr(c);
|
||||
g = _rgba_getg(c);
|
||||
b = _rgba_getb(c);
|
||||
a = _rgba_geta(c);
|
||||
|
||||
if (a != 0) {
|
||||
nearestcm = rgbmap->mapColor(r, g, b);
|
||||
/* rgb values for nearest color */
|
||||
nr = _rgba_getr(palette->getEntry(nearestcm));
|
||||
ng = _rgba_getg(palette->getEntry(nearestcm));
|
||||
nb = _rgba_getb(palette->getEntry(nearestcm));
|
||||
/* Color as far from rgb as nrngnb but in the other direction */
|
||||
oppr = MID(0, 2*r - nr, 255);
|
||||
oppg = MID(0, 2*g - ng, 255);
|
||||
oppb = MID(0, 2*b - nb, 255);
|
||||
/* Nearest match for opposite color: */
|
||||
oppnrcm = rgbmap->mapColor(oppr, oppg, oppb);
|
||||
/* If they're not the same, dither between them. */
|
||||
/* Dither constant is measured by where the true
|
||||
color lies between the two nearest approximations.
|
||||
Since the most nearly opposite color is not necessarily
|
||||
on the line from the nearest through the true color,
|
||||
some triangulation error can be introduced. In the worst
|
||||
case the r-nr distance can actually be less than the nr-oppr
|
||||
distance. */
|
||||
if (oppnrcm != nearestcm) {
|
||||
oppr = _rgba_getr(palette->getEntry(oppnrcm));
|
||||
oppg = _rgba_getg(palette->getEntry(oppnrcm));
|
||||
oppb = _rgba_getb(palette->getEntry(oppnrcm));
|
||||
|
||||
dither_const = DIST(nr, ng, nb, oppr, oppg, oppb);
|
||||
if (dither_const != 0) {
|
||||
dither_const = 64 * DIST(r, g, b, nr, ng, nb) / dither_const;
|
||||
dither_const = MIN(63, dither_const);
|
||||
|
||||
if (pattern[(x+offsetx) & 7][(y+offsety) & 7] < dither_const)
|
||||
nearestcm = oppnrcm;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
nearestcm = 0;
|
||||
|
||||
image_putpixel_fast<IndexedTraits>(dst_image, x, y, nearestcm);
|
||||
}
|
||||
}
|
||||
|
||||
return dst_image;
|
||||
}
|
||||
|
||||
/* quantize.c
|
||||
@ -117,26 +362,26 @@ void sprite_quantize_ex(const Sprite *sprite, Palette *palette)
|
||||
*/
|
||||
|
||||
#define TREE_DEPTH 2
|
||||
/* TREE_DEPTH should be a power of 2. The lower it is, the deeper the tree
|
||||
/* TREE_DEPTH should be a power of 2. The lower it is, the deeper the tree
|
||||
* will go. This will not usually affect the accuracy of colours generated,
|
||||
* but with images with very subtle changes of colour, the variation can be
|
||||
* but with images with very subtle changes of colour, the variation can be
|
||||
* lost with higher values of TREE_DEPTH.
|
||||
*
|
||||
* As these trees go deeper they use an extortionate amount of memory. If it
|
||||
* As these trees go deeper they use an extortionate amount of memory. If it
|
||||
* runs out, you have no choice but to increase the value of TREE_DEPTH.
|
||||
*/
|
||||
|
||||
/* quantize_bitmaps:
|
||||
* generates an optimised palette for the list of
|
||||
* bitmaps specified. All bitmaps must be true-colour. The number of bitmaps
|
||||
/* create_palette_from_bitmaps:
|
||||
* generates an optimised palette for the list of
|
||||
* bitmaps specified. All bitmaps must be true-colour. The number of bitmaps
|
||||
* must be passed in n_bmp, and bmp must point to an array of pointers to
|
||||
* BITMAP structures. bmp_i should point to an array parallel to bmp,
|
||||
* BITMAP structures. bmp_i should point to an array parallel to bmp,
|
||||
* containing importance values for the bitmaps. pal must point to a PALETTE
|
||||
* structure which will be filled with the optimised palette.
|
||||
*
|
||||
* pal will be scanned for predefined colours. Any entry where the .r element
|
||||
* is 255 will be considered a free entry. All others are taken as predefined
|
||||
* colours. Predefined colours will not be changed, and the rest of the
|
||||
* colours. Predefined colours will not be changed, and the rest of the
|
||||
* palette will be unaffected by them. There may be copies of these colours.
|
||||
*
|
||||
* If in the bitmaps this routine finds any occurrence of the colour to which
|
||||
@ -147,7 +392,7 @@ void sprite_quantize_ex(const Sprite *sprite, Palette *palette)
|
||||
* fill_other != 0, they will be filled with black. If fill_other == 0, they
|
||||
* will be left containing whatever values they contained before.
|
||||
*
|
||||
* This function does not convert the bitmaps to 256-colour format. The
|
||||
* This function does not convert the bitmaps to 256-colour format. The
|
||||
* conversion must be done afterwards by the main program.
|
||||
*/
|
||||
|
||||
@ -162,11 +407,11 @@ typedef struct PALETTE_NODE
|
||||
static PALETTE_NODE *rgb_node[64][64][64];
|
||||
|
||||
static PALETTE_NODE *create_node(unsigned int rl,
|
||||
unsigned int gl,
|
||||
unsigned int bl,
|
||||
unsigned int rh,
|
||||
unsigned int gh,
|
||||
unsigned int bh,PALETTE_NODE *parent)
|
||||
unsigned int gl,
|
||||
unsigned int bl,
|
||||
unsigned int rh,
|
||||
unsigned int gh,
|
||||
unsigned int bh,PALETTE_NODE *parent)
|
||||
{
|
||||
PALETTE_NODE *node;
|
||||
unsigned int rm,gm,bm;
|
||||
@ -232,7 +477,7 @@ static PALETTE_NODE *collapse_empty(PALETTE_NODE *node,unsigned int *n_colours)
|
||||
}
|
||||
|
||||
static PALETTE_NODE *collapse_nodes(PALETTE_NODE *node,unsigned int *n_colours,
|
||||
unsigned int n_entries,unsigned int Ep)
|
||||
unsigned int n_entries,unsigned int Ep)
|
||||
{
|
||||
unsigned int b,g,r;
|
||||
if (node->E<=Ep) {
|
||||
@ -321,7 +566,7 @@ static void destroy_tree(PALETTE_NODE *tree)
|
||||
jfree(tree);
|
||||
}
|
||||
|
||||
static int quantize_bitmaps(Image **image, int nimage, RGB *pal, int *bmp_i, int fill_other)
|
||||
static int create_palette_from_bitmaps(Image **image, int nimage, RGB *pal, int *bmp_i, int fill_other)
|
||||
{
|
||||
int c_bmp,x,y,r,g,b;
|
||||
unsigned int n_colours=0;
|
||||
@ -338,7 +583,7 @@ static int quantize_bitmaps(Image **image, int nimage, RGB *pal, int *bmp_i, int
|
||||
/*Scan the bitmaps*/
|
||||
/* add_progress(nimage+1); */
|
||||
for (c_bmp=0;c_bmp<nimage;c_bmp++) {
|
||||
/* add_progress(image[c_bmp]->h); */
|
||||
/* add_progress(image[c_bmp]->h); */
|
||||
for (y=0;y<image[c_bmp]->h;y++) {
|
||||
for (x=0;x<image[c_bmp]->w;x++) {
|
||||
c=image[c_bmp]->getpixel(x,y);
|
||||
@ -360,8 +605,8 @@ static int quantize_bitmaps(Image **image, int nimage, RGB *pal, int *bmp_i, int
|
||||
}
|
||||
/* do_progress(y); */
|
||||
}
|
||||
/* del_progress(); */
|
||||
/* do_progress(c_bmp); */
|
||||
/* del_progress(); */
|
||||
/* do_progress(c_bmp); */
|
||||
}
|
||||
/*Collapse empty nodes in the tree, and count leaves*/
|
||||
tree=collapse_empty(tree,&n_colours);
|
||||
@ -371,8 +616,8 @@ static int quantize_bitmaps(Image **image, int nimage, RGB *pal, int *bmp_i, int
|
||||
}
|
||||
/*Collapse nodes until there are few enough to fit in the palette*/
|
||||
if (n_colours > n_entries) {
|
||||
/* int n_colours1 = n_colours; */
|
||||
/* add_progress(n_colours1 - n_entries); */
|
||||
/* int n_colours1 = n_colours; */
|
||||
/* add_progress(n_colours1 - n_entries); */
|
||||
while (n_colours>n_entries) {
|
||||
Ep=0xFFFFFFFFul;
|
||||
minimum_Ep(tree,&Ep);
|
||||
@ -381,7 +626,7 @@ static int quantize_bitmaps(Image **image, int nimage, RGB *pal, int *bmp_i, int
|
||||
/* if (n_colours > n_entries) */
|
||||
/* do_progress(n_colours1 - n_colours); */
|
||||
}
|
||||
/* del_progress(); */
|
||||
/* del_progress(); */
|
||||
}
|
||||
/* del_progress(); */
|
||||
/* fill palette */
|
@ -16,29 +16,30 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef RASTER_QUANT_H_INCLUDED
|
||||
#define RASTER_QUANT_H_INCLUDED
|
||||
#ifndef RASTER_QUANTIZATION_H_INCLUDED
|
||||
#define RASTER_QUANTIZATION_H_INCLUDED
|
||||
|
||||
#include <allegro/color.h>
|
||||
|
||||
// Dithering methods
|
||||
enum {
|
||||
DITHERING_NONE,
|
||||
DITHERING_ORDERED,
|
||||
};
|
||||
#include "raster/dithering_method.h"
|
||||
|
||||
class Image;
|
||||
class Palette;
|
||||
class RgbMap;
|
||||
class Sprite;
|
||||
class Stock;
|
||||
|
||||
Image* image_set_imgtype(const Image* image, int imgtype,
|
||||
int dithering_method,
|
||||
namespace quantization {
|
||||
|
||||
// Creates a new palette suitable to quantize the given RGB sprite to Indexed color.
|
||||
Palette* create_palette_from_rgb(const Sprite* sprite);
|
||||
|
||||
// Changes the "imgtype" of the image. The dithering method is used
|
||||
// only when you want to convert from RGB to Indexed.
|
||||
Image* convert_imgtype(const Image* image, int imgtype,
|
||||
DitheringMethod ditheringMethod,
|
||||
const RgbMap* rgbmap,
|
||||
const Palette* palette);
|
||||
|
||||
Image* image_rgb_to_indexed(const Image* src_image,
|
||||
int offsetx, int offsety,
|
||||
const RgbMap* rgbmap,
|
||||
const Palette* palette);
|
||||
}
|
||||
|
||||
#endif
|
@ -30,7 +30,6 @@
|
||||
#include "raster/palette.h"
|
||||
#include "raster/path.h"
|
||||
#include "raster/pen.h"
|
||||
#include "raster/quant.h"
|
||||
#include "raster/rgbmap.h"
|
||||
#include "raster/rotate.h"
|
||||
#include "raster/sprite.h"
|
||||
|
@ -22,7 +22,6 @@
|
||||
|
||||
#include "gui/jlist.h"
|
||||
|
||||
#include "sprite_wrappers.h"
|
||||
#include "raster/blend.h"
|
||||
#include "raster/cel.h"
|
||||
#include "raster/dirty.h"
|
||||
@ -30,10 +29,11 @@
|
||||
#include "raster/layer.h"
|
||||
#include "raster/mask.h"
|
||||
#include "raster/palette.h"
|
||||
#include "raster/quant.h"
|
||||
#include "raster/quantization.h"
|
||||
#include "raster/sprite.h"
|
||||
#include "raster/stock.h"
|
||||
#include "raster/undo.h"
|
||||
#include "sprite_wrappers.h"
|
||||
#include "undoable.h"
|
||||
|
||||
/**
|
||||
@ -187,7 +187,7 @@ void Undoable::autocropSprite(int bgcolor)
|
||||
cropSprite(x1, y1, x2-x1+1, y2-y1+1, bgcolor);
|
||||
}
|
||||
|
||||
void Undoable::setImgType(int new_imgtype, int dithering_method)
|
||||
void Undoable::setImgType(int new_imgtype, DitheringMethod dithering_method)
|
||||
{
|
||||
Image *old_image;
|
||||
Image *new_image;
|
||||
@ -210,9 +210,9 @@ void Undoable::setImgType(int new_imgtype, int dithering_method)
|
||||
if (!old_image)
|
||||
continue;
|
||||
|
||||
new_image = image_set_imgtype(old_image, new_imgtype, dithering_method, rgbmap,
|
||||
// TODO check this out
|
||||
m_sprite->getCurrentPalette());
|
||||
new_image = quantization::convert_imgtype(old_image, new_imgtype, dithering_method, rgbmap,
|
||||
// TODO check this out
|
||||
m_sprite->getCurrentPalette());
|
||||
if (!new_image)
|
||||
return; /* TODO error handling: not enough memory!
|
||||
we should undo all work done */
|
||||
|
@ -19,6 +19,8 @@
|
||||
#ifndef UNDOABLE_H_INCLUDED
|
||||
#define UNDOABLE_H_INCLUDED
|
||||
|
||||
#include "raster/dithering_method.h"
|
||||
|
||||
class Cel;
|
||||
class Mask;
|
||||
class Layer;
|
||||
@ -50,7 +52,7 @@ public:
|
||||
void setSpriteSize(int w, int h);
|
||||
void cropSprite(int x, int y, int w, int h, int bgcolor);
|
||||
void autocropSprite(int bgcolor);
|
||||
void setImgType(int new_imgtype, int dithering_method);
|
||||
void setImgType(int new_imgtype, DitheringMethod dithering_method);
|
||||
|
||||
// for images in stock
|
||||
int addImageInStock(Image* image);
|
||||
|
@ -23,8 +23,8 @@
|
||||
|
||||
#include "gui/jinete.h"
|
||||
|
||||
#include "console.h"
|
||||
#include "app.h"
|
||||
#include "console.h"
|
||||
#include "modules/editors.h"
|
||||
#include "modules/gfx.h"
|
||||
#include "modules/gui.h"
|
||||
@ -33,7 +33,7 @@
|
||||
#include "raster/image.h"
|
||||
#include "raster/layer.h"
|
||||
#include "raster/palette.h"
|
||||
#include "raster/quant.h"
|
||||
#include "raster/quantization.h"
|
||||
#include "raster/rotate.h"
|
||||
#include "raster/sprite.h"
|
||||
#include "raster/stock.h"
|
||||
@ -243,8 +243,8 @@ void clipboard::paste(SpriteWriter& sprite)
|
||||
src_image = clipboard_image;
|
||||
else {
|
||||
RgbMap* rgbmap = sprite->getRgbMap();
|
||||
src_image = image_set_imgtype(clipboard_image, sprite->getImgType(), DITHERING_NONE,
|
||||
rgbmap, sprite->getPalette(sprite->getCurrentFrame()));
|
||||
src_image = quantization::convert_imgtype(clipboard_image, sprite->getImgType(), DITHERING_NONE,
|
||||
rgbmap, sprite->getPalette(sprite->getCurrentFrame()));
|
||||
}
|
||||
|
||||
// Do the interactive-transform loop (where the user can move the floating image)
|
||||
|
Loading…
x
Reference in New Issue
Block a user