mirror of https://github.com/odrling/Aegisub
365 lines
8.9 KiB
C++
365 lines
8.9 KiB
C++
// Copyright (c) 2005, Niels Martin Hansen, Rodrigo Braz Monteiro
|
|
// 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.
|
|
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
// may be used to endorse or promote products derived from this software
|
|
// without specific prior written permission.
|
|
//
|
|
// 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.
|
|
//
|
|
// -----------------------------------------------------------------------------
|
|
//
|
|
// AEGISUB
|
|
//
|
|
// Website: http://aegisub.cellosoft.com
|
|
// Contact: mailto:zeratul@cellosoft.com
|
|
//
|
|
|
|
#include "colorspace.h"
|
|
#include "utils.h"
|
|
|
|
|
|
// matrix from http://forum.doom9.org/showthread.php?p=684080#post684080
|
|
void yuv_to_rgb(int Y, int U, int V, unsigned char *R, unsigned char *G, unsigned char *B)
|
|
{
|
|
U = U - 128;
|
|
V = V - 128;
|
|
*R = clip_colorval((Y*65536 + int(1.140*65536) * V) / 65536);
|
|
*G = clip_colorval((Y*65536 - int(0.395*65536) * U - int(0.581*65536) * V) / 65536);
|
|
*B = clip_colorval((Y*65536 + int(2.032*65536) * U ) / 65536);
|
|
}
|
|
|
|
|
|
// algorithm from http://130.113.54.154/~monger/hsl-rgb.html
|
|
// making every value into 0..255 range though
|
|
void hsl_to_rgb(int H, int S, int L, unsigned char *R, unsigned char *G, unsigned char *B)
|
|
{
|
|
if (S == 0) {
|
|
*R = L;
|
|
*G = L;
|
|
*B = L;
|
|
return;
|
|
}
|
|
|
|
int r, g, b;
|
|
|
|
int temp2;
|
|
if (L < 128) {
|
|
temp2 = L * (256 + S) / 256;
|
|
} else {
|
|
temp2 = L + S - L * S / 256;
|
|
}
|
|
|
|
int temp1 = 2*L - temp2;
|
|
|
|
int temp3[3];
|
|
temp3[0] = H + 256/3;
|
|
if (temp3[0] < 0) temp3[0] += 256;
|
|
if (temp3[0] > 256) temp3[0] -= 256;
|
|
temp3[1] = H;
|
|
temp3[2] = H - 256/3;
|
|
if (temp3[2] < 0) temp3[2] += 256;
|
|
if (temp3[2] > 256) temp3[2] -= 256;
|
|
|
|
if (6 * temp3[0] < 255)
|
|
r = temp1 + (temp2 - temp1) * 6 * temp3[0] / 256;
|
|
else if (2 * temp3[0] < 255)
|
|
r = temp2;
|
|
else if (3 * temp3[0] < 511)
|
|
r = temp1 + (temp2 - temp1) * ((512/3) - temp3[0]) * 6 / 256;
|
|
else
|
|
r = temp1;
|
|
|
|
if (6 * temp3[1] < 255)
|
|
g = temp1 + (temp2 - temp1) * 6 * temp3[1] / 256;
|
|
else if (2 * temp3[1] < 255)
|
|
g = temp2;
|
|
else if (3 * temp3[1] < 511)
|
|
g = temp1 + (temp2 - temp1) * ((512/3) - temp3[1]) * 6 / 256;
|
|
else
|
|
g = temp1;
|
|
|
|
if (6 * temp3[2] < 255)
|
|
b = temp1 + (temp2 - temp1) * 6 * temp3[2] / 256;
|
|
else if (2 * temp3[2] < 255)
|
|
b = temp2;
|
|
else if (3 * temp3[2] < 511)
|
|
b = temp1 + (temp2 - temp1) * ((512/3) - temp3[2]) * 6 / 256;
|
|
else
|
|
b = temp1;
|
|
|
|
*R = clip_colorval(r);
|
|
*G = clip_colorval(g);
|
|
*B = clip_colorval(b);
|
|
}
|
|
|
|
|
|
// formulas taken from wikipedia: http://en.wikipedia.org/wiki/HSV_color_space
|
|
// the range for H is 0..255 instead of 0..359, so 60 degrees has been translated to 256/6 here
|
|
void hsv_to_rgb(int H, int S, int V, unsigned char *R, unsigned char *G, unsigned char *B)
|
|
{
|
|
*R = *G = *B = 0;
|
|
|
|
// Cap values
|
|
unsigned int h = H * 360;
|
|
unsigned int s = clip_colorval(S)*256;
|
|
unsigned int v = clip_colorval(V)*256;
|
|
|
|
// Saturation is zero, make grey
|
|
if (S == 0) {
|
|
*R = V;
|
|
*G = V;
|
|
*B = V;
|
|
return;
|
|
}
|
|
|
|
unsigned int r, g, b;
|
|
|
|
// Else, calculate color
|
|
unsigned int Hi = h / 60 / 256;
|
|
unsigned int f = h / 60 - Hi * 256;
|
|
unsigned int p = v * (65535 - s) / 65536;
|
|
unsigned int q = v * (65535 - (f * s)/256) / 65536;
|
|
unsigned int t = v * (65535 - ((255 - f) * s)/256) / 65536;
|
|
switch (Hi) {
|
|
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;
|
|
}
|
|
|
|
*R = clip_colorval(r/256);
|
|
*G = clip_colorval(g/256);
|
|
*B = clip_colorval(b/256);
|
|
}
|
|
|
|
|
|
// matrix from http://forum.doom9.org/showthread.php?p=684080#post684080
|
|
void rgb_to_yuv(int R, int G, int B, unsigned char *Y, unsigned char *U, unsigned char *V)
|
|
{
|
|
*Y = clip_colorval(( int(0.299*65536) * R + int(0.587*65536) * G + int(0.114*65536) * B) / 65536);
|
|
*U = clip_colorval((-int(0.147*65536) * R - int(0.289*65536) * G + int(0.436*65536) * B) / 65536 + 128);
|
|
*V = clip_colorval(( int(0.615*65536) * R - int(0.515*65536) * G - int(0.100*65536) * B) / 65536 + 128);
|
|
}
|
|
|
|
|
|
// also from http://130.113.54.154/~monger/hsl-rgb.html
|
|
// still keeping everything integer
|
|
void rgb_to_hsl(int R, int G, int B, unsigned char *H, unsigned char *S, unsigned char *L)
|
|
{
|
|
/*
|
|
int maxcolor = MAX(R, MAX(G, B));
|
|
int mincolor = MIN(R, MIN(G, B));
|
|
|
|
*L = (maxcolor + mincolor) / 2;
|
|
|
|
if (mincolor == maxcolor) {
|
|
*S = 0;
|
|
*H = 0;
|
|
return;
|
|
}
|
|
|
|
if (*L < 128)
|
|
*S = (maxcolor - mincolor) * 256 / (maxcolor + mincolor);
|
|
else
|
|
*S = (maxcolor - mincolor) * 256 / (512 - maxcolor - mincolor);
|
|
|
|
int h;
|
|
if (R == maxcolor)
|
|
h = (G - B) * 256 / (maxcolor - mincolor);
|
|
else if (G == maxcolor)
|
|
h = 512 + (B - R) / (maxcolor - mincolor);
|
|
else // if (B == maxcolor)
|
|
h = 1024 + (R - G) / (maxcolor - mincolor);
|
|
|
|
h /= 6;
|
|
if (h < 0)
|
|
h += 256;
|
|
*H = h;
|
|
*/
|
|
|
|
float r = R/255.f, g = G/255.f, b = B/255.f;
|
|
float h, s, l;
|
|
|
|
float maxrgb = MAX(r, MAX(g, b)), minrgb = MIN(r, MIN(g, b));
|
|
|
|
l = (minrgb + maxrgb) / 2;
|
|
|
|
if (minrgb == maxrgb) {
|
|
h = 0;
|
|
s = 0;
|
|
} else {
|
|
if (l < 0.5) {
|
|
s = (maxrgb - minrgb) / (maxrgb + minrgb);
|
|
} else {
|
|
s = (maxrgb - minrgb) / (2.f - maxrgb - minrgb);
|
|
}
|
|
if (r == maxrgb) {
|
|
h = (g-b) / (maxrgb-minrgb) + 0;
|
|
} else if (g == maxrgb) {
|
|
h = (b-r) / (maxrgb-minrgb) + 2;
|
|
} else { // if b == maxrgb
|
|
h = (r-g) / (maxrgb-minrgb) + 4;
|
|
}
|
|
}
|
|
|
|
if (h < 0) h += 6;
|
|
if (h >= 6) h -= 6;
|
|
|
|
*H = clip_colorval(int(h*256/6));
|
|
*S = clip_colorval(int(s*255));
|
|
*L = clip_colorval(int(l*255));
|
|
}
|
|
|
|
|
|
// formulas from http://en.wikipedia.org/wiki/HSV_color_space
|
|
void rgb_to_hsv(int R, int G, int B, unsigned char *H, unsigned char *S, unsigned char *V)
|
|
{
|
|
/*
|
|
int maxrgb = MAX(R, MAX(R, B)), minrgb = MIN(R, MIN(G, B));
|
|
|
|
*V = clip_colorval(maxrgb);
|
|
|
|
if (maxrgb == 0) {
|
|
*S = 0;
|
|
} else {
|
|
*S = clip_colorval((maxrgb - minrgb) * 256 / maxrgb);
|
|
}
|
|
|
|
int h;
|
|
if (minrgb == maxrgb) {
|
|
h = 0;
|
|
} else if (R == maxrgb) {
|
|
h = (G - B) * 256 / (maxrgb - minrgb) * 256 / 6;
|
|
} else if (G == maxrgb) {
|
|
h = (B - R) * 256 / (maxrgb - minrgb) * 256 / 6 + 256/3;
|
|
} else { // if B == maxrgb
|
|
h = (R - G) * 256 / (maxrgb - minrgb) * 256 / 6 + 256*2/3;
|
|
}
|
|
|
|
if (h < 0) h += 256;
|
|
if (h > 255) h -= 256;
|
|
*H = clip_colorval(h);
|
|
*/
|
|
|
|
float r = R/255.f, g = G/255.f, b = B/255.f;
|
|
float h, s, v;
|
|
|
|
float maxrgb = MAX(r, MAX(g, b)), minrgb = MIN(r, MIN(g, b));
|
|
|
|
v = maxrgb;
|
|
|
|
if (maxrgb < .001f) {
|
|
s = 1;
|
|
} else {
|
|
s = (maxrgb - minrgb) / maxrgb;
|
|
}
|
|
|
|
if (minrgb == maxrgb) {
|
|
h = 0;
|
|
} else if (maxrgb == r) {
|
|
h = (g-b) / (maxrgb-minrgb) + 0;
|
|
} else if (maxrgb == g) {
|
|
h = (b-r) / (maxrgb-minrgb) + 2;
|
|
} else { // if maxrgb == b
|
|
h = (r-g) / (maxrgb-minrgb) + 4;
|
|
}
|
|
|
|
if (h < 0) h += 6;
|
|
if (h >= 6) h -= 6;
|
|
|
|
*H = clip_colorval(int(h*256/6));
|
|
*S = clip_colorval(int(s*255));
|
|
*V = clip_colorval(int(v*255));
|
|
}
|
|
|
|
|
|
|
|
wxString color_to_html(wxColour color)
|
|
{
|
|
return wxString::Format(_T("#%02X%02X%02X"), color.Red(), color.Green(), color.Blue());
|
|
}
|
|
|
|
|
|
wxColour html_to_color(wxString html)
|
|
{
|
|
html.Trim(true);
|
|
html.Trim(false);
|
|
if (html.StartsWith(_T("#"))) {
|
|
html.Remove(0, 1);
|
|
}
|
|
if (html.size() == 6) {
|
|
// 8 bit per channel
|
|
long r, g, b;
|
|
wxString sr, sg, sb;
|
|
sr = html.Mid(0, 2);
|
|
sg = html.Mid(2, 2);
|
|
sb = html.Mid(4, 2);
|
|
if (sr.ToLong(&r, 16) && sg.ToLong(&g, 16) && sb.ToLong(&b, 16)) {
|
|
return wxColour(r, g, b);
|
|
} else {
|
|
return wxColour(*wxBLACK);
|
|
}
|
|
} else if (html.size() == 3) {
|
|
// 4 bit per channel
|
|
long r, g, b;
|
|
wxString sr, sg, sb;
|
|
sr = html.Mid(0, 1);
|
|
sg = html.Mid(1, 1);
|
|
sb = html.Mid(2, 1);
|
|
if (sr.ToLong(&r, 16) && sg.ToLong(&g, 16) && sb.ToLong(&b, 16)) {
|
|
return wxColour(r*16+r, g*16+g, b*16+b);
|
|
} else {
|
|
return wxColour(*wxBLACK);
|
|
}
|
|
} else {
|
|
// only care about valid colors
|
|
return wxColour(*wxBLACK);
|
|
}
|
|
}
|
|
|