aseprite/src/doc/palette.cpp

249 lines
4.8 KiB
C++
Raw Normal View History

// Aseprite Document Library
// Copyright (c) 2001-2015 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
2008-03-23 05:44:03 +08:00
#ifdef HAVE_CONFIG_H
2008-03-23 05:44:03 +08:00
#include "config.h"
#endif
2008-03-23 05:44:03 +08:00
#include "doc/palette.h"
2008-03-23 05:44:03 +08:00
#include "doc/image.h"
2015-05-09 01:28:27 +08:00
#include "doc/remap.h"
#include <algorithm>
#include <limits>
namespace doc {
using namespace gfx;
2008-03-23 05:44:03 +08:00
Palette::Palette(frame_t frame, int ncolors)
: Object(ObjectType::Palette)
{
ASSERT(ncolors >= 0);
2008-03-23 05:44:03 +08:00
m_frame = frame;
m_colors.resize(ncolors, doc::rgba(0, 0, 0, 255));
m_modifications = 0;
}
2008-03-23 05:44:03 +08:00
Palette::Palette(const Palette& palette)
: Object(palette)
{
m_frame = palette.m_frame;
m_colors = palette.m_colors;
2010-04-20 09:43:41 +08:00
m_modifications = 0;
2008-03-23 05:44:03 +08:00
}
2015-05-09 01:28:27 +08:00
Palette::Palette(const Palette& palette, const Remap& remap)
: Object(palette)
{
m_frame = palette.m_frame;
resize(palette.size());
for (int i=0; i<size(); ++i)
setEntry(remap[i], palette.getEntry(i));
m_modifications = 0;
}
Palette::~Palette()
2008-03-23 05:44:03 +08:00
{
}
2008-03-23 05:44:03 +08:00
Palette* Palette::createGrayscale()
{
Palette* graypal = new Palette(frame_t(0), 256);
for (int c=0; c<256; c++)
graypal->setEntry(c, rgba(c, c, c, 255));
return graypal;
}
2008-03-23 05:44:03 +08:00
void Palette::resize(int ncolors)
{
ASSERT(ncolors >= 0);
2008-03-23 05:44:03 +08:00
m_colors.resize(ncolors, doc::rgba(0, 0, 0, 255));
2010-04-20 09:43:41 +08:00
++m_modifications;
2008-03-23 05:44:03 +08:00
}
void Palette::addEntry(color_t color)
{
resize(size()+1);
setEntry(size()-1, color);
}
void Palette::setFrame(frame_t frame)
2008-03-23 05:44:03 +08:00
{
ASSERT(frame >= 0);
2008-03-23 05:44:03 +08:00
m_frame = frame;
2008-03-23 05:44:03 +08:00
}
void Palette::setEntry(int i, color_t color)
2008-03-23 05:44:03 +08:00
{
ASSERT(i >= 0 && i < size());
2008-03-23 05:44:03 +08:00
m_colors[i] = color;
2010-04-20 09:43:41 +08:00
++m_modifications;
}
2008-03-23 05:44:03 +08:00
void Palette::copyColorsTo(Palette* dst) const
{
dst->m_colors = m_colors;
2010-04-20 09:43:41 +08:00
++dst->m_modifications;
2008-03-23 05:44:03 +08:00
}
int Palette::countDiff(const Palette* other, int* from, int* to) const
2008-03-23 05:44:03 +08:00
{
int c, diff = 0;
int min = MIN(this->m_colors.size(), other->m_colors.size());
int max = MAX(this->m_colors.size(), other->m_colors.size());
2008-03-23 05:44:03 +08:00
if (from) *from = -1;
if (to) *to = -1;
// Compare palettes
2008-03-23 05:44:03 +08:00
for (c=0; c<min; ++c) {
if (this->m_colors[c] != other->m_colors[c]) {
2008-03-23 05:44:03 +08:00
if (from && *from < 0) *from = c;
if (to) *to = c;
++diff;
}
}
if (max != min) {
diff += max - min;
if (from && *from < 0) *from = min;
if (to) *to = max-1;
2008-03-23 05:44:03 +08:00
}
return diff;
}
bool Palette::isBlack() const
{
for (std::size_t c=0; c<m_colors.size(); ++c)
if (getEntry(c) != rgba(0, 0, 0, 255))
return false;
return true;
}
void Palette::makeBlack()
2008-03-23 05:44:03 +08:00
{
std::fill(m_colors.begin(), m_colors.end(), rgba(0, 0, 0, 255));
2010-04-20 09:43:41 +08:00
++m_modifications;
2008-03-23 05:44:03 +08:00
}
// Creates a linear ramp in the palette.
void Palette::makeGradient(int from, int to)
2008-03-23 05:44:03 +08:00
{
int r, g, b, a;
int r1, g1, b1, a1;
int r2, g2, b2, a2;
2008-03-23 05:44:03 +08:00
int i, n;
ASSERT(from >= 0 && from <= 255);
ASSERT(to >= 0 && to <= 255);
2008-03-23 05:44:03 +08:00
if (from > to)
std::swap(from, to);
2008-03-23 05:44:03 +08:00
n = to - from;
if (n < 2)
return;
r1 = rgba_getr(getEntry(from));
g1 = rgba_getg(getEntry(from));
b1 = rgba_getb(getEntry(from));
a1 = rgba_geta(getEntry(from));
r2 = rgba_getr(getEntry(to));
g2 = rgba_getg(getEntry(to));
b2 = rgba_getb(getEntry(to));
a2 = rgba_geta(getEntry(to));
2008-03-23 05:44:03 +08:00
for (i=from+1; i<to; ++i) {
r = r1 + (r2-r1) * (i-from) / n;
g = g1 + (g2-g1) * (i-from) / n;
b = b1 + (b2-b1) * (i-from) / n;
a = a1 + (a2-a1) * (i-from) / n;
setEntry(i, rgba(r, g, b, a));
2008-03-23 05:44:03 +08:00
}
}
int Palette::findExactMatch(int r, int g, int b, int a) const
{
for (int i=0; i<(int)m_colors.size(); ++i)
if (getEntry(i) == rgba(r, g, b, a))
return i;
return -1;
}
//////////////////////////////////////////////////////////////////////
// Based on Allegro's bestfit_color
2008-03-23 05:44:03 +08:00
static unsigned int col_diff[3*128];
2008-03-23 05:44:03 +08:00
static void bestfit_init()
2008-03-23 05:44:03 +08:00
{
int i, k;
2008-03-23 05:44:03 +08:00
for (i=1; i<64; i++) {
k = i * i;
col_diff[0 +i] = col_diff[0 +128-i] = k * (59 * 59);
col_diff[128+i] = col_diff[128+128-i] = k * (30 * 30);
col_diff[256+i] = col_diff[256+128-i] = k * (11 * 11);
}
}
int Palette::findBestfit(int r, int g, int b, int a, int mask_index) const
2008-03-23 05:44:03 +08:00
{
int i, bestfit, coldiff, lowest;
2008-03-23 05:44:03 +08:00
ASSERT(r >= 0 && r <= 255);
ASSERT(g >= 0 && g <= 255);
ASSERT(b >= 0 && b <= 255);
ASSERT(a >= 0 && a <= 255);
2008-03-23 05:44:03 +08:00
if (col_diff[1] == 0)
bestfit_init();
bestfit = 0;
lowest = std::numeric_limits<int>::max();
2008-03-23 05:44:03 +08:00
r >>= 3;
g >>= 3;
b >>= 3;
i = 0;
while (i < size()) {
color_t rgb = m_colors[i];
2008-03-23 05:44:03 +08:00
coldiff = (col_diff + 0) [ ((rgba_getg(rgb)>>3) - g) & 0x7F ];
2008-03-23 05:44:03 +08:00
if (coldiff < lowest) {
coldiff += (col_diff + 128) [ ((rgba_getr(rgb)>>3) - r) & 0x7F ];
2008-03-23 05:44:03 +08:00
if (coldiff < lowest) {
coldiff += (col_diff + 256) [ ((rgba_getb(rgb)>>3) - b) & 0x7F ];
if (coldiff < lowest && i != mask_index) {
bestfit = i;
if (coldiff == 0)
return bestfit;
lowest = coldiff;
}
2008-03-23 05:44:03 +08:00
}
}
i++;
}
return bestfit;
}
} // namespace doc