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.
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"
33 #include "c64_gfx.cpp"
35 CellRenderer::CellRenderer(PixbufFactory
& pixbuf_factory_
, const std::string
& theme_file
)
38 is_c64_colored(false),
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() {
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
];
68 for (unsigned i
=0; i
<G_N_ELEMENTS(cells
); ++i
) {
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
));
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
99 cells
[i
]=pixbuf_factory
.create_pixmap_from_pixbuf(pb
, false);
103 Pixbuf
*colored
=pixbuf_factory
.create_composite_color(pb
, gd_flash_color
);
104 cells
[i
]=pixbuf_factory
.create_pixmap_from_pixbuf(*colored
, false);
111 Pixbuf
*colored
=pixbuf_factory
.create_composite_color(pb
, gd_select_color
);
112 cells
[i
]=pixbuf_factory
.create_pixmap_from_pixbuf(*colored
, false);
117 g_assert_not_reached();
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
));
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!");
140 return true; /* passed checks */
143 bool CellRenderer::is_image_ok_for_theme(PixbufFactory
&pixbuf_factory
, const char *filename
)
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
);
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
)) {
167 /* remove old stuff */
173 cell_size
= image
->get_width()/NUM_OF_CELLS_X
;
176 if (check_if_pixbuf_c64_png(*loaded
)) {
177 /* c64 pixbuf with a small number of colors which can be changed */
179 is_c64_colored
= true;
181 /* normal, "truecolor" pixbuf */
184 is_c64_colored
= false;
190 /* load theme from image file. */
191 /* return true if successful. */
192 bool CellRenderer::loadcells_file(const std::string
& filename
)
194 /* load cell graphics */
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());
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 */
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 */
246 c64_color_index(int h
, int s
, int v
, int a
)
249 return 8; /* transparent */
251 return 0; /* black */
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();
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)
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:
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
);
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
);
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(
355 cols
[index
].get_s() * s_multiplier
/ 100,
356 cols
[index
].get_v() * v_multiplier
/ 100).to_rgb();
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
;