mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-23 22:43:32 +00:00
Add Hsv and Rgb classes to gfx-lib.
This commit is contained in:
parent
7ace9a2099
commit
9dcb29749d
@ -2,6 +2,8 @@
|
||||
# Copyright (C) 2001-2010 David Capello
|
||||
|
||||
add_library(gfx-lib
|
||||
hsv.cpp
|
||||
point.cpp
|
||||
rect.cpp
|
||||
rgb.cpp
|
||||
size.cpp)
|
||||
|
71
src/gfx/hsv.cpp
Normal file
71
src/gfx/hsv.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
// ASE gfx library
|
||||
// Copyright (C) 2001-2010 David Capello
|
||||
//
|
||||
// This source file is ditributed under a BSD-like license, please
|
||||
// read LICENSE.txt for more information.
|
||||
|
||||
#include "gfx/hsv.h"
|
||||
#include "gfx/rgb.h"
|
||||
#include <cmath>
|
||||
|
||||
using namespace gfx;
|
||||
using namespace std;
|
||||
|
||||
Hsv::Hsv(double hue, double saturation, double value)
|
||||
: m_hue(hue)
|
||||
, m_saturation(saturation)
|
||||
, m_value(value)
|
||||
{
|
||||
while (m_hue < 0.0)
|
||||
m_hue += 360.0;
|
||||
m_hue = fmod(hue, 360.0);
|
||||
|
||||
assert(hue >= 0.0 && hue <= 360.0);
|
||||
assert(saturation >= 0.0 && saturation <= 1.0);
|
||||
assert(value >= 0.0 && value <= 1.0);
|
||||
}
|
||||
|
||||
// Reference: http://en.wikipedia.org/wiki/HSL_and_HSV
|
||||
Hsv::Hsv(const Rgb& rgb)
|
||||
{
|
||||
int M = rgb.maxComponent();
|
||||
int m = rgb.minComponent();
|
||||
int c = M - m;
|
||||
double chroma = double(c) / 255.0;
|
||||
double hue_prime;
|
||||
double h, s, v;
|
||||
double r, g, b;
|
||||
|
||||
v = double(M) / 255.0;
|
||||
|
||||
if (c == 0) {
|
||||
h = 0.0; // Undefined Hue because max == min
|
||||
s = 0.0;
|
||||
}
|
||||
else {
|
||||
r = double(rgb.red()) / 255.0;
|
||||
g = double(rgb.green()) / 255.0;
|
||||
b = double(rgb.blue()) / 255.0;
|
||||
s = chroma / v;
|
||||
|
||||
if (M == rgb.red()) {
|
||||
hue_prime = (g - b) / chroma;
|
||||
|
||||
while (hue_prime < 0.0)
|
||||
hue_prime += 6.0;
|
||||
hue_prime = fmod(hue_prime, 6.0);
|
||||
}
|
||||
else if (M == rgb.green()) {
|
||||
hue_prime = ((b - r) / chroma) + 2.0;
|
||||
}
|
||||
else if (M == rgb.blue()) {
|
||||
hue_prime = ((r - g) / chroma) + 4.0;
|
||||
}
|
||||
|
||||
h = hue_prime * 60.0;
|
||||
}
|
||||
|
||||
m_hue = h;
|
||||
m_saturation = s;
|
||||
m_value = v;
|
||||
}
|
87
src/gfx/hsv.h
Normal file
87
src/gfx/hsv.h
Normal file
@ -0,0 +1,87 @@
|
||||
// ASE gfx library
|
||||
// Copyright (C) 2001-2010 David Capello
|
||||
//
|
||||
// This source file is ditributed under a BSD-like license, please
|
||||
// read LICENSE.txt for more information.
|
||||
|
||||
#ifndef GFX_HSV_H_INCLUDED
|
||||
#define GFX_HSV_H_INCLUDED
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace gfx {
|
||||
|
||||
class Rgb;
|
||||
|
||||
class Hsv
|
||||
{
|
||||
public:
|
||||
Hsv()
|
||||
: m_hue(0.0)
|
||||
, m_saturation(0.0)
|
||||
, m_value(0.0)
|
||||
{ }
|
||||
|
||||
Hsv(double hue, double saturation, double value);
|
||||
|
||||
Hsv(const Hsv& hsv)
|
||||
: m_hue(hsv.hue())
|
||||
, m_saturation(hsv.saturation())
|
||||
, m_value(hsv.value())
|
||||
{ }
|
||||
|
||||
// RGB to HSV conversion
|
||||
explicit Hsv(const Rgb& rgb);
|
||||
|
||||
double hue() const {
|
||||
return m_hue;
|
||||
}
|
||||
|
||||
double saturation() const {
|
||||
return m_saturation;
|
||||
}
|
||||
|
||||
double value() const {
|
||||
return m_value;
|
||||
}
|
||||
|
||||
// Getters in "int" type, hue=[0,360), saturation=[0,100], value=[0,100]
|
||||
int hueInt() const { return int(m_hue+0.5); }
|
||||
int saturationInt() const { return int(m_saturation*100.0+0.5); }
|
||||
int valueInt() const { return int(m_value*100.0+0.5); }
|
||||
|
||||
void hue(double hue) {
|
||||
assert(hue >= 0.0 && hue <= 360.0);
|
||||
m_hue = hue;
|
||||
}
|
||||
|
||||
void saturation(double saturation) {
|
||||
assert(saturation >= 0.0 && saturation <= 1.0);
|
||||
m_saturation = saturation;
|
||||
}
|
||||
|
||||
void value(double value) {
|
||||
assert(value >= 0.0 && value <= 1.0);
|
||||
m_value = value;
|
||||
}
|
||||
|
||||
bool operator==(const Hsv& other) const {
|
||||
// Compare floating points as integers (saturation and value are compared in [0,255] range)
|
||||
return (int(m_hue+0.5) == int(other.m_hue+0.5) &&
|
||||
int(m_saturation*255.0+0.5) == int(other.m_saturation*255.0+0.5) &&
|
||||
int(m_value*255.0+0.5) == int(other.m_value*255.0+0.5));
|
||||
}
|
||||
|
||||
bool operator!=(const Hsv& other) const {
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
private:
|
||||
double m_hue;
|
||||
double m_saturation;
|
||||
double m_value;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
|
||||
#endif
|
42
src/gfx/hsv_unittest.cpp
Normal file
42
src/gfx/hsv_unittest.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
// ASE gfx library
|
||||
// Copyright (C) 2001-2010 David Capello
|
||||
//
|
||||
// This source file is ditributed under a BSD-like license, please
|
||||
// read LICENSE.txt for more information.
|
||||
|
||||
#include "tests/test.h"
|
||||
|
||||
#include "gfx/hsv.h"
|
||||
#include "gfx/rgb.h"
|
||||
|
||||
using namespace gfx;
|
||||
using namespace std;
|
||||
|
||||
ostream& operator<<(ostream& os, const Hsv& hsv)
|
||||
{
|
||||
return os << "("
|
||||
<< hsv.hueInt() << ", "
|
||||
<< hsv.saturationInt() << ", "
|
||||
<< hsv.valueInt() << "); real: ("
|
||||
<< hsv.hue() << ", "
|
||||
<< hsv.saturation() << ", "
|
||||
<< hsv.value() << ")";
|
||||
}
|
||||
|
||||
TEST(Hsv, FromRgb)
|
||||
{
|
||||
EXPECT_EQ(Hsv( 0.0, 0.000, 0.000), Hsv(Rgb( 0, 0, 0)));
|
||||
EXPECT_EQ(Hsv( 0.0, 1.000, 0.010), Hsv(Rgb( 3, 0, 0)));
|
||||
EXPECT_EQ(Hsv( 0.0, 1.000, 0.990), Hsv(Rgb(252, 0, 0)));
|
||||
EXPECT_EQ(Hsv( 0.0, 1.000, 1.000), Hsv(Rgb(255, 0, 0)));
|
||||
EXPECT_EQ(Hsv( 60.0, 1.000, 0.750), Hsv(Rgb(191, 191, 0)));
|
||||
EXPECT_EQ(Hsv(120.0, 1.000, 0.500), Hsv(Rgb( 0, 128, 0)));
|
||||
EXPECT_EQ(Hsv(120.0, 1.000, 1.000), Hsv(Rgb( 0, 255, 0)));
|
||||
EXPECT_EQ(Hsv(180.0, 0.500, 1.000), Hsv(Rgb(128, 255, 255)));
|
||||
EXPECT_EQ(Hsv(240.0, 0.500, 1.000), Hsv(Rgb(128, 128, 255)));
|
||||
EXPECT_EQ(Hsv(240.0, 1.000, 1.000), Hsv(Rgb( 0, 0, 255)));
|
||||
EXPECT_EQ(Hsv(300.0, 0.667, 0.750), Hsv(Rgb(191, 64, 191)));
|
||||
EXPECT_EQ(Hsv(360.0, 1.000, 0.990), Hsv(Rgb(252, 0, 0)));
|
||||
EXPECT_EQ(Hsv(360.0, 1.000, 1.000), Hsv(Rgb(255, 0, 0)));
|
||||
}
|
||||
|
79
src/gfx/rgb.cpp
Normal file
79
src/gfx/rgb.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
// ASE gfx library
|
||||
// Copyright (C) 2001-2010 David Capello
|
||||
//
|
||||
// This source file is ditributed under a BSD-like license, please
|
||||
// read LICENSE.txt for more information.
|
||||
|
||||
#include "gfx/rgb.h"
|
||||
#include "gfx/hsv.h"
|
||||
#include <cmath>
|
||||
|
||||
using namespace gfx;
|
||||
using namespace std;
|
||||
|
||||
// Reference: http://en.wikipedia.org/wiki/HSL_and_HSV
|
||||
Rgb::Rgb(const Hsv& hsv)
|
||||
{
|
||||
double chroma = hsv.value() * hsv.saturation();
|
||||
double hue_prime = hsv.hue() / 60.0;
|
||||
double x = chroma * (1.0 - fabs(fmod(hue_prime, 2.0) - 1.0));
|
||||
double r, g, b;
|
||||
|
||||
r = g = b = 0.0;
|
||||
|
||||
switch (int(hue_prime)) {
|
||||
|
||||
case 6:
|
||||
case 0:
|
||||
r = chroma;
|
||||
g = x;
|
||||
break;
|
||||
case 1:
|
||||
r = x;
|
||||
g = chroma;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
g = chroma;
|
||||
b = x;
|
||||
break;
|
||||
case 3:
|
||||
g = x;
|
||||
b = chroma;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
b = chroma;
|
||||
r = x;
|
||||
break;
|
||||
case 5:
|
||||
b = x;
|
||||
r = chroma;
|
||||
break;
|
||||
}
|
||||
|
||||
double m = hsv.value() - chroma;
|
||||
r += m;
|
||||
g += m;
|
||||
b += m;
|
||||
|
||||
m_red = int(r*255.0+0.5);
|
||||
m_green = int(g*255.0+0.5);
|
||||
m_blue = int(b*255.0+0.5);
|
||||
}
|
||||
|
||||
int Rgb::maxComponent() const
|
||||
{
|
||||
if (m_red > m_green)
|
||||
return (m_red > m_blue) ? m_red: m_blue;
|
||||
else
|
||||
return (m_green > m_blue) ? m_green: m_blue;
|
||||
}
|
||||
|
||||
int Rgb::minComponent() const
|
||||
{
|
||||
if (m_red < m_green)
|
||||
return (m_red < m_blue) ? m_red: m_blue;
|
||||
else
|
||||
return (m_green < m_blue) ? m_green: m_blue;
|
||||
}
|
92
src/gfx/rgb.h
Normal file
92
src/gfx/rgb.h
Normal file
@ -0,0 +1,92 @@
|
||||
// ASE gfx library
|
||||
// Copyright (C) 2001-2010 David Capello
|
||||
//
|
||||
// This source file is ditributed under a BSD-like license, please
|
||||
// read LICENSE.txt for more information.
|
||||
|
||||
#ifndef GFX_RGB_H_INCLUDED
|
||||
#define GFX_RGB_H_INCLUDED
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace gfx {
|
||||
|
||||
class Hsv;
|
||||
|
||||
class Rgb
|
||||
{
|
||||
public:
|
||||
Rgb()
|
||||
: m_red(0)
|
||||
, m_green(0)
|
||||
, m_blue(0)
|
||||
{ }
|
||||
|
||||
Rgb(int red, int green, int blue)
|
||||
: m_red(red)
|
||||
, m_green(green)
|
||||
, m_blue(blue)
|
||||
{
|
||||
assert(red >= 0 && red <= 255);
|
||||
assert(green >= 0 && green <= 255);
|
||||
assert(blue >= 0 && blue <= 255);
|
||||
}
|
||||
|
||||
Rgb(const Rgb& rgb)
|
||||
: m_red(rgb.red())
|
||||
, m_green(rgb.green())
|
||||
, m_blue(rgb.blue())
|
||||
{ }
|
||||
|
||||
// HSV to RGB conversion
|
||||
explicit Rgb(const Hsv& hsv);
|
||||
|
||||
int red() const {
|
||||
return m_red;
|
||||
}
|
||||
|
||||
int green() const {
|
||||
return m_green;
|
||||
}
|
||||
|
||||
int blue() const {
|
||||
return m_blue;
|
||||
}
|
||||
|
||||
int maxComponent() const;
|
||||
int minComponent() const;
|
||||
|
||||
void red(int red) {
|
||||
assert(red >= 0 && red <= 255);
|
||||
m_red = red;
|
||||
}
|
||||
|
||||
void green(int green) {
|
||||
assert(green >= 0 && green <= 255);
|
||||
m_green = green;
|
||||
}
|
||||
|
||||
void blue(int blue) {
|
||||
assert(blue >= 0 && blue <= 255);
|
||||
m_blue = blue;
|
||||
}
|
||||
|
||||
bool operator==(const Rgb& other) const {
|
||||
return (m_red == other.m_red &&
|
||||
m_green == other.m_green &&
|
||||
m_blue == other.m_blue);
|
||||
}
|
||||
|
||||
bool operator!=(const Rgb& other) const {
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
private:
|
||||
int m_red;
|
||||
int m_green;
|
||||
int m_blue;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
|
||||
#endif
|
81
src/gfx/rgb_unittest.cpp
Normal file
81
src/gfx/rgb_unittest.cpp
Normal file
@ -0,0 +1,81 @@
|
||||
// ASE gfx library
|
||||
// Copyright (C) 2001-2010 David Capello
|
||||
//
|
||||
// This source file is ditributed under a BSD-like license, please
|
||||
// read LICENSE.txt for more information.
|
||||
|
||||
#include "tests/test.h"
|
||||
|
||||
#include "gfx/rgb.h"
|
||||
#include "gfx/hsv.h"
|
||||
|
||||
using namespace gfx;
|
||||
using namespace std;
|
||||
|
||||
ostream& operator<<(ostream& os, const Rgb& rgb)
|
||||
{
|
||||
return os << "("
|
||||
<< rgb.red() << ", "
|
||||
<< rgb.green() << ", "
|
||||
<< rgb.blue() << ")";
|
||||
}
|
||||
|
||||
TEST(Rgb, Ctor)
|
||||
{
|
||||
EXPECT_EQ(1, Rgb(1, 2, 3).red());
|
||||
EXPECT_EQ(2, Rgb(1, 2, 3).green());
|
||||
EXPECT_EQ(3, Rgb(1, 2, 3).blue());
|
||||
EXPECT_EQ(Rgb(0, 0, 0), Rgb());
|
||||
}
|
||||
|
||||
TEST(Rgb, Equal)
|
||||
{
|
||||
EXPECT_TRUE(Rgb(1, 2, 3) == Rgb(1, 2, 3));
|
||||
EXPECT_FALSE(Rgb(1, 2, 3) != Rgb(1, 2, 3));
|
||||
EXPECT_FALSE(Rgb(1, 2, 3) == Rgb(1, 3, 2));
|
||||
EXPECT_FALSE(Rgb(0, 0, 0) == Rgb(0, 0, 1));
|
||||
EXPECT_FALSE(Rgb(0, 0, 0) == Rgb(0, 1, 0));
|
||||
EXPECT_FALSE(Rgb(0, 0, 0) == Rgb(1, 0, 0));
|
||||
}
|
||||
|
||||
TEST(Rgb, MaxComponent)
|
||||
{
|
||||
EXPECT_EQ(3, Rgb(1, 2, 3).maxComponent());
|
||||
EXPECT_EQ(3, Rgb(1, 3, 2).maxComponent());
|
||||
EXPECT_EQ(3, Rgb(2, 1, 3).maxComponent());
|
||||
EXPECT_EQ(3, Rgb(2, 3, 1).maxComponent());
|
||||
EXPECT_EQ(3, Rgb(3, 1, 2).maxComponent());
|
||||
EXPECT_EQ(3, Rgb(3, 2, 1).maxComponent());
|
||||
}
|
||||
|
||||
TEST(Rgb, MinComponent)
|
||||
{
|
||||
EXPECT_EQ(1, Rgb(1, 2, 3).minComponent());
|
||||
EXPECT_EQ(1, Rgb(1, 3, 2).minComponent());
|
||||
EXPECT_EQ(1, Rgb(2, 1, 3).minComponent());
|
||||
EXPECT_EQ(1, Rgb(2, 3, 1).minComponent());
|
||||
EXPECT_EQ(1, Rgb(3, 1, 2).minComponent());
|
||||
EXPECT_EQ(1, Rgb(3, 2, 1).minComponent());
|
||||
}
|
||||
|
||||
TEST(Rgb, FromHsv)
|
||||
{
|
||||
for (double hue = 0.0; hue <= 360.0; hue += 10.0) {
|
||||
EXPECT_EQ(Rgb(255, 255, 255), Rgb(Hsv(hue, 0.000, 1.000)));
|
||||
EXPECT_EQ(Rgb(128, 128, 128), Rgb(Hsv(hue, 0.000, 0.500)));
|
||||
EXPECT_EQ(Rgb( 0, 0, 0), Rgb(Hsv(hue, 0.000, 0.000)));
|
||||
}
|
||||
|
||||
EXPECT_EQ(Rgb( 3, 0, 0), Rgb(Hsv( 0.0, 1.000, 0.010)));
|
||||
EXPECT_EQ(Rgb(252, 0, 0), Rgb(Hsv( 0.0, 1.000, 0.990)));
|
||||
EXPECT_EQ(Rgb(255, 0, 0), Rgb(Hsv( 0.0, 1.000, 1.000)));
|
||||
EXPECT_EQ(Rgb(191, 191, 0), Rgb(Hsv( 60.0, 1.000, 0.750)));
|
||||
EXPECT_EQ(Rgb( 0, 128, 0), Rgb(Hsv(120.0, 1.000, 0.500)));
|
||||
EXPECT_EQ(Rgb( 0, 255, 0), Rgb(Hsv(120.0, 1.000, 1.000)));
|
||||
EXPECT_EQ(Rgb(128, 255, 255), Rgb(Hsv(180.0, 0.500, 1.000)));
|
||||
EXPECT_EQ(Rgb(128, 128, 255), Rgb(Hsv(240.0, 0.500, 1.000)));
|
||||
EXPECT_EQ(Rgb( 0, 0, 255), Rgb(Hsv(240.0, 1.000, 1.000)));
|
||||
EXPECT_EQ(Rgb(191, 64, 191), Rgb(Hsv(300.0, 0.667, 0.750)));
|
||||
EXPECT_EQ(Rgb(252, 0, 0), Rgb(Hsv(360.0, 1.000, 0.990)));
|
||||
EXPECT_EQ(Rgb(255, 0, 0), Rgb(Hsv(360.0, 1.000, 1.000)));
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user