428 lines
11 KiB
C++
428 lines
11 KiB
C++
/*
|
|
QuickCG 20191227
|
|
|
|
Copyright (c) 2004-2019, Lode Vandevenne
|
|
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
|
|
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
QuickCG is an SDL codebase that wraps some of the SDL functionality.
|
|
It' used by Lode's Computer Graphics Tutorial to work with simple C++ calls
|
|
to demonstrate graphical programs.
|
|
|
|
QuickCG can handle some things that standard C++ doesn't but that are commonly useful, such as:
|
|
-drawing graphics
|
|
-a bitmap font
|
|
-simplified saving and loading of files
|
|
-reading keyboard and mouse input
|
|
-playing sound
|
|
-color models
|
|
-loading images
|
|
*/
|
|
|
|
#include "quickcg.h"
|
|
|
|
#include <cstdlib>
|
|
#include <cmath>
|
|
#include <vector>
|
|
#include <map>
|
|
#include <iostream>
|
|
#include <fstream>
|
|
|
|
namespace QuickCG
|
|
{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// COLOR STRUCTS/////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
ColorRGB::ColorRGB(Uint8 r, Uint8 g, Uint8 b)
|
|
{
|
|
this->r = r;
|
|
this->g = g;
|
|
this->b = b;
|
|
}
|
|
ColorRGB::ColorRGB(const ColorRGB8bit &color)
|
|
{
|
|
this->r = color.r;
|
|
this->g = color.g;
|
|
this->b = color.b;
|
|
}
|
|
ColorRGB::ColorRGB()
|
|
{
|
|
this->r = 0;
|
|
this->g = 0;
|
|
this->b = 0;
|
|
}
|
|
ColorRGB8bit::ColorRGB8bit(Uint8 r, Uint8 g, Uint8 b)
|
|
{
|
|
this->r = r;
|
|
this->g = g;
|
|
this->b = b;
|
|
}
|
|
ColorRGB8bit::ColorRGB8bit(const ColorRGB &color)
|
|
{
|
|
this->r = color.r;
|
|
this->g = color.g;
|
|
this->b = color.b;
|
|
}
|
|
ColorRGB8bit::ColorRGB8bit()
|
|
{
|
|
this->r = 0;
|
|
this->g = 0;
|
|
this->b = 0;
|
|
}
|
|
|
|
// Add two colors
|
|
ColorRGB operator+(const ColorRGB &color, const ColorRGB &color2)
|
|
{
|
|
ColorRGB c;
|
|
c.r = color.r + color2.r;
|
|
c.g = color.g + color2.g;
|
|
c.b = color.b + color2.b;
|
|
return c;
|
|
}
|
|
|
|
// Subtract two colors
|
|
ColorRGB operator-(const ColorRGB &color, const ColorRGB &color2)
|
|
{
|
|
ColorRGB c;
|
|
c.r = color.r - color2.r;
|
|
c.g = color.g - color2.g;
|
|
c.b = color.b - color2.b;
|
|
return c;
|
|
}
|
|
|
|
// Multiplies a color with an integer
|
|
ColorRGB operator*(const ColorRGB &color, int a)
|
|
{
|
|
ColorRGB c;
|
|
c.r = color.r * a;
|
|
c.g = color.g * a;
|
|
c.b = color.b * a;
|
|
return c;
|
|
}
|
|
|
|
// Multiplies a color with an integer
|
|
ColorRGB operator*(int a, const ColorRGB &color)
|
|
{
|
|
ColorRGB c;
|
|
c.r = color.r * a;
|
|
c.g = color.g * a;
|
|
c.b = color.b * a;
|
|
return c;
|
|
}
|
|
|
|
// Divides a color through an integer
|
|
ColorRGB operator/(const ColorRGB &color, int a)
|
|
{
|
|
if (a == 0)
|
|
return color;
|
|
ColorRGB c;
|
|
c.r = color.r / a;
|
|
c.g = color.g / a;
|
|
c.b = color.b / a;
|
|
return c;
|
|
}
|
|
|
|
// Are both colors equal?
|
|
bool operator==(const ColorRGB &color, const ColorRGB &color2)
|
|
{
|
|
return (color.r == color2.r && color.g == color2.g && color.b == color2.b);
|
|
}
|
|
|
|
// Are both colors not equal?
|
|
bool operator!=(const ColorRGB &color, const ColorRGB &color2)
|
|
{
|
|
return (!(color.r == color2.r && color.g == color2.g && color.b == color2.b));
|
|
}
|
|
|
|
ColorHSL::ColorHSL(Uint8 h, Uint8 s, Uint8 l)
|
|
{
|
|
this->h = h;
|
|
this->s = s;
|
|
this->l = l;
|
|
}
|
|
ColorHSL::ColorHSL()
|
|
{
|
|
this->h = 0;
|
|
this->s = 0;
|
|
this->l = 0;
|
|
}
|
|
ColorHSV::ColorHSV(Uint8 h, Uint8 s, Uint8 v)
|
|
{
|
|
this->h = h;
|
|
this->s = s;
|
|
this->v = v;
|
|
}
|
|
ColorHSV::ColorHSV()
|
|
{
|
|
this->h = 0;
|
|
this->s = 0;
|
|
this->v = 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// COLOR CONVERSIONS/////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/*
|
|
Convert colors from one type to another
|
|
r=red g=green b=blue h=hue s=saturation l=lightness v=value
|
|
Color components from the color structs are Uint8's between 0 and 255
|
|
color components used in the calculations are normalized between 0.0-1.0
|
|
*/
|
|
|
|
// Converts an RGB color to HSL color
|
|
ColorHSL RGBtoHSL(const ColorRGB &colorRGB)
|
|
{
|
|
float r, g, b, h = 0, s = 0, l; // this function works with floats between 0 and 1
|
|
r = colorRGB.r / 256.0;
|
|
g = colorRGB.g / 256.0;
|
|
b = colorRGB.b / 256.0;
|
|
|
|
float maxColor = std::max(r, std::max(g, b));
|
|
float minColor = std::min(r, std::min(g, b));
|
|
|
|
if (minColor == maxColor) // R = G = B, so it's a shade of grey
|
|
{
|
|
h = 0; // it doesn't matter what value it has
|
|
s = 0;
|
|
l = r; // doesn't matter if you pick r, g, or b
|
|
}
|
|
else
|
|
{
|
|
l = (minColor + maxColor) / 2;
|
|
|
|
if (l < 0.5)
|
|
s = (maxColor - minColor) / (maxColor + minColor);
|
|
if (l >= 0.5)
|
|
s = (maxColor - minColor) / (2.0 - maxColor - minColor);
|
|
|
|
if (r == maxColor)
|
|
h = (g - b) / (maxColor - minColor);
|
|
if (g == maxColor)
|
|
h = 2.0 + (b - r) / (maxColor - minColor);
|
|
if (b == maxColor)
|
|
h = 4.0 + (r - g) / (maxColor - minColor);
|
|
|
|
h /= 6; // to bring it to a number between 0 and 1
|
|
if (h < 0)
|
|
h += 1;
|
|
}
|
|
|
|
ColorHSL colorHSL;
|
|
colorHSL.h = int(h * 255.0);
|
|
colorHSL.s = int(s * 255.0);
|
|
colorHSL.l = int(l * 255.0);
|
|
return colorHSL;
|
|
}
|
|
|
|
// Converts an HSL color to RGB color
|
|
ColorRGB HSLtoRGB(const ColorHSL &colorHSL)
|
|
{
|
|
float r, g, b, h, s, l; // this function works with floats between 0 and 1
|
|
float temp1, temp2, tempr, tempg, tempb;
|
|
h = colorHSL.h / 256.0;
|
|
s = colorHSL.s / 256.0;
|
|
l = colorHSL.l / 256.0;
|
|
|
|
// If saturation is 0, the color is a shade of grey
|
|
if (s == 0)
|
|
r = g = b = l;
|
|
// If saturation > 0, more complex calculations are needed
|
|
else
|
|
{
|
|
// set the temporary values
|
|
if (l < 0.5)
|
|
temp2 = l * (1 + s);
|
|
else
|
|
temp2 = (l + s) - (l * s);
|
|
temp1 = 2 * l - temp2;
|
|
tempr = h + 1.0 / 3.0;
|
|
if (tempr > 1.0)
|
|
tempr--;
|
|
tempg = h;
|
|
tempb = h - 1.0 / 3.0;
|
|
if (tempb < 0.0)
|
|
tempb++;
|
|
|
|
// red
|
|
if (tempr < 1.0 / 6.0)
|
|
r = temp1 + (temp2 - temp1) * 6.0 * tempr;
|
|
else if (tempr < 0.5)
|
|
r = temp2;
|
|
else if (tempr < 2.0 / 3.0)
|
|
r = temp1 + (temp2 - temp1) * ((2.0 / 3.0) - tempr) * 6.0;
|
|
else
|
|
r = temp1;
|
|
|
|
// green
|
|
if (tempg < 1.0 / 6.0)
|
|
g = temp1 + (temp2 - temp1) * 6.0 * tempg;
|
|
else if (tempg < 0.5)
|
|
g = temp2;
|
|
else if (tempg < 2.0 / 3.0)
|
|
g = temp1 + (temp2 - temp1) * ((2.0 / 3.0) - tempg) * 6.0;
|
|
else
|
|
g = temp1;
|
|
|
|
// blue
|
|
if (tempb < 1.0 / 6.0)
|
|
b = temp1 + (temp2 - temp1) * 6.0 * tempb;
|
|
else if (tempb < 0.5)
|
|
b = temp2;
|
|
else if (tempb < 2.0 / 3.0)
|
|
b = temp1 + (temp2 - temp1) * ((2.0 / 3.0) - tempb) * 6.0;
|
|
else
|
|
b = temp1;
|
|
}
|
|
|
|
ColorRGB colorRGB;
|
|
colorRGB.r = int(r * 255.0);
|
|
colorRGB.g = int(g * 255.0);
|
|
colorRGB.b = int(b * 255.0);
|
|
return colorRGB;
|
|
}
|
|
|
|
// Converts an RGB color to HSV color
|
|
ColorHSV RGBtoHSV(const ColorRGB &colorRGB)
|
|
{
|
|
float r, g, b, h = 0.0, s = 0.0, v; // this function works with floats between 0 and 1
|
|
r = colorRGB.r / 256.0;
|
|
g = colorRGB.g / 256.0;
|
|
b = colorRGB.b / 256.0;
|
|
|
|
float maxColor = std::max(r, std::max(g, b));
|
|
float minColor = std::min(r, std::min(g, b));
|
|
|
|
v = maxColor;
|
|
|
|
if (maxColor != 0.0) // avoid division by zero when the color is black
|
|
{
|
|
s = (maxColor - minColor) / maxColor;
|
|
}
|
|
|
|
if (s == 0.0)
|
|
{
|
|
h = 0.0; // it doesn't matter what value it has
|
|
}
|
|
else
|
|
{
|
|
if (r == maxColor)
|
|
h = (g - b) / (maxColor - minColor);
|
|
if (g == maxColor)
|
|
h = 2.0 + (b - r) / (maxColor - minColor);
|
|
if (b == maxColor)
|
|
h = 4.0 + (r - g) / (maxColor - minColor);
|
|
|
|
h /= 6.0; // to bring it to a number between 0 and 1
|
|
if (h < 0.0)
|
|
h++;
|
|
}
|
|
|
|
ColorHSV colorHSV;
|
|
colorHSV.h = int(h * 255.0);
|
|
colorHSV.s = int(s * 255.0);
|
|
colorHSV.v = int(v * 255.0);
|
|
return colorHSV;
|
|
}
|
|
|
|
// Converts an HSV color to RGB color
|
|
ColorRGB HSVtoRGB(const ColorHSV &colorHSV)
|
|
{
|
|
float r, g, b, h, s, v; // this function works with floats between 0 and 1
|
|
h = colorHSV.h / 256.0;
|
|
s = colorHSV.s / 256.0;
|
|
v = colorHSV.v / 256.0;
|
|
|
|
// if saturation is 0, the color is a shade of grey
|
|
if (s == 0.0)
|
|
r = g = b = v;
|
|
// if saturation > 0, more complex calculations are needed
|
|
else
|
|
{
|
|
float f, p, q, t;
|
|
int i;
|
|
h *= 6.0; // to bring hue to a number between 0 and 6, better for the calculations
|
|
i = int(floor(h)); // e.g. 2.7 becomes 2 and 3.01 becomes 3 or 4.9999 becomes 4
|
|
f = h - i; // the fractional part of h
|
|
|
|
p = v * (1.0 - s);
|
|
q = v * (1.0 - (s * f));
|
|
t = v * (1.0 - (s * (1.0 - f)));
|
|
|
|
switch (i)
|
|
{
|
|
case 0:
|
|
r = v;
|
|
g = t;
|
|
b = p;
|
|
break;
|
|
case 1:
|
|
r = q;
|
|
g = v;
|
|
b = p;
|
|
break;
|
|
case 2:
|
|
r = p;
|
|
g = v;
|
|
b = t;
|
|
break;
|
|
case 3:
|
|
r = p;
|
|
g = q;
|
|
b = v;
|
|
break;
|
|
case 4:
|
|
r = t;
|
|
g = p;
|
|
b = v;
|
|
break;
|
|
case 5:
|
|
r = v;
|
|
g = p;
|
|
b = q;
|
|
break;
|
|
default:
|
|
r = g = b = 0;
|
|
break;
|
|
}
|
|
}
|
|
ColorRGB colorRGB;
|
|
colorRGB.r = int(r * 255.0);
|
|
colorRGB.g = int(g * 255.0);
|
|
colorRGB.b = int(b * 255.0);
|
|
return colorRGB;
|
|
}
|
|
|
|
Uint32 RGBtoINT(const ColorRGB &colorRGB)
|
|
{
|
|
return 65536 * colorRGB.r + 256 * colorRGB.g + colorRGB.b;
|
|
}
|
|
|
|
ColorRGB INTtoRGB(Uint32 colorINT)
|
|
{
|
|
ColorRGB colorRGB;
|
|
colorRGB.r = (colorINT / 65536) % 256;
|
|
colorRGB.g = (colorINT / 256) % 256;
|
|
colorRGB.b = colorINT % 256;
|
|
return colorRGB;
|
|
}
|
|
} |