1/*
2 * Copyright © 2016 Endless
3 * 2018 Benjamin Otte
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library 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 GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * Authors: Benjamin Otte <otte@gnome.org>
19 */
20
21#include "config.h"
22
23#include "gskcairorenderer.h"
24
25#include "gskdebugprivate.h"
26#include "gskrendererprivate.h"
27#include "gskrendernodeprivate.h"
28#include "gdk/gdktextureprivate.h"
29
30#ifdef G_ENABLE_DEBUG
31typedef struct {
32 GQuark cpu_time;
33 GQuark gpu_time;
34} ProfileTimers;
35#endif
36
37struct _GskCairoRenderer
38{
39 GskRenderer parent_instance;
40
41 GdkCairoContext *cairo_context;
42
43#ifdef G_ENABLE_DEBUG
44 ProfileTimers profile_timers;
45#endif
46};
47
48struct _GskCairoRendererClass
49{
50 GskRendererClass parent_class;
51};
52
53G_DEFINE_TYPE (GskCairoRenderer, gsk_cairo_renderer, GSK_TYPE_RENDERER)
54
55static gboolean
56gsk_cairo_renderer_realize (GskRenderer *renderer,
57 GdkSurface *surface,
58 GError **error)
59{
60 GskCairoRenderer *self = GSK_CAIRO_RENDERER (renderer);
61
62 if (surface)
63 self->cairo_context = gdk_surface_create_cairo_context (surface);
64
65 return TRUE;
66}
67
68static void
69gsk_cairo_renderer_unrealize (GskRenderer *renderer)
70{
71 GskCairoRenderer *self = GSK_CAIRO_RENDERER (renderer);
72
73 g_clear_object (&self->cairo_context);
74}
75
76static void
77gsk_cairo_renderer_do_render (GskRenderer *renderer,
78 cairo_t *cr,
79 GskRenderNode *root)
80{
81#ifdef G_ENABLE_DEBUG
82 GskCairoRenderer *self = GSK_CAIRO_RENDERER (renderer);
83 GskProfiler *profiler;
84 gint64 cpu_time;
85#endif
86
87#ifdef G_ENABLE_DEBUG
88 profiler = gsk_renderer_get_profiler (renderer);
89 gsk_profiler_timer_begin (profiler, timer_id: self->profile_timers.cpu_time);
90#endif
91
92 gsk_render_node_draw (node: root, cr);
93
94#ifdef G_ENABLE_DEBUG
95 cpu_time = gsk_profiler_timer_end (profiler, timer_id: self->profile_timers.cpu_time);
96 gsk_profiler_timer_set (profiler, timer_id: self->profile_timers.cpu_time, value: cpu_time);
97
98 gsk_profiler_push_samples (profiler);
99#endif
100}
101
102static GdkTexture *
103gsk_cairo_renderer_render_texture (GskRenderer *renderer,
104 GskRenderNode *root,
105 const graphene_rect_t *viewport)
106{
107 GdkTexture *texture;
108 cairo_surface_t *surface;
109 cairo_t *cr;
110 int width, height;
111 /* limit from cairo's source code */
112#define MAX_IMAGE_SIZE 32767
113
114 width = ceil (x: viewport->size.width);
115 height = ceil (x: viewport->size.height);
116 if (width > MAX_IMAGE_SIZE || height > MAX_IMAGE_SIZE)
117 {
118 gsize x, y, size, stride;
119 GBytes *bytes;
120 guchar *data;
121
122 stride = width * 4;
123 size = stride * height;
124 data = g_malloc_n (n_blocks: stride, n_block_bytes: height);
125
126 for (y = 0; y < height; y += MAX_IMAGE_SIZE)
127 {
128 for (x = 0; x < width; x += MAX_IMAGE_SIZE)
129 {
130 texture = gsk_cairo_renderer_render_texture (renderer, root,
131 viewport: &GRAPHENE_RECT_INIT (x, y,
132 MIN (MAX_IMAGE_SIZE, viewport->size.width - x),
133 MIN (MAX_IMAGE_SIZE, viewport->size.height - y)));
134 gdk_texture_download (texture,
135 data: data + stride * y + x * 4,
136 stride);
137 g_object_unref (object: texture);
138 }
139 }
140
141 bytes = g_bytes_new_take (data, size);
142 texture = gdk_memory_texture_new (width, height, GDK_MEMORY_DEFAULT, bytes, stride);
143 g_bytes_unref (bytes);
144 return texture;
145 }
146
147 surface = cairo_image_surface_create (format: CAIRO_FORMAT_ARGB32, width, height);
148 cr = cairo_create (target: surface);
149
150 cairo_translate (cr, tx: - viewport->origin.x, ty: - viewport->origin.y);
151
152 gsk_cairo_renderer_do_render (renderer, cr, root);
153
154 cairo_destroy (cr);
155
156 texture = gdk_texture_new_for_surface (surface);
157 cairo_surface_destroy (surface);
158
159 return texture;
160}
161
162static void
163gsk_cairo_renderer_render (GskRenderer *renderer,
164 GskRenderNode *root,
165 const cairo_region_t *region)
166{
167 GskCairoRenderer *self = GSK_CAIRO_RENDERER (renderer);
168 cairo_t *cr;
169
170 gdk_draw_context_begin_frame (GDK_DRAW_CONTEXT (self->cairo_context),
171 region);
172 cr = gdk_cairo_context_cairo_create (self: self->cairo_context);
173
174 g_return_if_fail (cr != NULL);
175
176#ifdef G_ENABLE_DEBUG
177 if (GSK_RENDERER_DEBUG_CHECK (renderer, GEOMETRY))
178 {
179 GdkSurface *surface = gsk_renderer_get_surface (renderer);
180
181 cairo_save (cr);
182 cairo_set_operator (cr, op: CAIRO_OPERATOR_OVER);
183 cairo_rectangle (cr,
184 x: 0, y: 0,
185 width: gdk_surface_get_width (surface), height: gdk_surface_get_height (surface));
186 cairo_set_source_rgba (cr, red: 0, green: 0, blue: 0.85, alpha: 0.5);
187 cairo_stroke (cr);
188 cairo_restore (cr);
189 }
190#endif
191
192 gsk_cairo_renderer_do_render (renderer, cr, root);
193
194 cairo_destroy (cr);
195
196 gdk_draw_context_end_frame (GDK_DRAW_CONTEXT (self->cairo_context));
197}
198
199static void
200gsk_cairo_renderer_class_init (GskCairoRendererClass *klass)
201{
202 GskRendererClass *renderer_class = GSK_RENDERER_CLASS (klass);
203
204 renderer_class->realize = gsk_cairo_renderer_realize;
205 renderer_class->unrealize = gsk_cairo_renderer_unrealize;
206 renderer_class->render = gsk_cairo_renderer_render;
207 renderer_class->render_texture = gsk_cairo_renderer_render_texture;
208}
209
210static void
211gsk_cairo_renderer_init (GskCairoRenderer *self)
212{
213#ifdef G_ENABLE_DEBUG
214 GskProfiler *profiler = gsk_renderer_get_profiler (GSK_RENDERER (self));
215
216 self->profile_timers.cpu_time = gsk_profiler_add_timer (profiler, timer_name: "cpu-time", description: "CPU time", FALSE, TRUE);
217#endif
218}
219
220/**
221 * gsk_cairo_renderer_new:
222 *
223 * Creates a new Cairo renderer.
224 *
225 * The Cairo renderer is the fallback renderer drawing in ways similar
226 * to how GTK 3 drew its content. Its primary use is as comparison tool.
227 *
228 * The Cairo renderer is incomplete. It cannot render 3D transformed
229 * content and will instead render an error marker. Its usage should be
230 * avoided.
231 *
232 * Returns: a new Cairo renderer.
233 **/
234GskRenderer *
235gsk_cairo_renderer_new (void)
236{
237 return g_object_new (GSK_TYPE_CAIRO_RENDERER, NULL);
238}
239

source code of gtk/gsk/gskcairorenderer.c