20130427
[gdash.git] / src / gtk / gtkscreen.cpp
blob32b70581ad468f732d246df8369f43f12356287e
1 /*
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.
24 #include "config.h"
26 #include <gtk/gtk.h>
27 #include <memory>
28 #include <cmath>
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),
57 cr(NULL),
58 back(NULL) {
59 set_drawing_area(drawing_area);
63 void GTKScreen::free_buffer() {
64 if (cr)
65 cairo_destroy(cr);
66 cr = NULL;
67 if (back)
68 cairo_surface_destroy(back);
69 back = NULL;
73 void GTKScreen::configure_size() {
74 free_buffer();
75 /* invalid size? do not configure yet. */
76 if (w == 0 || h == 0)
77 return;
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);
91 else
92 back = gdk_window_create_similar_surface(drawing_area->window, CAIRO_CONTENT_COLOR, w, h);
93 } else {
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() {
102 free_buffer();
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) {
115 free_buffer();
116 this->drawing_area = drawing_area;
117 if (drawing_area == NULL) {
118 /* make sure that the new set size request will do its job */
119 w = h = 0;
121 /* call this to reconfigure back buffer & stuff */
122 configure_size();
126 void GTKScreen::set_clip_rect(int x1, int y1, int w, int h) {
127 cairo_rectangle(cr, x1, y1, w, h);
128 cairo_clip(cr);
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)
140 return;
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);
145 cairo_fill(flipcr);
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;
152 c.get_rgb(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);
155 cairo_fill(cr);
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)
163 return;
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);
168 cairo_fill(cr);
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);
184 cairo_fill(cr);
185 cairo_destroy(cr);
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);
205 cairo_fill(cr);