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