20130313
[gdash.git] / src / gfx / cellrenderer.cpp
blobfcb1d2c1da39c1d153006c3d2f2116ca3ed0b2ab
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 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 #include "config.h"
19 #include <cstdlib>
21 #include "misc/logger.hpp"
22 #include "misc/util.hpp"
23 #include "misc/printf.hpp"
25 #include "gfx/cellrenderer.hpp"
26 #include "gfx/pixbuf.hpp"
27 #include "gfx/pixmap.hpp"
28 #include "gfx/pixbuffactory.hpp"
29 #include "settings.hpp"
32 /* data */
33 #include "c64_gfx.cpp"
35 CellRenderer::CellRenderer(PixbufFactory& pixbuf_factory_, const std::string& theme_file)
36 : loaded(0),
37 cells_all(0),
38 is_c64_colored(false),
39 cell_size(0),
40 cells_pixbufs(),
41 cells(),
42 color0(GD_GDASH_BLACK),
43 color1(GD_GDASH_MIDDLEBLUE),
44 color2(GD_GDASH_LIGHTRED),
45 color3(GD_GDASH_WHITE),
46 color4(GD_GDASH_WHITE),
47 color5(GD_GDASH_WHITE),
48 pixbuf_factory(pixbuf_factory_)
50 load_theme_file(theme_file);
54 CellRenderer::~CellRenderer() {
55 remove_cached();
56 delete loaded;
57 delete cells_all;
60 /* remove pixmaps from x server */
61 void CellRenderer::remove_cached()
63 /* if cells loaded, delete them */
64 for (unsigned i=0; i<G_N_ELEMENTS(cells_pixbufs); ++i) {
65 delete cells_pixbufs[i];
66 cells_pixbufs[i]=0;
68 for (unsigned i=0; i<G_N_ELEMENTS(cells); ++i) {
69 delete cells[i];
70 cells[i]=0;
72 if (is_c64_colored) {
73 delete cells_all;
74 cells_all = 0;
78 Pixbuf &CellRenderer::cell_pixbuf(unsigned i)
80 g_assert(i<G_N_ELEMENTS(cells_pixbufs));
81 if (cells_all == NULL)
82 create_colorized_cells();
83 g_assert(cells_all != NULL);
84 if (cells_pixbufs[i] == NULL)
85 cells_pixbufs[i] = pixbuf_factory.create_subpixbuf(*cells_all, (i%NUM_OF_CELLS_X)*cell_size, (i/NUM_OF_CELLS_X)*cell_size, cell_size, cell_size);
86 return *cells_pixbufs[i];
89 Pixmap &CellRenderer::cell(unsigned i)
91 g_assert(i<G_N_ELEMENTS(cells));
92 if (cells[i]==0) {
93 int type=i/NUM_OF_CELLS; // 0=normal, 1=colored1, 2=colored2
94 int index=i%NUM_OF_CELLS;
95 Pixbuf &pb=cell_pixbuf(index); // this is to be rendered as a pixmap, but may be colored
97 switch (type) {
98 case 0:
99 cells[i]=pixbuf_factory.create_pixmap_from_pixbuf(pb, false);
100 break;
101 case 1:
103 Pixbuf *colored=pixbuf_factory.create_composite_color(pb, gd_flash_color);
104 cells[i]=pixbuf_factory.create_pixmap_from_pixbuf(*colored, false);
105 delete colored;
107 break;
109 case 2:
111 Pixbuf *colored=pixbuf_factory.create_composite_color(pb, gd_select_color);
112 cells[i]=pixbuf_factory.create_pixmap_from_pixbuf(*colored, false);
113 delete colored;
115 break;
116 default:
117 g_assert_not_reached();
120 return *cells[i];
123 /* check if given surface is ok to be a gdash theme. */
124 bool CellRenderer::is_pixbuf_ok_for_theme(const Pixbuf &surface)
126 if ((surface.get_width() % NUM_OF_CELLS_X != 0)
127 || (surface.get_height() % NUM_OF_CELLS_Y != 0)
128 || (surface.get_width() / NUM_OF_CELLS_X != surface.get_height() / NUM_OF_CELLS_Y)) {
129 gd_critical(CPrintf("Image should contain %d cells in a row and %d in a column!") % int(NUM_OF_CELLS_X) % int(NUM_OF_CELLS_Y));
130 return false;
132 if (surface.get_width() / NUM_OF_CELLS_X < 16) {
133 gd_critical("The image should contain cells which are at least 16x16 pixels in size!");
135 if (!surface.has_alpha()) {
136 gd_critical("Image should have an alpha channel!");
137 return false;
140 return true; /* passed checks */
143 bool CellRenderer::is_image_ok_for_theme(PixbufFactory &pixbuf_factory, const char *filename)
145 try {
146 Pixbuf *image=pixbuf_factory.create_from_file(filename);
147 /* if the image is loaded */
148 SetLoggerContextForFunction scf(filename);
149 bool result=is_pixbuf_ok_for_theme(*image);
150 delete image;
151 return result;
152 } catch (...) {
153 return false;
157 /* load theme from image file. */
158 /* return true if successful. */
159 bool CellRenderer::loadcells_image(Pixbuf *image)
161 /* do some checks. if those fail, the error is already reported by the function */
162 if (!is_pixbuf_ok_for_theme(*image)) {
163 delete image;
164 return false;
167 /* remove old stuff */
168 remove_cached();
169 delete loaded;
170 loaded = NULL;
172 /* load new stuff */
173 cell_size = image->get_width()/NUM_OF_CELLS_X;
174 loaded = image;
176 if (check_if_pixbuf_c64_png(*loaded)) {
177 /* c64 pixbuf with a small number of colors which can be changed */
178 cells_all = NULL;
179 is_c64_colored = true;
180 } else {
181 /* normal, "truecolor" pixbuf */
182 cells_all = loaded;
183 loaded = NULL;
184 is_c64_colored = false;
186 return true;
190 /* load theme from image file. */
191 /* return true if successful. */
192 bool CellRenderer::loadcells_file(const std::string& filename)
194 /* load cell graphics */
195 /* load from file */
196 try {
197 Pixbuf *image = pixbuf_factory.create_from_file(filename.c_str());
198 return loadcells_image(image);
199 } catch (std::exception &e) {
200 gd_critical(CPrintf("%s: unable to load image (%s)") % filename % e.what());
201 return false;
205 /* load the theme specified in theme_file. */
206 /* if successful, ok. */
207 /* if fails, or no theme specified, load the builtin */
208 void CellRenderer::load_theme_file(const std::string& theme_file)
210 if (theme_file!="" && loadcells_file(theme_file)) {
211 /* loaded from png file */
212 } else {
213 Pixbuf *image = pixbuf_factory.create_from_inline(sizeof(c64_gfx), c64_gfx);
214 loadcells_image(image);
218 int CellRenderer::get_cell_size()
220 return cell_size*pixbuf_factory.get_pixmap_scale();
223 bool CellRenderer::get_pal_emulation() const
225 return pixbuf_factory.get_pal_emulation();
228 void CellRenderer::select_pixbuf_colors(GdColor c0, GdColor c1, GdColor c2, GdColor c3, GdColor c4, GdColor c5)
230 if (c0!=color0 || c1!=color1 || c2!=color2 || c3!=color3 || c4!=color4 || c5!=color5) {
231 /* if not the same colors as requested before */
232 color0 = c0;
233 color1 = c1;
234 color2 = c2;
235 color3 = c3;
236 color4 = c4;
237 color5 = c5;
238 if (is_c64_colored)
239 remove_cached();
245 static inline int
246 c64_color_index(int h, int s, int v, int a)
248 if (a < 0x80)
249 return 8; /* transparent */
250 if (v < 0x10)
251 return 0; /* black */
252 if (s < 0x10)
253 return 7; /* editor white, arrows & etc */
255 if (h < 30 || h >= 330) /* around 0 */
256 return 1; /* red - foreg1 */
257 if (h >= 270 && h < 330) /* around 300 */
258 return 2; /* purple - foreg2 */
259 if (h >= 30 && h < 90) /* around 60 */
260 return 3; /* yellow - foreg3 */
261 if (h >= 90 && h < 150) /* around 120 */
262 return 4; /* green - amoeba */
263 if (h >= 210 && h < 270) /* around 240 */
264 return 5; /* slime */
266 if (h >= 150 && h < 210) /* around 180 */
267 return 6; /* cyan - editor black */
269 return 0; /* should be unreachable */
273 /* returns true, if the given pixbuf seems to be a c64 imported image. */
274 bool CellRenderer::check_if_pixbuf_c64_png(Pixbuf const& image)
276 int wx=image.get_width()*4; // 4 bytes/pixel
277 int h=image.get_height();
279 image.lock();
280 bool c64_png = true;
281 for (int y=0; y<h; y++) {
282 const unsigned char *p=(const unsigned char *) image.get_row(y);
283 for (int x=0; x<wx; x++)
284 if (p[x]!=0 && p[x]!=255)
285 c64_png=false;
287 image.unlock();
289 return c64_png;
293 /** This function takes the loaded image, and transforms it using the selected
294 * cave colors, to create cells_all.
296 * The process is as follows. All pixels are converted to HSV (hue, saturation,
297 * value). The hues of the pixels should be 0 (red), 60 (yellow), 120 (green)
298 * etc, n*60. This way will the routine recognize the colors.
299 * After converting the pixels to HSV, the hue selects the cave color to use.
300 * The resulting color will use the hue of the selected cave color, the product
301 * of the saturations of the cave color and the original color, and the
302 * product of the values:
304 * Hresult = Hcave
305 * Sresult = Scave * Sloadedimage
306 * Vresult = Vcave * Vloadedimage.
308 * This allows for modulating the cave colors in saturation and value. If the
309 * loaded image contains a dark purple color instead of RGB(255;0;255) purple,
310 * the cave color will also be darkened at that pixel and so on. */
311 void CellRenderer::create_colorized_cells() {
312 g_assert(is_c64_colored);
313 g_assert(loaded != NULL);
315 if (cells_all)
316 delete cells_all;
318 GdColor cols[9]; /* holds rgba for color indexes internally used */
320 cols[0] = color0.to_hsv(); /* c64 background */
321 cols[1] = color1.to_hsv(); /* foreg1 */
322 cols[2] = color2.to_hsv(); /* foreg2 */
323 cols[3] = color3.to_hsv(); /* foreg3 */
324 cols[4] = color4.to_hsv(); /* amoeba */
325 cols[5] = color5.to_hsv(); /* slime */
326 cols[6] = GdColor::from_hsv(0, 0, 0); /* black, opaque */
327 cols[7] = GdColor::from_hsv(0, 0, 100); /* white, opaque */
328 cols[8] = GdColor::from_hsv(0, 0, 0); /* for the transparent */
330 int w = loaded->get_width(), h = loaded->get_height();
331 cells_all = pixbuf_factory.create(w, h);
333 cells_all->lock();
334 loaded->lock();
336 for (int y=0; y<h; y++) {
337 const guint32 *p = loaded->get_row(y);
338 guint32 *to = cells_all->get_row(y);
339 for (int x=0; x<w; x++) {
340 /* rgb values found in image */
341 int r = (p[x] & loaded->rmask) >> loaded->rshift;
342 int g = (p[x] & loaded->gmask) >> loaded->gshift;
343 int b = (p[x] & loaded->bmask) >> loaded->bshift;
344 int a = (p[x] & loaded->amask) >> loaded->ashift;
346 GdColor in = GdColor::from_rgb(r, g, b).to_hsv();
347 /* the color code from the original image (essentially the hue) will select the color index */
348 unsigned index = c64_color_index(in.get_h(), in.get_s(), in.get_v(), a);
349 /* and the saturation and value from the original image will modify it */
350 unsigned s_multiplier = in.get_s();
351 unsigned v_multiplier = in.get_v();
353 GdColor newcol = GdColor::from_hsv(
354 cols[index].get_h(),
355 cols[index].get_s() * s_multiplier / 100,
356 cols[index].get_v() * v_multiplier / 100).to_rgb();
358 guint32 newcolword =
359 newcol.get_r() << cells_all->rshift |
360 newcol.get_g() << cells_all->gshift |
361 newcol.get_b() << cells_all->bshift |
362 a << cells_all->ashift;
364 to[x] = newcolword;
368 loaded->unlock();
369 cells_all->unlock();