1 //
2 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010,
3 // 2011 Free Software Foundation, Inc
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19 #include "log.h"
20 #include "gtk_glue_cairo.h"
21 #include "Renderer_cairo.h"
22
23 #if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 8
24 # include "gtk_cairo_create.h"
25 #endif
26
27
28 namespace gnash
29 {
30
GtkCairoGlue()31 GtkCairoGlue::GtkCairoGlue()
32 : _cairo_handle(nullptr),
33 _cairo_offscreen(nullptr),
34 _renderer(nullptr),
35 _image(nullptr)
36 {
37 GNASH_REPORT_FUNCTION;
38 }
39
~GtkCairoGlue()40 GtkCairoGlue::~GtkCairoGlue()
41 {
42 if (_cairo_handle) cairo_destroy(_cairo_handle);
43 if (_cairo_offscreen) cairo_destroy(_cairo_offscreen);
44 if (_image) gdk_image_destroy(_image);
45 }
46
47 bool
init(int,char ***)48 GtkCairoGlue::init(int /*argc*/, char *** /*argv*/)
49 {
50 return true;
51 }
52
53 void
prepDrawingArea(GtkWidget * drawing_area)54 GtkCairoGlue::prepDrawingArea(GtkWidget *drawing_area)
55 {
56 _drawing_area = drawing_area;
57 // Disable double buffering, otherwise gtk tries to update widget
58 // contents from its internal offscreen buffer at the end of expose event
59 gtk_widget_set_double_buffered(_drawing_area, FALSE);
60 }
61
62 Renderer*
createRenderHandler()63 GtkCairoGlue::createRenderHandler()
64 {
65 _renderer = renderer::cairo::create_handler();
66
67 return _renderer;
68 }
69
70 void
beforeRendering()71 GtkCairoGlue::beforeRendering()
72 {
73 if (_image && _image->type == GDK_IMAGE_SHARED) {
74 gdk_flush();
75 }
76 }
77
78 void
render(int minx,int miny,int maxx,int maxy)79 GtkCairoGlue::render(int minx, int miny, int maxx, int maxy)
80 {
81 if (!_cairo_offscreen) {
82 return;
83 }
84
85 const int& x = minx;
86 const int& y = miny;
87 int width = maxx - minx;
88 int height = maxy - miny;
89
90 if (_image) {
91 // Using GdkImage for our image buffer, use GdkRgb (potentially shm).
92 GdkGC* gc = gdk_gc_new(_drawing_area->window);
93
94 gdk_draw_image(_drawing_area->window, gc, _image, x, y, x, y, width,
95 height);
96 gdk_gc_unref(gc);
97 return;
98 }
99
100 cairo_save(_cairo_offscreen);
101
102 cairo_rectangle(_cairo_offscreen, minx, miny, maxx - minx, maxy - miny);
103 cairo_clip(_cairo_offscreen);
104
105 render();
106
107 cairo_restore(_cairo_offscreen);
108 }
109
110 void
render()111 GtkCairoGlue::render()
112 {
113 if (!_cairo_offscreen) return;
114
115 // Blit offscreen image onto output window
116 cairo_set_source_surface(_cairo_handle,
117 cairo_get_target(_cairo_offscreen), 0, 0);
118 cairo_paint(_cairo_handle);
119 }
120
121 bool
cairoFormatFromVisual(const GdkVisual * visual,cairo_format_t * format)122 cairoFormatFromVisual(const GdkVisual* visual, cairo_format_t* format /*out*/)
123 {
124 switch(visual->depth) {
125 case 24:
126 *format = CAIRO_FORMAT_RGB24;
127 break;
128 case 32:
129 *format = CAIRO_FORMAT_ARGB32;
130 break;
131 default:
132 format = nullptr;
133 return false;
134 }
135 return true;
136 }
137
138 cairo_surface_t*
createGdkImageSurface(const int & width,const int & height)139 GtkCairoGlue::createGdkImageSurface(const int& width, const int& height)
140 {
141 GdkVisual* visual = gdk_drawable_get_visual(_drawing_area->window);
142 assert(_drawing_area);
143 assert(visual);
144 cairo_format_t format;
145
146 if (!cairoFormatFromVisual(visual, &format)) {
147 return nullptr;
148 }
149
150 _image = gdk_image_new (GDK_IMAGE_FASTEST, visual, width, height);
151 if (!_image) {
152 return nullptr;
153 }
154
155 cairo_surface_t* surface =
156 cairo_image_surface_create_for_data ((unsigned char *)_image->mem,
157 format, _image->width, _image->height, _image->bpl);
158
159 if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
160 cairo_surface_destroy(surface);
161 gdk_image_destroy(_image);
162 _image = nullptr;
163 return nullptr;
164 }
165
166 return surface;
167 }
168
169 cairo_surface_t*
createSimilarSurface(const int & width,const int & height)170 GtkCairoGlue::createSimilarSurface(const int& width, const int& height)
171 {
172 cairo_surface_t* target = cairo_get_target(_cairo_handle);
173
174 cairo_surface_t* surface = cairo_surface_create_similar(target,
175 cairo_surface_get_content(target), width, height);
176
177 if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
178 cairo_surface_destroy(surface);
179 return nullptr;
180 }
181 return surface;
182 }
183
184 cairo_surface_t*
createMemorySurface(const int & width,const int & height)185 GtkCairoGlue::createMemorySurface(const int& width, const int& height)
186 {
187 cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
188 width, height);
189
190 if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
191 cairo_surface_destroy(surface);
192 return nullptr;
193 }
194 return surface;
195 }
196
197 void
configure(GtkWidget * const,GdkEventConfigure * const event)198 GtkCairoGlue::configure(GtkWidget *const /*widget*/,
199 GdkEventConfigure *const event)
200 {
201 if (!_drawing_area) return;
202
203 if (_image) {
204 gdk_image_destroy(_image);
205 _image = nullptr;
206 }
207
208 cairo_surface_t* surface = createGdkImageSurface(event->width, event->height);
209
210 if (!surface) {
211
212 if (!_cairo_handle) {
213 _cairo_handle = gdk_cairo_create(_drawing_area->window);
214 }
215
216 surface = createMemorySurface(event->width, event->height);
217 }
218
219 if (!surface) {
220 surface = createSimilarSurface(event->width, event->height);
221 }
222
223 if (!surface) {
224 log_error(_("Cairo: failed to create a rendering buffer!"));
225 return;
226 }
227
228 _cairo_offscreen = cairo_create(surface);
229 cairo_surface_destroy(surface);
230
231 renderer::cairo::set_context(_renderer, _cairo_offscreen);
232 }
233
234 } // namespace gnash
235
236