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.
30 #include "gtk/gtkpixbuf.hpp"
31 #include "gtk/gtkpixbuffactory.hpp"
32 #include "gtk/gtkscreen.hpp"
33 #include "cave/colors.hpp"
34 #include "cave/particle.hpp"
35 #include "settings.hpp"
38 GTKPixmap::~GTKPixmap() {
39 g_object_unref(pixbuf
);
40 cairo_surface_destroy(surface
);
44 int GTKPixmap::get_width() const {
45 return gdk_pixbuf_get_width(pixbuf
);
49 int GTKPixmap::get_height() const {
50 return gdk_pixbuf_get_height(pixbuf
);
54 GTKScreen::GTKScreen(PixbufFactory
&pixbuf_factory
, GtkWidget
*drawing_area
)
55 : Screen(pixbuf_factory
),
56 drawing_area(drawing_area
),
59 set_drawing_area(drawing_area
);
63 void GTKScreen::free_buffer() {
68 cairo_surface_destroy(back
);
73 void GTKScreen::configure_size() {
75 /* invalid size? do not configure yet. */
78 if (drawing_area
!= NULL
) {
79 /* rendering to a screen. */
80 gtk_widget_set_double_buffered(drawing_area
, FALSE
);
81 gtk_widget_set_size_request(drawing_area
, w
, h
);
82 /* when using particle effects, a client-side image is created for the back buffer.
83 * when not using particles, a server-side image.
84 * this is because drawing particle effects is much more efficient on the client side.
85 * if we were drawing on the serverside, it would be very slow, as rgba rendering requires
86 * roundtrips to the server.
87 * the chosen back image will also affect the pixmap objects created, but that is handled
88 * automatically by cairo. */
89 if (gd_particle_effects
)
90 back
= cairo_image_surface_create(CAIRO_FORMAT_RGB24
, w
, h
);
92 back
= gdk_window_create_similar_surface(drawing_area
->window
, CAIRO_CONTENT_COLOR
, w
, h
);
94 /* rendering to a software buffer. */
95 back
= cairo_image_surface_create(CAIRO_FORMAT_RGB24
, w
, h
);
97 cr
= cairo_create(back
);
101 GTKScreen::~GTKScreen() {
106 void GTKScreen::set_title(char const *title
) {
107 if (drawing_area
!= NULL
) {
108 GtkWidget
*toplevel
= gtk_widget_get_toplevel(drawing_area
);
109 gtk_window_set_title(GTK_WINDOW(toplevel
), title
);
114 void GTKScreen::set_drawing_area(GtkWidget
*drawing_area
) {
116 this->drawing_area
= drawing_area
;
117 if (drawing_area
== NULL
) {
118 /* make sure that the new set size request will do its job */
121 /* call this to reconfigure back buffer & stuff */
126 void GTKScreen::set_clip_rect(int x1
, int y1
, int w
, int h
) {
127 cairo_rectangle(cr
, x1
, y1
, w
, h
);
132 void GTKScreen::remove_clip_rect() {
133 cairo_reset_clip(cr
);
137 void GTKScreen::flip() {
138 /* if rendering to a software buffer, flipping does nothing. */
139 if (drawing_area
== NULL
)
141 /* otherwise rendering to a window. copy the back buffer to the window. */
142 cairo_t
*flipcr
= gdk_cairo_create(drawing_area
->window
);
143 cairo_set_source_surface(flipcr
, back
, 0, 0);
144 cairo_rectangle(flipcr
, 0, 0, w
, h
);
146 cairo_destroy(flipcr
);
150 void GTKScreen::fill_rect(int x
, int y
, int w
, int h
, const GdColor
&c
) {
151 unsigned char r
, g
, b
;
153 cairo_set_source_rgb(cr
, r
/ 255.0, g
/ 255.0, b
/ 255.0);
154 cairo_rectangle(cr
, x
, y
, w
, h
);
159 void GTKScreen::blit(Pixmap
const &src
, int dx
, int dy
) const {
160 int sw
= src
.get_width(), sh
= src
.get_height();
161 /* if totally out of window, skip */
162 if (dx
+ sw
< 0 || dy
+ sh
< 0 || dx
> w
|| dy
> h
)
164 /* "The x and y parameters give the user-space coordinate at which the surface origin should appear." */
165 GTKPixmap
const &srcgtk
= static_cast<GTKPixmap
const &>(src
);
166 cairo_set_source_surface(cr
, srcgtk
.surface
, dx
, dy
);
167 cairo_rectangle(cr
, dx
, dy
, sw
, sh
);
172 Pixmap
*GTKScreen::create_pixmap_from_pixbuf(const Pixbuf
&pb
, bool keep_alpha
) const {
173 GdkPixbuf
*pixbuf
= (GdkPixbuf
*) static_cast<GTKPixbuf
const &>(pb
).get_gdk_pixbuf();
174 /* we keep the pixmap in a surface that is similar to the back buffer.
175 * if the back buffer is an xlib pixmap, then this will be an xlib pixmap as well,
176 * and the blitting will be very fast, and done by the x server.
177 * if it is a cairo image, this one also will be a cairo image, and cairo will
178 * do the blitting. */
179 cairo_surface_t
*surface
= cairo_surface_create_similar(back
,
180 keep_alpha
? CAIRO_CONTENT_COLOR_ALPHA
: CAIRO_CONTENT_COLOR
, w
, h
);
181 cairo_t
*cr
= cairo_create(surface
);
182 gdk_cairo_set_source_pixbuf(cr
, pixbuf
, 0, 0);
183 cairo_rectangle(cr
, 0, 0, w
, h
);
186 return new GTKPixmap(pixbuf
, surface
);
190 void GTKScreen::draw_particle_set(int dx
, int dy
, ParticleSet
const &ps
) {
191 unsigned char r
, g
, b
;
192 ps
.color
.get_rgb(r
, g
, b
);
193 cairo_set_source_rgba(cr
, r
/ 255.0, g
/ 255.0, b
/ 255.0, ps
.life
/ 1000.0 * ps
.opacity
);
194 int size
= ceil(ps
.size
);
195 /* cairo gets the center of the pixel, like opengl. because it works with
196 * float coordinates, not integers.
197 * dx0, dy0 are the center, and the sides "outgrow". */
198 double dxm
= dx
- size
, dx0
= dx
+ 0.5, dxp
= dx
+ size
+ 1;
199 double dym
= dy
- size
, dy0
= dy
+ 0.5, dyp
= dy
+ size
+ 1;
200 for (ParticleSet::const_iterator it
= ps
.begin(); it
!= ps
.end(); ++it
) {
201 cairo_move_to(cr
, (int)it
->px
+ dx0
, (int)it
->py
+ dym
);
202 cairo_line_to(cr
, (int)it
->px
+ dxp
, (int)it
->py
+ dy0
);
203 cairo_line_to(cr
, (int)it
->px
+ dx0
, (int)it
->py
+ dyp
);
204 cairo_line_to(cr
, (int)it
->px
+ dxm
, (int)it
->py
+ dy0
);