2 * Copyright (c) 2007-2013, Czirkos Zoltan http://code.google.com/p/gdash/
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
19 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
20 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 #include "gfx/cellrenderer.hpp"
30 #include "misc/logger.hpp"
31 #include "gfx/pixbuf.hpp"
32 #include "gfx/pixbuffactory.hpp"
33 #include "gfx/screen.hpp"
37 #include "c64_gfx.cpp"
39 CellRenderer::CellRenderer(Screen
&screen
, const std::string
&theme_file
)
40 : PixmapStorage(screen
),
43 is_c64_colored(false),
47 color0(GD_GDASH_BLACK
),
48 color1(GD_GDASH_MIDDLEBLUE
),
49 color2(GD_GDASH_LIGHTRED
),
50 color3(GD_GDASH_WHITE
),
51 color4(GD_GDASH_WHITE
),
52 color5(GD_GDASH_WHITE
),
54 load_theme_file(theme_file
);
58 CellRenderer::~CellRenderer() {
64 /** Remove colored Pixbufs and Pixmaps created. */
65 void CellRenderer::remove_cached() {
66 for (unsigned i
= 0; i
< G_N_ELEMENTS(cells_pixbufs
); ++i
) {
67 delete cells_pixbufs
[i
];
68 cells_pixbufs
[i
] = NULL
;
70 CellRenderer::release_pixmaps();
78 void CellRenderer::release_pixmaps() {
79 for (unsigned i
= 0; i
< G_N_ELEMENTS(cells
); ++i
) {
87 Pixbuf
&CellRenderer::cell_pixbuf(unsigned i
) {
88 g_assert(i
< G_N_ELEMENTS(cells_pixbufs
));
89 if (cells_all
== NULL
)
90 create_colorized_cells();
91 g_assert(cells_all
!= NULL
);
92 if (cells_pixbufs
[i
] == NULL
)
93 cells_pixbufs
[i
] = screen
.pixbuf_factory
.create_subpixbuf(*cells_all
, (i
% NUM_OF_CELLS_X
) * cell_size
, (i
/ NUM_OF_CELLS_X
) * cell_size
, cell_size
, cell_size
);
94 return *cells_pixbufs
[i
];
97 Pixmap
&CellRenderer::cell(unsigned i
) {
98 g_assert(i
< G_N_ELEMENTS(cells
));
99 if (cells
[i
] == NULL
) {
100 int type
= i
/ NUM_OF_CELLS
; // 0=normal, 1=colored1, 2=colored2
101 int index
= i
% NUM_OF_CELLS
;
102 Pixbuf
&pb
= cell_pixbuf(index
); // this is to be rendered as a pixmap, but may be colored
106 cells
[i
] = screen
.create_scaled_pixmap_from_pixbuf(pb
, false);
109 std::auto_ptr
<Pixbuf
> colored(screen
.pixbuf_factory
.create_composite_color(pb
, gd_flash_color
));
110 cells
[i
] = screen
.create_scaled_pixmap_from_pixbuf(*colored
, false);
114 std::auto_ptr
<Pixbuf
> colored(screen
.pixbuf_factory
.create_composite_color(pb
, gd_select_color
));
115 cells
[i
] = screen
.create_scaled_pixmap_from_pixbuf(*colored
, false);
119 g_assert_not_reached();
126 /* check if given surface is ok to be a gdash theme. */
127 bool CellRenderer::is_pixbuf_ok_for_theme(const Pixbuf
&surface
) {
128 if ((surface
.get_width() % NUM_OF_CELLS_X
!= 0)
129 || (surface
.get_height() % NUM_OF_CELLS_Y
!= 0)
130 || (surface
.get_width() / NUM_OF_CELLS_X
!= surface
.get_height() / NUM_OF_CELLS_Y
)) {
131 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
));
134 if (surface
.get_width() / NUM_OF_CELLS_X
< 16) {
135 gd_critical("The image should contain cells which are at least 16x16 pixels in size!");
138 return true; /* passed checks */
141 bool CellRenderer::is_image_ok_for_theme(PixbufFactory
&pixbuf_factory
, const char *filename
) {
143 SetLoggerContextForFunction
scf(filename
);
144 std::auto_ptr
<Pixbuf
> image(pixbuf_factory
.create_from_file(filename
));
145 return is_pixbuf_ok_for_theme(*image
);
151 /* load theme from image file. */
152 /* return true if successful. */
153 bool CellRenderer::loadcells_image(Pixbuf
*image
) {
154 /* do some checks. if those fail, the error is already reported by the function */
155 if (!is_pixbuf_ok_for_theme(*image
)) {
160 /* remove old stuff */
166 cell_size
= image
->get_width() / NUM_OF_CELLS_X
;
169 if (check_if_pixbuf_c64_png(*loaded
)) {
170 /* c64 pixbuf with a small number of colors which can be changed */
172 is_c64_colored
= true;
174 /* normal, "truecolor" pixbuf */
177 is_c64_colored
= false;
183 /* load theme from image file. */
184 /* return true if successful. */
185 bool CellRenderer::loadcells_file(const std::string
&filename
) {
186 /* load cell graphics */
189 Pixbuf
*image
= screen
.pixbuf_factory
.create_from_file(filename
.c_str());
190 return loadcells_image(image
);
191 } catch (std::exception
&e
) {
192 gd_critical(CPrintf("%s: unable to load image (%s)") % filename
% e
.what());
197 /* load the theme specified in theme_file. */
198 /* if successful, ok. */
199 /* if fails, or no theme specified, load the builtin */
200 void CellRenderer::load_theme_file(const std::string
&theme_file
) {
201 if (theme_file
!= "" && loadcells_file(theme_file
)) {
202 /* loaded from png file */
204 Pixbuf
*image
= screen
.pixbuf_factory
.create_from_inline(sizeof(c64_gfx
), c64_gfx
);
205 loadcells_image(image
);
209 int CellRenderer::get_cell_size() {
210 return cell_size
* screen
.get_pixmap_scale();
214 void CellRenderer::select_pixbuf_colors(GdColor c0
, GdColor c1
, GdColor c2
, GdColor c3
, GdColor c4
, GdColor c5
) {
215 if (c0
!= color0
|| c1
!= color1
|| c2
!= color2
|| c3
!= color3
|| c4
!= color4
|| c5
!= color5
) {
216 /* if not the same colors as requested before */
231 c64_color_index(int h
, int s
, int v
, int a
) {
233 return 8; /* transparent */
235 return 0; /* black */
237 return 7; /* editor white, arrows & etc */
239 if (h
< 30 || h
>= 330) /* around 0 */
240 return 1; /* red - foreg1 */
241 if (h
>= 270 && h
< 330) /* around 300 */
242 return 2; /* purple - foreg2 */
243 if (h
>= 30 && h
< 90) /* around 60 */
244 return 3; /* yellow - foreg3 */
245 if (h
>= 90 && h
< 150) /* around 120 */
246 return 4; /* green - amoeba */
247 if (h
>= 210 && h
< 270) /* around 240 */
248 return 5; /* slime */
250 if (h
>= 150 && h
< 210) /* around 180 */
251 return 6; /* cyan - editor black */
253 return 0; /* should be unreachable */
257 /* returns true, if the given pixbuf seems to be a c64 imported image. */
258 bool CellRenderer::check_if_pixbuf_c64_png(Pixbuf
const &image
) {
259 int wx
= image
.get_width() * 4; // 4 bytes/pixel
260 int h
= image
.get_height();
263 for (int y
= 0; y
< h
; y
++) {
264 const unsigned char *p
= (const unsigned char *) image
.get_row(y
);
265 for (int x
= 0; x
< wx
; x
++)
266 if (p
[x
] != 0 && p
[x
] != 255)
274 /** This function takes the loaded image, and transforms it using the selected
275 * cave colors, to create cells_all.
277 * The process is as follows. All pixels are converted to HSV (hue, saturation,
278 * value). The hues of the pixels should be 0 (red), 60 (yellow), 120 (green)
279 * etc, n*60. This way will the routine recognize the colors.
280 * After converting the pixels to HSV, the hue selects the cave color to use.
281 * The resulting color will use the hue of the selected cave color, the product
282 * of the saturations of the cave color and the original color, and the
283 * product of the values:
286 * Sresult = Scave * Sloadedimage
287 * Vresult = Vcave * Vloadedimage.
289 * This allows for modulating the cave colors in saturation and value. If the
290 * loaded image contains a dark purple color instead of RGB(255;0;255) purple,
291 * the cave color will also be darkened at that pixel and so on. */
292 void CellRenderer::create_colorized_cells() {
293 g_assert(is_c64_colored
);
294 g_assert(loaded
!= NULL
);
299 GdColor cols
[9]; /* holds rgba for color indexes internally used */
301 cols
[0] = color0
.to_hsv(); /* c64 background */
302 cols
[1] = color1
.to_hsv(); /* foreg1 */
303 cols
[2] = color2
.to_hsv(); /* foreg2 */
304 cols
[3] = color3
.to_hsv(); /* foreg3 */
305 cols
[4] = color4
.to_hsv(); /* amoeba */
306 cols
[5] = color5
.to_hsv(); /* slime */
307 cols
[6] = GdColor::from_hsv(0, 0, 0); /* black, opaque */
308 cols
[7] = GdColor::from_hsv(0, 0, 100); /* white, opaque */
309 cols
[8] = GdColor::from_hsv(0, 0, 0); /* for the transparent */
311 int w
= loaded
->get_width(), h
= loaded
->get_height();
312 cells_all
= screen
.pixbuf_factory
.create(w
, h
);
314 for (int y
= 0; y
< h
; y
++) {
315 const guint32
*p
= loaded
->get_row(y
);
316 guint32
*to
= cells_all
->get_row(y
);
317 for (int x
= 0; x
< w
; x
++) {
318 /* rgb values found in image */
319 unsigned r
= (p
[x
] & loaded
->rmask
) >> loaded
->rshift
;
320 unsigned g
= (p
[x
] & loaded
->gmask
) >> loaded
->gshift
;
321 unsigned b
= (p
[x
] & loaded
->bmask
) >> loaded
->bshift
;
322 unsigned a
= (p
[x
] & loaded
->amask
) >> loaded
->ashift
;
323 GdColor in
= GdColor::from_rgb(r
, g
, b
).to_hsv();
324 unsigned h
= in
.get_h();
325 unsigned s
= in
.get_s();
326 unsigned v
= in
.get_v();
328 /* the color code from the original image (essentially the hue) will select the color index */
329 unsigned index
= c64_color_index(h
, s
, v
, a
);
331 if (index
== 0 || index
>= 6) {
332 /* for the background and the editor colors, no shading is used */
333 newcol
= cols
[index
];
335 /* otherwise the saturation and value from the original image will modify it */
336 unsigned s_multiplier
= s
;
337 unsigned v_multiplier
= v
;
338 newcol
= GdColor::from_hsv(
340 cols
[index
].get_s() * s_multiplier
/ 100,
341 cols
[index
].get_v() * v_multiplier
/ 100).to_rgb();
345 newcol
.get_r() << cells_all
->rshift
|
346 newcol
.get_g() << cells_all
->gshift
|
347 newcol
.get_b() << cells_all
->bshift
|
348 a
<< cells_all
->ashift
;