Add Hsv and Rgb classes to gfx-lib.

This commit is contained in:
David Capello 2010-12-07 22:47:44 -03:00
parent 7ace9a2099
commit 9dcb29749d
7 changed files with 454 additions and 0 deletions

View File

@ -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
View 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
View 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
View 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
View 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
View 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
View 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)));
}