mirror of
https://github.com/aseprite/aseprite.git
synced 2025-02-06 12:39:57 +00:00
Add more precision to HSV <-> RGB conversion (fix #961)
This patch fixes problems using the eyedropper tool in HSB mode. If we use "int" precision for HSB values, the resulting RGB color could be different from the original one.
This commit is contained in:
parent
19f8aad2ef
commit
3f47c23cd8
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
@ -46,7 +46,7 @@ Color Color::fromRgb(int r, int g, int b, int a)
|
||||
}
|
||||
|
||||
// static
|
||||
Color Color::fromHsv(int h, int s, int v, int a)
|
||||
Color Color::fromHsv(double h, double s, double v, int a)
|
||||
{
|
||||
Color color(Color::HsvType);
|
||||
color.m_value.hsv.h = h;
|
||||
@ -124,24 +124,25 @@ Color Color::fromString(const std::string& str)
|
||||
if (str.find("rgb{") == 0 ||
|
||||
str.find("hsv{") == 0 ||
|
||||
str.find("gray{") == 0) {
|
||||
int c = 0, table[4] = { 0, 0, 0, 255 };
|
||||
int c = 0;
|
||||
double table[4] = { 0.0, 0.0, 0.0, 255.0 };
|
||||
std::string::size_type i = str.find_first_of('{')+1, j;
|
||||
|
||||
while ((j = str.find_first_of(",}", i)) != std::string::npos) {
|
||||
std::string element = str.substr(i, j - i);
|
||||
if (c < 4)
|
||||
table[c++] = std::strtol(element.c_str(), NULL, 10);
|
||||
table[c++] = std::strtod(element.c_str(), NULL);
|
||||
if (c >= 4)
|
||||
break;
|
||||
i = j+1;
|
||||
}
|
||||
|
||||
if (str[0] == 'r')
|
||||
color = Color::fromRgb(table[0], table[1], table[2], table[3]);
|
||||
color = Color::fromRgb(table[0], table[1], table[2], int(table[3]));
|
||||
else if (str[0] == 'h')
|
||||
color = Color::fromHsv(table[0], table[1], table[2], table[3]);
|
||||
color = Color::fromHsv(table[0], table[1], table[2], int(table[3]));
|
||||
else if (str[0] == 'g')
|
||||
color = Color::fromGray(table[0], c >= 2 ? table[1]: 255);
|
||||
color = Color::fromGray(table[0], (c >= 2 ? int(table[1]): 255));
|
||||
}
|
||||
else if (str.find("index{") == 0) {
|
||||
color = Color::fromIndex(std::strtol(str.c_str()+6, NULL, 10));
|
||||
@ -171,6 +172,8 @@ std::string Color::toString() const
|
||||
|
||||
case Color::HsvType:
|
||||
result << "hsv{"
|
||||
<< std::setprecision(2)
|
||||
<< std::fixed
|
||||
<< m_value.hsv.h << ","
|
||||
<< m_value.hsv.s << ","
|
||||
<< m_value.hsv.v << ","
|
||||
@ -225,9 +228,9 @@ std::string Color::toHumanReadableString(PixelFormat pixelFormat, HumanReadableS
|
||||
}
|
||||
else {
|
||||
result << "HSB "
|
||||
<< m_value.hsv.h << "\xc2\xb0 "
|
||||
<< m_value.hsv.s << " "
|
||||
<< m_value.hsv.v;
|
||||
<< int(m_value.hsv.h) << "\xc2\xb0 "
|
||||
<< int(m_value.hsv.s) << "% "
|
||||
<< int(m_value.hsv.v) << "%";
|
||||
|
||||
if (pixelFormat == IMAGE_INDEXED)
|
||||
result << " Index " << color_utils::color_for_image(*this, pixelFormat);
|
||||
@ -290,9 +293,9 @@ std::string Color::toHumanReadableString(PixelFormat pixelFormat, HumanReadableS
|
||||
result << "Gry-" << getGray();
|
||||
}
|
||||
else {
|
||||
result << m_value.hsv.h << "\xc2\xb0"
|
||||
<< m_value.hsv.s << ","
|
||||
<< m_value.hsv.v;
|
||||
result << int(m_value.hsv.h) << "\xc2\xb0"
|
||||
<< int(m_value.hsv.s) << ","
|
||||
<< int(m_value.hsv.v);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -332,10 +335,10 @@ bool Color::operator==(const Color& other) const
|
||||
|
||||
case Color::HsvType:
|
||||
return
|
||||
m_value.hsv.h == other.m_value.hsv.h &&
|
||||
m_value.hsv.s == other.m_value.hsv.s &&
|
||||
m_value.hsv.v == other.m_value.hsv.v &&
|
||||
m_value.hsv.a == other.m_value.hsv.a;
|
||||
(std::fabs(m_value.hsv.h - other.m_value.hsv.h) < 0.001) &&
|
||||
(std::fabs(m_value.hsv.s - other.m_value.hsv.s) < 0.001) &&
|
||||
(std::fabs(m_value.hsv.v - other.m_value.hsv.v) < 0.001) &&
|
||||
(m_value.hsv.a == other.m_value.hsv.a);
|
||||
|
||||
case Color::GrayType:
|
||||
return
|
||||
@ -379,8 +382,8 @@ int Color::getRed() const
|
||||
|
||||
case Color::HsvType:
|
||||
return Rgb(Hsv(m_value.hsv.h,
|
||||
double(m_value.hsv.s) / 100.0,
|
||||
double(m_value.hsv.v) / 100.0)).red();
|
||||
m_value.hsv.s / 100.0,
|
||||
m_value.hsv.v / 100.0)).red();
|
||||
|
||||
case Color::GrayType:
|
||||
return m_value.gray.g;
|
||||
@ -411,8 +414,8 @@ int Color::getGreen() const
|
||||
|
||||
case Color::HsvType:
|
||||
return Rgb(Hsv(m_value.hsv.h,
|
||||
double(m_value.hsv.s) / 100.0,
|
||||
double(m_value.hsv.v) / 100.0)).green();
|
||||
m_value.hsv.s / 100.0,
|
||||
m_value.hsv.v / 100.0)).green();
|
||||
|
||||
case Color::GrayType:
|
||||
return m_value.gray.g;
|
||||
@ -443,8 +446,8 @@ int Color::getBlue() const
|
||||
|
||||
case Color::HsvType:
|
||||
return Rgb(Hsv(m_value.hsv.h,
|
||||
double(m_value.hsv.s) / 100.0,
|
||||
double(m_value.hsv.v) / 100.0)).blue();
|
||||
m_value.hsv.s / 100.0,
|
||||
m_value.hsv.v / 100.0)).blue();
|
||||
|
||||
case Color::GrayType:
|
||||
return m_value.gray.g;
|
||||
@ -463,23 +466,23 @@ int Color::getBlue() const
|
||||
return -1;
|
||||
}
|
||||
|
||||
int Color::getHue() const
|
||||
double Color::getHue() const
|
||||
{
|
||||
switch (getType()) {
|
||||
|
||||
case Color::MaskType:
|
||||
return 0;
|
||||
return 0.0;
|
||||
|
||||
case Color::RgbType:
|
||||
return Hsv(Rgb(m_value.rgb.r,
|
||||
m_value.rgb.g,
|
||||
m_value.rgb.b)).hueInt();
|
||||
m_value.rgb.b)).hue();
|
||||
|
||||
case Color::HsvType:
|
||||
return m_value.hsv.h;
|
||||
|
||||
case Color::GrayType:
|
||||
return 0;
|
||||
return 0.0;
|
||||
|
||||
case Color::IndexType: {
|
||||
int i = m_value.index;
|
||||
@ -487,19 +490,19 @@ int Color::getHue() const
|
||||
uint32_t c = get_current_palette()->getEntry(i);
|
||||
return Hsv(Rgb(rgba_getr(c),
|
||||
rgba_getg(c),
|
||||
rgba_getb(c))).hueInt();
|
||||
rgba_getb(c))).hue();
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ASSERT(false);
|
||||
return -1;
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
int Color::getSaturation() const
|
||||
double Color::getSaturation() const
|
||||
{
|
||||
switch (getType()) {
|
||||
|
||||
@ -509,7 +512,7 @@ int Color::getSaturation() const
|
||||
case Color::RgbType:
|
||||
return Hsv(Rgb(m_value.rgb.r,
|
||||
m_value.rgb.g,
|
||||
m_value.rgb.b)).saturationInt();
|
||||
m_value.rgb.b)).saturation() * 100.0;
|
||||
|
||||
case Color::HsvType:
|
||||
return m_value.hsv.s;
|
||||
@ -523,35 +526,35 @@ int Color::getSaturation() const
|
||||
uint32_t c = get_current_palette()->getEntry(i);
|
||||
return Hsv(Rgb(rgba_getr(c),
|
||||
rgba_getg(c),
|
||||
rgba_getb(c))).saturationInt();
|
||||
rgba_getb(c))).saturation() * 100.0;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ASSERT(false);
|
||||
return -1;
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
int Color::getValue() const
|
||||
double Color::getValue() const
|
||||
{
|
||||
switch (getType()) {
|
||||
|
||||
case Color::MaskType:
|
||||
return 0;
|
||||
return 0.0;
|
||||
|
||||
case Color::RgbType:
|
||||
return Hsv(Rgb(m_value.rgb.r,
|
||||
m_value.rgb.g,
|
||||
m_value.rgb.b)).valueInt();
|
||||
m_value.rgb.b)).value() * 100.0;
|
||||
|
||||
case Color::HsvType:
|
||||
return m_value.hsv.v;
|
||||
|
||||
case Color::GrayType:
|
||||
return 100 * m_value.gray.g / 255;
|
||||
return 100.0 * m_value.gray.g / 255.0;
|
||||
|
||||
case Color::IndexType: {
|
||||
int i = m_value.index;
|
||||
@ -559,16 +562,16 @@ int Color::getValue() const
|
||||
uint32_t c = get_current_palette()->getEntry(i);
|
||||
return Hsv(Rgb(rgba_getr(c),
|
||||
rgba_getg(c),
|
||||
rgba_getb(c))).valueInt();
|
||||
rgba_getb(c))).value() * 100.0;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ASSERT(false);
|
||||
return -1;
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
int Color::getGray() const
|
||||
@ -579,12 +582,12 @@ int Color::getGray() const
|
||||
return 0;
|
||||
|
||||
case Color::RgbType:
|
||||
return 255 * Hsv(Rgb(m_value.rgb.r,
|
||||
m_value.rgb.g,
|
||||
m_value.rgb.b)).valueInt() / 100;
|
||||
return int(255.0 * Hsv(Rgb(m_value.rgb.r,
|
||||
m_value.rgb.g,
|
||||
m_value.rgb.b)).value() / 100.0);
|
||||
|
||||
case Color::HsvType:
|
||||
return 255 * m_value.hsv.v / 100;
|
||||
return int(255.0 * m_value.hsv.v / 100.0);
|
||||
|
||||
case Color::GrayType:
|
||||
return m_value.gray.g;
|
||||
@ -593,9 +596,9 @@ int Color::getGray() const
|
||||
int i = m_value.index;
|
||||
if (i >= 0 && i < get_current_palette()->size()) {
|
||||
uint32_t c = get_current_palette()->getEntry(i);
|
||||
return 255 * Hsv(Rgb(rgba_getr(c),
|
||||
rgba_getg(c),
|
||||
rgba_getb(c))).valueInt() / 100;
|
||||
return int(255.0 * Hsv(Rgb(rgba_getr(c),
|
||||
rgba_getg(c),
|
||||
rgba_getb(c))).value() / 100.0);
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
@ -43,7 +43,7 @@ namespace app {
|
||||
|
||||
static Color fromMask();
|
||||
static Color fromRgb(int r, int g, int b, int a = 255);
|
||||
static Color fromHsv(int h, int s, int v, int a = 255); // h=[0,360], s=[0,100], v=[0,100]
|
||||
static Color fromHsv(double h, double s, double v, int a = 255); // h=[0,360], s=[0,100], v=[0,100]
|
||||
static Color fromGray(int g, int a = 255);
|
||||
static Color fromIndex(int index);
|
||||
|
||||
@ -69,9 +69,9 @@ namespace app {
|
||||
int getRed() const;
|
||||
int getGreen() const;
|
||||
int getBlue() const;
|
||||
int getHue() const;
|
||||
int getSaturation() const;
|
||||
int getValue() const;
|
||||
double getHue() const;
|
||||
double getSaturation() const;
|
||||
double getValue() const;
|
||||
int getGray() const;
|
||||
int getIndex() const;
|
||||
int getAlpha() const;
|
||||
@ -88,7 +88,8 @@ namespace app {
|
||||
int r, g, b, a;
|
||||
} rgb;
|
||||
struct {
|
||||
int h, s, v, a;
|
||||
double h, s, v;
|
||||
int a;
|
||||
} hsv;
|
||||
struct {
|
||||
int g, a;
|
||||
|
@ -283,9 +283,9 @@ HsvSliders::HsvSliders()
|
||||
|
||||
void HsvSliders::onSetColor(const app::Color& color)
|
||||
{
|
||||
setAbsSliderValue(0, color.getHue());
|
||||
setAbsSliderValue(1, color.getSaturation());
|
||||
setAbsSliderValue(2, color.getValue());
|
||||
setAbsSliderValue(0, int(color.getHue()));
|
||||
setAbsSliderValue(1, int(color.getSaturation()));
|
||||
setAbsSliderValue(2, int(color.getValue()));
|
||||
setAbsSliderValue(3, color.getAlpha());
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
@ -60,14 +60,14 @@ app::Color ColorSpectrum::pickColor(const gfx::Point& pos) const
|
||||
umax = MAX(1, rc.h-1);
|
||||
}
|
||||
|
||||
int hue = 360 * u / umax;
|
||||
int sat = (v < vmid ? 100 * v / vmid : 100);
|
||||
int val = (v < vmid ? 100 : 100-(100 * (v-vmid) / vmid));
|
||||
double hue = 360.0 * u / umax;
|
||||
double sat = (v < vmid ? 100.0 * v / vmid : 100.0);
|
||||
double val = (v < vmid ? 100.0 : 100.0-(100.0 * (v-vmid) / vmid));
|
||||
|
||||
return app::Color::fromHsv(
|
||||
MID(0, hue, 360),
|
||||
MID(0, sat, 100),
|
||||
MID(0, val, 100));
|
||||
MID(0.0, hue, 360.0),
|
||||
MID(0.0, sat, 100.0),
|
||||
MID(0.0, val, 100.0));
|
||||
}
|
||||
|
||||
void ColorSpectrum::selectColor(const app::Color& color)
|
||||
@ -116,32 +116,32 @@ void ColorSpectrum::onPaint(ui::PaintEvent& ev)
|
||||
umax = MAX(1, rc.h-1);
|
||||
}
|
||||
|
||||
int hue = 360 * u / umax;
|
||||
int sat = (v < vmid ? 100 * v / vmid : 100);
|
||||
int val = (v < vmid ? 100 : 100-(100 * (v-vmid) / vmid));
|
||||
double hue = 360.0 * u / umax;
|
||||
double sat = (v < vmid ? 100.0 * v / vmid : 100.0);
|
||||
double val = (v < vmid ? 100.0 : 100.0-(100.0 * (v-vmid) / vmid));
|
||||
|
||||
gfx::Color color = color_utils::color_for_ui(
|
||||
app::Color::fromHsv(
|
||||
MID(0, hue, 360),
|
||||
MID(0, sat, 100),
|
||||
MID(0, val, 100)));
|
||||
MID(0.0, hue, 360.0),
|
||||
MID(0.0, sat, 100.0),
|
||||
MID(0.0, val, 100.0)));
|
||||
|
||||
g->putPixel(color, rc.x+x, rc.y+y);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_color.getType() != app::Color::MaskType) {
|
||||
int hue = m_color.getHue();
|
||||
int sat = m_color.getSaturation();
|
||||
int val = m_color.getValue();
|
||||
int lit = (200 - sat) * val / 200;
|
||||
gfx::Point pos(rc.x + hue * rc.w / 360,
|
||||
rc.y + rc.h - (lit * rc.h / 100));
|
||||
double hue = m_color.getHue();
|
||||
double sat = m_color.getSaturation();
|
||||
double val = m_color.getValue();
|
||||
double lit = (200.0 - sat) * val / 200.0;
|
||||
gfx::Point pos(rc.x + int(hue * rc.w / 360.0),
|
||||
rc.y + rc.h - int(lit * rc.h / 100.0));
|
||||
|
||||
she::Surface* icon = theme->parts.colorWheelIndicator()->bitmap(0);
|
||||
g->drawColoredRgbaSurface(
|
||||
icon,
|
||||
lit > 50 ? gfx::rgba(0, 0, 0): gfx::rgba(255, 255, 255),
|
||||
lit > 50.0 ? gfx::rgba(0, 0, 0): gfx::rgba(255, 255, 255),
|
||||
pos.x-icon->width()/2,
|
||||
pos.y-icon->height()/2);
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ app::Color ColorWheel::pickColor(const gfx::Point& pos) const
|
||||
boxsize, boxsize).contains(pos)) {
|
||||
m_harmonyPicked = true;
|
||||
|
||||
color = app::Color::fromHsv(convertHueAngle(color.getHue(), 1),
|
||||
color = app::Color::fromHsv(convertHueAngle(int(color.getHue()), 1),
|
||||
color.getSaturation(),
|
||||
color.getValue());
|
||||
return color;
|
||||
@ -186,10 +186,10 @@ app::Color ColorWheel::getColorInHarmony(int j) const
|
||||
{
|
||||
int i = MID(0, (int)m_harmony, (int)Harmony::LAST);
|
||||
j = MID(0, j, harmonies[i].n-1);
|
||||
int hue = convertHueAngle(m_mainColor.getHue(), -1) + harmonies[i].hues[j];
|
||||
int sat = m_mainColor.getSaturation() * harmonies[i].sats[j] / 100;
|
||||
return app::Color::fromHsv(hue % 360,
|
||||
MID(0, sat, 100),
|
||||
double hue = convertHueAngle(int(m_mainColor.getHue()), -1) + harmonies[i].hues[j];
|
||||
double sat = m_mainColor.getSaturation() * harmonies[i].sats[j] / 100.0;
|
||||
return app::Color::fromHsv(std::fmod(hue, 360),
|
||||
MID(0.0, sat, 100.0),
|
||||
m_mainColor.getValue());
|
||||
}
|
||||
|
||||
@ -253,17 +253,17 @@ void ColorWheel::onPaint(ui::PaintEvent& ev)
|
||||
|
||||
for (int i=0; i<n; ++i) {
|
||||
app::Color color = getColorInHarmony(i);
|
||||
int angle = color.getHue()-30;
|
||||
int dist = color.getSaturation();
|
||||
double angle = color.getHue()-30.0;
|
||||
double dist = color.getSaturation();
|
||||
|
||||
color = app::Color::fromHsv(convertHueAngle(color.getHue(), 1),
|
||||
color = app::Color::fromHsv(convertHueAngle(int(color.getHue()), 1),
|
||||
color.getSaturation(),
|
||||
color.getValue());
|
||||
|
||||
gfx::Point pos =
|
||||
m_wheelBounds.center() +
|
||||
gfx::Point(int(+std::cos(PI*angle/180)*double(m_wheelRadius)*dist/100.0),
|
||||
int(-std::sin(PI*angle/180)*double(m_wheelRadius)*dist/100.0));
|
||||
gfx::Point(int(+std::cos(PI*angle/180.0)*double(m_wheelRadius)*dist/100.0),
|
||||
int(-std::sin(PI*angle/180.0)*double(m_wheelRadius)*dist/100.0));
|
||||
|
||||
she::Surface* icon = theme->parts.colorWheelIndicator()->bitmap(0);
|
||||
g->drawRgbaSurface(icon,
|
||||
|
Loading…
x
Reference in New Issue
Block a user