20130313
[gdash.git] / src / cave / helper / colors.cpp
blob6d65af5d34e559a68f2583bfd5505019a3dc2df9
1 /*
2 * Copyright (c) 2007-2013, Czirkos Zoltan http://code.google.com/p/gdash/
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 #include "config.h"
18 #include <cstdio>
19 #include <cmath>
20 #include <glib.h>
21 #include <glib/gi18n.h>
23 #include "settings.hpp"
24 #include "cave/helper/colors.hpp"
25 #include "misc/printf.hpp"
26 #include "misc/logger.hpp"
27 #include "misc/util.hpp"
29 /* These are in the include/ directory. */
30 /* Stores the atari palettes. */
31 #include "ataripal.cpp"
32 /* Stores the DTV palettes. */
33 #include "dtvpal.cpp"
35 static const char *c64_color_bdcff_names[]={
36 "Black", "White", "Red", "Cyan", "Purple", "Green", "Blue", "Yellow",
37 "Orange", "Brown", "LightRed", "Gray1", "Gray2", "LightGreen", "LightBlue", "Gray3",
40 static const char *c64_color_visible_names[]={
41 N_("Black"), N_("White"), N_("Red"), N_("Cyan"), N_("Purple"), N_("Green"), N_("Blue"), N_("Yellow"),
42 N_("Orange"), N_("Brown"), N_("Light red"), N_("Dark gray"), N_("Gray"), N_("Light green"), N_("Light blue"), N_("Light gray"),
45 static guint32 c64_colors_vice_old[]={
46 0x000000, 0xFFFFFF, 0x68372b, 0x70a4b2, 0x6f3d86, 0x588d43, 0x352879, 0xb8c76f,
47 0x6f4f25, 0x433900, 0x9a6759, 0x444444, 0x6c6c6c, 0x9ad284, 0x6c5eb5, 0x959595,
50 static guint32 c64_colors_vice_new[]={
51 0x000000, 0xffffff, 0x894036, 0x7abfc7, 0x8a46ae, 0x68a941, 0x3e31a2, 0xd0dc71,
52 0x905f25, 0x5c4700, 0xbb776d, 0x555555, 0x808080, 0xacea88, 0x7c70da, 0xababab,
55 static guint32 c64_colors_c64_hq[]={
56 0x0A0A0A, 0xFFF8FF, 0x851F02, 0x65CDA8, 0xA73B9F, 0x4DAB19, 0x1A0C92, 0xEBE353,
57 0xA94B02, 0x441E00, 0xD28074, 0x464646, 0x8B8B8B, 0x8EF68E, 0x4D91D1, 0xBABABA,
60 static guint32 c64_colors_c64s[]={
61 0x000000, 0xFCFCFC, 0xA80000, 0x54FCFC, 0xA800A8, 0x00A800, 0x0000A8, 0xFCFC00,
62 0xA85400, 0x802C00, 0xFC5454, 0x545454, 0x808080, 0x54FC54, 0x5454FC, 0xA8A8A8,
65 static guint32 c64_colors_ccs64[]={
66 0x101010, 0xFFFFFF, 0xE04040, 0x60FFFF, 0xE060E0, 0x40E040, 0x4040E0, 0xFFFF40,
67 0xE0A040, 0x9C7448, 0xFFA0A0, 0x545454, 0x888888, 0xA0FFA0, 0xA0A0FF, 0xC0C0C0,
70 static guint32 c64_colors_vice_default[]={
71 0x000000, 0xFDFEFC, 0xBE1A24, 0x30E6C6, 0xB41AE2, 0x1FD21E, 0x211BAE, 0xDFF60A,
72 0xB84104, 0x6A3304, 0xFE4A57, 0x424540, 0x70746F, 0x59FE59, 0x5F53FE, 0xA4A7A2,
75 static guint32 c64_colors_frodo[]={
76 0x000000, 0xFFFFFF, 0xCC0000, 0x00FFCC, 0xFF00FF, 0x00CC00, 0x0000CC, 0xFFFF00,
77 0xFF8800, 0x884400, 0xFF8888, 0x444444, 0x888888, 0x88FF88, 0x8888FF, 0xCCCCCC,
80 static guint32 c64_colors_godot[]={
81 0x000000, 0xFFFFFF, 0x880000, 0xAAFFEE, 0xCC44CC, 0x00CC55, 0x0000AA, 0xEEEE77,
82 0xDD8855, 0x664400, 0xFE7777, 0x333333, 0x777777, 0xAAFF66, 0x0088FF, 0xBBBBBB,
85 static guint32 c64_colors_pc64[]={
86 0x212121, 0xFFFFFF, 0xB52121, 0x73FFFF, 0xB521B5, 0x21B521, 0x2121B5, 0xFFFF21,
87 0xB57321, 0x944221, 0xFF7373, 0x737373, 0x949494, 0x73FF73, 0x7373FF, 0xB5B5B5,
90 static guint32 c64_colors_rtadash[]={
91 0x000000, 0xffffff, 0xea3418, 0x58ffff, 0xd82cff, 0x55fb00, 0x4925ff, 0xffff09,
92 0xe66c00, 0x935f00, 0xff7c64, 0x6c6c6c, 0xa1a1a1, 0xafff4d, 0x9778ff, 0xd8d8d8,
96 /* these values are taken from the title screen, drawn by cws. */
97 /* so menus and everything else will look nice! */
98 /* the 16 colors that can be used are the same as on c64. */
99 /* "Black", "White", "Red", "Cyan", "Purple", "Green", "Blue", "Yellow", */
100 /* "Orange", "Brown", "LightRed", "Gray1", "Gray2", "LightGreen", "LightBlue", "Gray3", */
101 /* not in the png: cyan, purple. gray3 is darker in the png. */
102 /* 17th color is the player's leg in the png. i not connected it to any c64 */
103 /* color, but it is used for theme images for example. */
104 static const guint32 gdash_colors[]={
105 0x000000, 0xffffff, 0xe33939, 0x55aaaa, 0xaa55aa, 0x71aa55, 0x0039ff, 0xffff55,
106 0xe37139, 0xaa7139, 0xe09080, 0x555555, 0x717171, 0xc6e38e, 0xaaaaff, 0x8e8e8e,
108 0x5555aa,
112 /* make sure that pointeres and names match! */
113 static guint32 *c64_palette_pointers[]={
114 c64_colors_vice_new,
115 c64_colors_vice_old, c64_colors_vice_default, c64_colors_c64_hq, c64_colors_c64s,
116 c64_colors_ccs64, c64_colors_frodo, c64_colors_godot, c64_colors_pc64, c64_colors_rtadash,
117 NULL
119 static const char *c64_palettes_names[]={
120 "Vice new",
121 "Vice old", "Vice default", "C64HQ", "C64S",
122 "CCS64", "Frodo", "GoDot", "PC64", "RTADash",
123 NULL
126 /* indexes in this array must match GdColorType */
127 static const char *palette_types_names[]={
128 N_("RGB colors"),
129 N_("C64 colors"),
130 N_("C64DTV colors"),
131 N_("Atari colors"),
132 NULL
136 /// Color of flashing the screen, gate opening to exit.
137 const GdColor gd_flash_color=GdColor::from_rgb(0xff, 0xff, 0xff);
138 /// Color of selected object in editor.
139 const GdColor gd_select_color=GdColor::from_rgb(0x80, 0x80, 0xff);
142 /// Get an array of C strings, which stores the color types known.
143 const char **GdColor::get_palette_types_names() {
144 return palette_types_names;
147 /// Get an array of C strings, which stores the names of C64 palettes known.
148 const char ** GdColor::get_c64_palette_names() {
149 return c64_palettes_names;
152 /// Get an array of C strings, which stores the names of C64DTV palettes known.
153 const char ** GdColor::get_c64dtv_palette_names() {
154 return c64dtv_palettes_names;
157 /// Get an array of C strings, which stores the names of Atari palettes known.
158 const char ** GdColor::get_atari_palette_names() {
159 return atari_palettes_names;
162 /// Constructor to create an RGB color from a 0x00RRGGBB value.
163 /// Used internally, as palettes can easily be stored this way.
164 /// @param rgb The 0x00RRGGBB value.
165 GdColor::GdColor(unsigned int rgbval) {
166 g_assert(rgbval < 0x1000000);
167 type = TypeRGB;
168 rgb.b = (rgbval>>0) & 0xff;
169 rgb.g = (rgbval>>8) & 0xff;
170 rgb.r = (rgbval>>16) & 0xff;
173 /// Create a GDash color. These are similar to C64 colors,
174 /// but always use the same palette. Internally, they are
175 /// RGB colors.
176 /// @param c Color index, 0..16.
177 GdColor GdColor::from_gdash_index(unsigned c) {
178 g_assert(c>=0 && c<G_N_ELEMENTS(gdash_colors));
179 return GdColor(gdash_colors[c]);
182 /// Create a C64 color.
183 /// @param index C64 color index, 0..15
184 GdColor GdColor::from_c64(unsigned index) {
185 g_assert(index>=0 && index<=15);
186 return GdColor(TypeC64, index);
190 /// Create an Atari color.
191 /// @param index Color index, 0..255
192 GdColor GdColor::from_atari(unsigned index) {
193 g_assert(index>=0 && index<=255);
194 return GdColor(TypeAtari, index);
198 /// Create an Atari color, hue+sat version.
199 /// Makes generating random Atari palettes easier.
200 /// @param hue Hue, 0..15
201 /// @param sat Saturation, 0..15
202 GdColor GdColor::from_atari_huesat(unsigned hue, unsigned sat) {
203 g_assert(hue>=0 && hue<=15);
204 g_assert(sat>=0 && sat<=15);
205 return GdColor(TypeAtari, 16*hue+sat);
208 /// Create a C64DTV color.
209 /// @param index Color index, 0..255
210 GdColor GdColor::from_c64dtv(unsigned index) {
211 g_assert(index>=0 && index<=255);
212 return GdColor(TypeC64DTV, index);
215 /// Create a C64DTV color, hue+sat version.
216 /// @param hue Hue, 0..15
217 /// @param sat Saturation, 0..15
218 GdColor GdColor::from_c64dtv_huesat(unsigned hue, unsigned sat) {
219 g_assert(hue>=0 && hue<=15);
220 g_assert(sat>=0 && sat<=15);
221 return GdColor(TypeC64DTV, 16*hue+sat);
224 /// Create a color from a given r, g, b value.
225 /// @param r Red value 0..255.
226 /// @param g Green value 0..255.
227 /// @param b Blue value 0..255.
228 GdColor GdColor::from_rgb(unsigned r, unsigned g, unsigned b) {
229 return GdColor(r, g, b);
232 /// Make up GdColor from h,s,v values.
233 /// @param h Hue, 0..360
234 /// @param s Saturation, 0..100
235 /// @param v Value, 0..100
236 GdColor GdColor::from_hsv(unsigned short h, unsigned char s, unsigned char v) {
237 GdColor n;
238 n.type = TypeHSV;
239 n.hsv.h = h;
240 n.hsv.s = s;
241 n.hsv.v = v;
242 return n;
246 /// Create a color from a BDCFF description.
247 /// @param color The string which contains the BDCFF representation.
248 /// @return The new color object.
249 bool read_from_string(std::string const& str, GdColor &c) {
250 // check if it is a name of a c64 color
251 for (unsigned i=0; i<G_N_ELEMENTS(c64_color_bdcff_names); i++)
252 if (gd_str_ascii_caseequal(str, c64_color_bdcff_names[i])) {
253 c=GdColor::from_c64(i);
254 return true;
257 std::string strupper(str);
258 for (unsigned i=0; i<strupper.size(); ++i)
259 strupper[i]=toupper(strupper[i]);
260 const char *cstr=strupper.c_str();
262 int x;
263 // check if atari color.
264 if (sscanf(cstr, "ATARI%02x", &x)==1) {
265 c=GdColor::from_atari(x);
266 return true;
269 // check if c64dtv color.
270 if (sscanf(cstr, "C64DTV%02x", &x)==1) {
271 c=GdColor::from_c64dtv(x);
272 return true;
275 // rgb color? may or may not have a #
276 if (cstr[0]=='#')
277 ++cstr;
278 int r, g, b;
279 if (sscanf(cstr, "%02x%02x%02x", &r, &g, &b)==3) {
280 c = GdColor::from_rgb(r, g, b);
281 return true;
284 // could not read in any way
285 return false;
289 /// Create a color object, which is RGB internally.
290 /// Uses the current user preferences for conversion.
291 /// The color object created should not be saved, but
292 /// only used on-screen.
293 /// @param color The color to convert.
294 GdColor GdColor::to_rgb() const {
295 const guint8 *atari_pal;
296 const guint8 *c64dtv_pal;
297 const guint32 *c64_pal;
299 switch (type) {
300 case TypeRGB:
301 /* is already rgb */
302 return *this;
304 case TypeC64:
305 if (gd_c64_palette<0 || gd_c64_palette>=(int)G_N_ELEMENTS(c64_palette_pointers)-1)
306 gd_c64_palette=0; /* silently switch to default, if invalid value */
307 c64_pal=c64_palette_pointers[gd_c64_palette];
308 return GdColor(c64_pal[index]);
310 case TypeC64DTV:
311 if (gd_c64dtv_palette<0 || gd_c64dtv_palette>=(int)G_N_ELEMENTS(c64dtv_palettes_pointers)-1)
312 gd_c64dtv_palette=0;
313 c64dtv_pal=c64dtv_palettes_pointers[gd_c64dtv_palette];
314 return from_rgb(c64dtv_pal[index*3], c64dtv_pal[index*3+1], c64dtv_pal[index*3+2]);
316 case TypeAtari:
317 if (gd_atari_palette<0 || gd_atari_palette>=(int)G_N_ELEMENTS(atari_palettes_pointers)-1)
318 gd_atari_palette=0;
319 atari_pal=atari_palettes_pointers[gd_atari_palette];
320 return from_rgb(atari_pal[index*3], atari_pal[index*3+1], atari_pal[index*3+2]);
322 case TypeHSV:
324 double h = hsv.h, s = hsv.s/100.0, v = hsv.v/100.0;
325 int i = (int)(h/60)%6; /* divided by 60 degrees */
326 double f = h/60-(int)(h/60); /* fractional part */
327 double p = v*(1-s);
328 double q = v*(1-s*f);
329 double t = v*(1-s*(1-f));
331 v *= 255.0;
332 p *= 255.0;
333 q *= 255.0;
334 t *= 255.0;
336 switch (i) {
337 case 0: return GdColor::from_rgb(v, t, p);
338 case 1: return GdColor::from_rgb(q, v, p);
339 case 2: return GdColor::from_rgb(p, v, t);
340 case 3: return GdColor::from_rgb(p, q, v);
341 case 4: return GdColor::from_rgb(t, p, v);
342 case 5: return GdColor::from_rgb(v, p, q);
344 /* no way we reach this */
345 g_assert_not_reached();
346 return GdColor::from_rgb(0,0,0);
349 g_assert_not_reached();
353 /// Create a color object, which is HSV internally.
354 GdColor GdColor::to_hsv() const {
355 if (type == TypeHSV)
356 return *this;
358 double R = get_r()/255.0, G = get_g()/255.0, B = get_b()/255.0;
359 double M = std::max(std::max(R, G), B);
360 double m = std::min(std::min(R, G), B);
361 double C = M-m;
362 double H, S, V;
364 V = M;
365 if (V > 0)
366 S = C/V;
367 else
368 S = 0;
369 if (R >= M)
370 H = (G-B)/C;
371 else if (G >= M)
372 H = 2 + (B-R)/C;
373 else
374 H = 4 + (R-G)/C;
375 return GdColor::from_hsv(fmod(H*60.0+360, 360), S*100.0, V*100.0);
379 /// Get red component of color.
380 /// Uses the current user palette, if needed.
381 unsigned int GdColor::get_r() const {
382 if (type == TypeRGB)
383 return rgb.r;
384 else
385 return to_rgb().rgb.r;
388 /// Get green component of color.
389 /// Uses the current user palette, if needed.
390 unsigned int GdColor::get_g() const {
391 if (type == TypeRGB)
392 return rgb.g;
393 else
394 return to_rgb().rgb.g;
397 /// Get blue component of color.
398 /// Uses the current user palette, if needed.
399 unsigned int GdColor::get_b() const {
400 if (type == TypeRGB)
401 return rgb.b;
402 else
403 return to_rgb().rgb.b;
407 /// Get red component of color.
408 /// Uses the current user palette, if needed.
409 unsigned int GdColor::get_h() const {
410 if (type == TypeHSV)
411 return hsv.h;
412 else
413 return to_hsv().hsv.h;
416 /// Get green component of color.
417 /// Uses the current user palette, if needed.
418 unsigned int GdColor::get_s() const {
419 if (type == TypeHSV)
420 return hsv.s;
421 else
422 return to_hsv().hsv.s;
425 /// Get blue component of color.
426 /// Uses the current user palette, if needed.
427 unsigned int GdColor::get_v() const {
428 if (type == TypeHSV)
429 return hsv.v;
430 else
431 return to_hsv().hsv.v;
435 /// Get RGB components of color as an unsigned int 0x00RRGGBB
436 unsigned int GdColor::get_uint() const {
437 unsigned r = get_r();
438 unsigned g = get_g();
439 unsigned b = get_b();
441 return (r<<16)+(g<<8)+b;
445 /// Standard operator<< to write BDCFF info of the color to an ostream.
446 std::ostream& operator<<(std::ostream& os, const GdColor& c) {
447 char text[32];
449 switch (c.type) {
450 case GdColor::TypeC64:
451 sprintf(text, "%s", c64_color_bdcff_names[c.index]);
452 break;
454 case GdColor::TypeAtari:
455 sprintf(text, "Atari%02x", c.index);
456 break;
458 case GdColor::TypeC64DTV:
459 sprintf(text, "C64DTV%02x", c.index);
460 break;
462 case GdColor::TypeRGB:
463 case GdColor::TypeHSV:
464 sprintf(text, "#%02x%02x%02x", c.get_r(), c.get_g(), c.get_b());
465 break;
467 os << text;
468 return os;
471 /// Return on-screen visible name of color.
472 /// Returns strings which can be translated.
473 /// @todo throw?
474 std::string visible_name(const GdColor& c) {
475 char text[32];
477 switch (c.type) {
478 case GdColor::TypeC64:
479 return c64_color_visible_names[c.index];
480 break;
482 case GdColor::TypeAtari:
483 sprintf(text, "Atari #%02X", c.index);
484 return text;
486 case GdColor::TypeC64DTV:
487 sprintf(text, "C64DTV #%02X", c.index);
488 return text;
490 case GdColor::TypeRGB:
491 case GdColor::TypeHSV:
492 sprintf(text, "RGB #%02X%02X%02X", c.get_r(), c.get_g(), c.get_b());
493 return text;
495 /* should not happen */
496 return N_("Invalid");
500 /// Check if the color is a C64 color.
501 /// @return True, if C64 color.
502 bool GdColor::is_c64() const {
503 return type==TypeC64;
506 /// Return the "traditional" c64 index of color.
507 /// If not found, reports the error, and gives a random color.
508 /// @return The C64 index.
509 int GdColor::get_c64_index() const {
510 if (type==TypeC64)
511 return index;
512 gd_message(CPrintf("Non-C64 color: %s") % visible_name(*this));
513 return g_random_int_range(0, 16);
516 /// Compare two color objects for equality.
517 /// @return True, if they are equal. They must be also of the same type, same rgb values are not enough!
518 bool GdColor::operator==(const GdColor &rhs) const {
519 if (type!=rhs.type)
520 return false;
522 /* for rgb, all must match. */
523 if (type==TypeRGB)
524 return rgb.r==rhs.rgb.r && rgb.g==rhs.rgb.g && rgb.b==rhs.rgb.b;
525 if (type==TypeHSV)
526 return hsv.h==rhs.hsv.h && hsv.s==rhs.hsv.s && hsv.v==rhs.hsv.v;
528 /* for others, we check only r which is used as an index. */
529 return index==rhs.index;