/src/mozilla-central/gfx/thebes/gfxXlibNativeRenderer.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
2 | | * This Source Code Form is subject to the terms of the Mozilla Public |
3 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
4 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
5 | | |
6 | | #include "gfxXlibNativeRenderer.h" |
7 | | |
8 | | #include "gfxXlibSurface.h" |
9 | | #include "gfxImageSurface.h" |
10 | | #include "gfxContext.h" |
11 | | #include "gfxPlatform.h" |
12 | | #include "gfxAlphaRecovery.h" |
13 | | #include "cairo-xlib.h" |
14 | | #include "cairo-xlib-xrender.h" |
15 | | #include "mozilla/gfx/BorrowedContext.h" |
16 | | #include "mozilla/gfx/HelpersCairo.h" |
17 | | #include "gfx2DGlue.h" |
18 | | |
19 | | using namespace mozilla; |
20 | | using namespace mozilla::gfx; |
21 | | |
22 | | #if 0 |
23 | | #include <stdio.h> |
24 | | #define NATIVE_DRAWING_NOTE(m) fprintf(stderr, m) |
25 | | #else |
26 | 0 | #define NATIVE_DRAWING_NOTE(m) do {} while (0) |
27 | | #endif |
28 | | |
29 | | /* We have four basic strategies available: |
30 | | |
31 | | 1) 'direct': If the target is an xlib surface, and other conditions are met, |
32 | | we can pass the underlying drawable directly to the callback. |
33 | | |
34 | | 2) 'simple': If the drawing is opaque, or we can draw to a surface with an |
35 | | alpha channel, then we can create a temporary xlib surface, pass its |
36 | | underlying drawable to the callback, and composite the result using |
37 | | cairo. |
38 | | |
39 | | 3) 'copy-background': If the drawing is not opaque but the target is |
40 | | opaque, and we can draw to a surface with format such that pixel |
41 | | conversion to and from the target format is exact, we can create a |
42 | | temporary xlib surface, copy the background from the target, pass the |
43 | | underlying drawable to the callback, and copy back to the target. |
44 | | |
45 | | This strategy is not used if the pixel format conversion is not exact, |
46 | | because that would mean that drawing intended to be very transparent |
47 | | messes with other content. |
48 | | |
49 | | The strategy is prefered over simple for non-opaque drawing and opaque |
50 | | targets on the same screen as compositing without alpha is a simpler |
51 | | operation. |
52 | | |
53 | | 4) 'alpha-extraction': create a temporary xlib surface, fill with black, |
54 | | pass its underlying drawable to the callback, copy the results to a |
55 | | cairo image surface, repeat with a white background, update the on-black |
56 | | image alpha values by comparing the two images, then paint the on-black |
57 | | image using cairo. |
58 | | |
59 | | Sure would be nice to have an X extension or GL to do this for us on the |
60 | | server... |
61 | | */ |
62 | | |
63 | | static cairo_bool_t |
64 | | _convert_coord_to_int (double coord, int32_t *v) |
65 | 0 | { |
66 | 0 | *v = (int32_t)coord; |
67 | 0 | /* XXX allow some tolerance here? */ |
68 | 0 | return *v == coord; |
69 | 0 | } |
70 | | |
71 | | static bool |
72 | | _get_rectangular_clip (cairo_t *cr, |
73 | | const IntRect& bounds, |
74 | | bool *need_clip, |
75 | | IntRect *rectangles, int max_rectangles, |
76 | | int *num_rectangles) |
77 | 0 | { |
78 | 0 | cairo_rectangle_list_t *cliplist; |
79 | 0 | cairo_rectangle_t *clips; |
80 | 0 | int i; |
81 | 0 | bool retval = true; |
82 | 0 |
|
83 | 0 | cliplist = cairo_copy_clip_rectangle_list (cr); |
84 | 0 | if (cliplist->status != CAIRO_STATUS_SUCCESS) { |
85 | 0 | retval = false; |
86 | 0 | NATIVE_DRAWING_NOTE("FALLBACK: non-rectangular clip"); |
87 | 0 | goto FINISH; |
88 | 0 | } |
89 | 0 |
|
90 | 0 | /* the clip is always in surface backend coordinates (i.e. native backend coords) */ |
91 | 0 | clips = cliplist->rectangles; |
92 | 0 |
|
93 | 0 | for (i = 0; i < cliplist->num_rectangles; ++i) { |
94 | 0 |
|
95 | 0 | IntRect rect; |
96 | 0 | if (!_convert_coord_to_int (clips[i].x, &rect.x) || |
97 | 0 | !_convert_coord_to_int (clips[i].y, &rect.y) || |
98 | 0 | !_convert_coord_to_int (clips[i].width, &rect.width) || |
99 | 0 | !_convert_coord_to_int (clips[i].height, &rect.height)) |
100 | 0 | { |
101 | 0 | retval = false; |
102 | 0 | NATIVE_DRAWING_NOTE("FALLBACK: non-integer clip"); |
103 | 0 | goto FINISH; |
104 | 0 | } |
105 | 0 |
|
106 | 0 | if (rect.IsEqualInterior(bounds)) { |
107 | 0 | /* the bounds are entirely inside the clip region so we don't need to clip. */ |
108 | 0 | *need_clip = false; |
109 | 0 | goto FINISH; |
110 | 0 | } |
111 | 0 | |
112 | 0 | NS_ASSERTION(bounds.Contains(rect), |
113 | 0 | "Was expecting to be clipped to bounds"); |
114 | 0 |
|
115 | 0 | if (i >= max_rectangles) { |
116 | 0 | retval = false; |
117 | 0 | NATIVE_DRAWING_NOTE("FALLBACK: unsupported clip rectangle count"); |
118 | 0 | goto FINISH; |
119 | 0 | } |
120 | 0 |
|
121 | 0 | rectangles[i] = rect; |
122 | 0 | } |
123 | 0 | |
124 | 0 | *need_clip = true; |
125 | 0 | *num_rectangles = cliplist->num_rectangles; |
126 | 0 |
|
127 | 0 | FINISH: |
128 | 0 | cairo_rectangle_list_destroy (cliplist); |
129 | 0 |
|
130 | 0 | return retval; |
131 | 0 | } |
132 | | |
133 | 0 | #define MAX_STATIC_CLIP_RECTANGLES 50 |
134 | | |
135 | | /** |
136 | | * Try the direct path. |
137 | | * @return True if we took the direct path |
138 | | */ |
139 | | bool |
140 | | gfxXlibNativeRenderer::DrawDirect(DrawTarget* aDT, IntSize size, |
141 | | uint32_t flags, |
142 | | Screen *screen, Visual *visual) |
143 | 0 | { |
144 | 0 | // We need to actually borrow the context because we want to read out the |
145 | 0 | // clip rectangles. |
146 | 0 | BorrowedCairoContext borrowed(aDT); |
147 | 0 | if (!borrowed.mCairo) { |
148 | 0 | return false; |
149 | 0 | } |
150 | 0 | |
151 | 0 | bool direct = DrawCairo(borrowed.mCairo, size, flags, screen, visual); |
152 | 0 | borrowed.Finish(); |
153 | 0 |
|
154 | 0 | return direct; |
155 | 0 | } |
156 | | |
157 | | bool |
158 | | gfxXlibNativeRenderer::DrawCairo(cairo_t* cr, IntSize size, |
159 | | uint32_t flags, |
160 | | Screen *screen, Visual *visual) |
161 | 0 | { |
162 | 0 | /* Check that the target surface is an xlib surface. */ |
163 | 0 | cairo_surface_t *target = cairo_get_group_target (cr); |
164 | 0 | if (cairo_surface_get_type (target) != CAIRO_SURFACE_TYPE_XLIB) { |
165 | 0 | NATIVE_DRAWING_NOTE("FALLBACK: non-X surface"); |
166 | 0 | return false; |
167 | 0 | } |
168 | 0 |
|
169 | 0 | cairo_matrix_t matrix; |
170 | 0 | cairo_get_matrix (cr, &matrix); |
171 | 0 | double device_offset_x, device_offset_y; |
172 | 0 | cairo_surface_get_device_offset (target, &device_offset_x, &device_offset_y); |
173 | 0 |
|
174 | 0 | /* Draw() checked that the matrix contained only a very-close-to-integer |
175 | 0 | translation. Here (and in several other places and thebes) device |
176 | 0 | offsets are assumed to be integer. */ |
177 | 0 | NS_ASSERTION(int32_t(device_offset_x) == device_offset_x && |
178 | 0 | int32_t(device_offset_y) == device_offset_y, |
179 | 0 | "Expected integer device offsets"); |
180 | 0 | IntPoint offset(NS_lroundf(matrix.x0 + device_offset_x), |
181 | 0 | NS_lroundf(matrix.y0 + device_offset_y)); |
182 | 0 |
|
183 | 0 | int max_rectangles = 0; |
184 | 0 | if (flags & DRAW_SUPPORTS_CLIP_RECT) { |
185 | 0 | max_rectangles = 1; |
186 | 0 | } |
187 | 0 | if (flags & DRAW_SUPPORTS_CLIP_LIST) { |
188 | 0 | max_rectangles = MAX_STATIC_CLIP_RECTANGLES; |
189 | 0 | } |
190 | 0 |
|
191 | 0 | /* The client won't draw outside the surface so consider this when |
192 | 0 | analysing clip rectangles. */ |
193 | 0 | IntRect bounds(offset, size); |
194 | 0 | bounds.IntersectRect(bounds, |
195 | 0 | IntRect(0, 0, |
196 | 0 | cairo_xlib_surface_get_width(target), |
197 | 0 | cairo_xlib_surface_get_height(target))); |
198 | 0 |
|
199 | 0 | bool needs_clip = true; |
200 | 0 | IntRect rectangles[MAX_STATIC_CLIP_RECTANGLES]; |
201 | 0 | int rect_count = 0; |
202 | 0 |
|
203 | 0 | /* Check that the clip is rectangular and aligned on unit boundaries. */ |
204 | 0 | /* Temporarily set the matrix for _get_rectangular_clip. It's basically |
205 | 0 | the identity matrix, but we must adjust for the fact that our |
206 | 0 | offset-rect is in device coordinates. */ |
207 | 0 | cairo_identity_matrix (cr); |
208 | 0 | cairo_translate (cr, -device_offset_x, -device_offset_y); |
209 | 0 | bool have_rectangular_clip = |
210 | 0 | _get_rectangular_clip (cr, bounds, &needs_clip, |
211 | 0 | rectangles, max_rectangles, &rect_count); |
212 | 0 | cairo_set_matrix (cr, &matrix); |
213 | 0 | if (!have_rectangular_clip) |
214 | 0 | return false; |
215 | 0 | |
216 | 0 | /* Stop now if everything is clipped out */ |
217 | 0 | if (needs_clip && rect_count == 0) |
218 | 0 | return true; |
219 | 0 | |
220 | 0 | /* Check that the screen is supported. |
221 | 0 | Visuals belong to screens, so, if alternate visuals are not supported, |
222 | 0 | then alternate screens cannot be supported. */ |
223 | 0 | bool supports_alternate_visual = |
224 | 0 | (flags & DRAW_SUPPORTS_ALTERNATE_VISUAL) != 0; |
225 | 0 | bool supports_alternate_screen = supports_alternate_visual && |
226 | 0 | (flags & DRAW_SUPPORTS_ALTERNATE_SCREEN); |
227 | 0 | if (!supports_alternate_screen && |
228 | 0 | cairo_xlib_surface_get_screen (target) != screen) { |
229 | 0 | NATIVE_DRAWING_NOTE("FALLBACK: non-default screen"); |
230 | 0 | return false; |
231 | 0 | } |
232 | 0 |
|
233 | 0 | /* Check that there is a visual */ |
234 | 0 | Visual *target_visual = cairo_xlib_surface_get_visual (target); |
235 | 0 | if (!target_visual) { |
236 | 0 | NATIVE_DRAWING_NOTE("FALLBACK: no Visual for surface"); |
237 | 0 | return false; |
238 | 0 | } |
239 | 0 | /* Check that the visual is supported */ |
240 | 0 | if (!supports_alternate_visual && target_visual != visual) { |
241 | 0 | // Only the format of the visual is important (not the GLX properties) |
242 | 0 | // for Xlib or XRender drawing. |
243 | 0 | XRenderPictFormat *target_format = |
244 | 0 | cairo_xlib_surface_get_xrender_format (target); |
245 | 0 | if (!target_format || |
246 | 0 | (target_format != |
247 | 0 | XRenderFindVisualFormat (DisplayOfScreen(screen), visual))) { |
248 | 0 | NATIVE_DRAWING_NOTE("FALLBACK: unsupported Visual"); |
249 | 0 | return false; |
250 | 0 | } |
251 | 0 | } |
252 | 0 |
|
253 | 0 | /* we're good to go! */ |
254 | 0 | NATIVE_DRAWING_NOTE("TAKING FAST PATH\n"); |
255 | 0 | cairo_surface_flush (target); |
256 | 0 | nsresult rv = DrawWithXlib(target, |
257 | 0 | offset, rectangles, |
258 | 0 | needs_clip ? rect_count : 0); |
259 | 0 | if (NS_SUCCEEDED(rv)) { |
260 | 0 | cairo_surface_mark_dirty (target); |
261 | 0 | return true; |
262 | 0 | } |
263 | 0 | return false; |
264 | 0 | } |
265 | | |
266 | | static bool |
267 | 0 | VisualHasAlpha(Screen *screen, Visual *visual) { |
268 | 0 | // There may be some other visuals format with alpha but usually this is |
269 | 0 | // the only one we care about. |
270 | 0 | return visual->c_class == TrueColor && |
271 | 0 | visual->bits_per_rgb == 8 && |
272 | 0 | visual->red_mask == 0xff0000 && |
273 | 0 | visual->green_mask == 0xff00 && |
274 | 0 | visual->blue_mask == 0xff && |
275 | 0 | gfxXlibSurface::DepthOfVisual(screen, visual) == 32; |
276 | 0 | } |
277 | | |
278 | | // Returns whether pixel conversion between visual and format is exact (in |
279 | | // both directions). |
280 | | static bool |
281 | 0 | FormatConversionIsExact(Screen *screen, Visual *visual, XRenderPictFormat *format) { |
282 | 0 | if (!format || |
283 | 0 | visual->c_class != TrueColor || |
284 | 0 | format->type != PictTypeDirect || |
285 | 0 | gfxXlibSurface::DepthOfVisual(screen, visual) != format->depth) |
286 | 0 | return false; |
287 | 0 | |
288 | 0 | XRenderPictFormat *visualFormat = |
289 | 0 | XRenderFindVisualFormat(DisplayOfScreen(screen), visual); |
290 | 0 |
|
291 | 0 | if (visualFormat->type != PictTypeDirect ) |
292 | 0 | return false; |
293 | 0 | |
294 | 0 | const XRenderDirectFormat& a = visualFormat->direct; |
295 | 0 | const XRenderDirectFormat& b = format->direct; |
296 | 0 | return a.redMask == b.redMask && |
297 | 0 | a.greenMask == b.greenMask && |
298 | 0 | a.blueMask == b.blueMask; |
299 | 0 | } |
300 | | |
301 | | // The 3 non-direct strategies described above. |
302 | | // The surface format and strategy are inter-dependent. |
303 | | enum DrawingMethod { |
304 | | eSimple, |
305 | | eCopyBackground, |
306 | | eAlphaExtraction |
307 | | }; |
308 | | |
309 | | static cairo_surface_t* |
310 | | CreateTempXlibSurface (cairo_surface_t* cairoTarget, |
311 | | DrawTarget* drawTarget, |
312 | | IntSize size, |
313 | | bool canDrawOverBackground, |
314 | | uint32_t flags, Screen *screen, Visual *visual, |
315 | | DrawingMethod *method) |
316 | 0 | { |
317 | 0 | NS_ASSERTION(cairoTarget || drawTarget, "Must have some type"); |
318 | 0 |
|
319 | 0 | bool drawIsOpaque = (flags & gfxXlibNativeRenderer::DRAW_IS_OPAQUE) != 0; |
320 | 0 | bool supportsAlternateVisual = |
321 | 0 | (flags & gfxXlibNativeRenderer::DRAW_SUPPORTS_ALTERNATE_VISUAL) != 0; |
322 | 0 | bool supportsAlternateScreen = supportsAlternateVisual && |
323 | 0 | (flags & gfxXlibNativeRenderer::DRAW_SUPPORTS_ALTERNATE_SCREEN); |
324 | 0 |
|
325 | 0 | cairo_surface_type_t cairoTargetType = |
326 | 0 | cairoTarget ? cairo_surface_get_type (cairoTarget) : (cairo_surface_type_t)0xFF; |
327 | 0 |
|
328 | 0 | Screen *target_screen = cairoTargetType == CAIRO_SURFACE_TYPE_XLIB ? |
329 | 0 | cairo_xlib_surface_get_screen (cairoTarget) : screen; |
330 | 0 |
|
331 | 0 | // When the background has an alpha channel, we need to draw with an alpha |
332 | 0 | // channel anyway, so there is no need to copy the background. If |
333 | 0 | // doCopyBackground is set here, we'll also need to check below that the |
334 | 0 | // background can copied without any loss in format conversions. |
335 | 0 | bool doCopyBackground = !drawIsOpaque && canDrawOverBackground && |
336 | 0 | cairoTarget && cairo_surface_get_content (cairoTarget) == CAIRO_CONTENT_COLOR; |
337 | 0 |
|
338 | 0 | if (supportsAlternateScreen && screen != target_screen && drawIsOpaque) { |
339 | 0 | // Prefer a visual on the target screen. |
340 | 0 | // (If !drawIsOpaque, we'll need doCopyBackground or an alpha channel.) |
341 | 0 | visual = DefaultVisualOfScreen(target_screen); |
342 | 0 | screen = target_screen; |
343 | 0 |
|
344 | 0 | } else if (doCopyBackground || (supportsAlternateVisual && drawIsOpaque)) { |
345 | 0 | // Analyse the pixel formats either to check whether we can |
346 | 0 | // doCopyBackground or to see if we can find a better visual for |
347 | 0 | // opaque drawing. |
348 | 0 | Visual *target_visual = nullptr; |
349 | 0 | XRenderPictFormat *target_format = nullptr; |
350 | 0 | if (cairoTargetType == CAIRO_SURFACE_TYPE_XLIB) { |
351 | 0 | target_visual = cairo_xlib_surface_get_visual (cairoTarget); |
352 | 0 | target_format = cairo_xlib_surface_get_xrender_format (cairoTarget); |
353 | 0 | } else if (cairoTargetType == CAIRO_SURFACE_TYPE_IMAGE || drawTarget) { |
354 | 0 | gfxImageFormat imageFormat = |
355 | 0 | drawTarget ? SurfaceFormatToImageFormat(drawTarget->GetFormat()) : |
356 | 0 | CairoFormatToGfxFormat(cairo_image_surface_get_format(cairoTarget)); |
357 | 0 | target_visual = gfxXlibSurface::FindVisual(screen, imageFormat); |
358 | 0 | Display *dpy = DisplayOfScreen(screen); |
359 | 0 | if (target_visual) { |
360 | 0 | target_format = XRenderFindVisualFormat(dpy, target_visual); |
361 | 0 | } else { |
362 | 0 | target_format = |
363 | 0 | gfxXlibSurface::FindRenderFormat(dpy, imageFormat); |
364 | 0 | } |
365 | 0 | } |
366 | 0 |
|
367 | 0 | if (supportsAlternateVisual && |
368 | 0 | (supportsAlternateScreen || screen == target_screen)) { |
369 | 0 | if (target_visual) { |
370 | 0 | visual = target_visual; |
371 | 0 | screen = target_screen; |
372 | 0 | } |
373 | 0 | } |
374 | 0 | // Could try harder to match formats across screens for background |
375 | 0 | // copying when !supportsAlternateScreen, if we cared. Preferably |
376 | 0 | // we'll find a visual below with an alpha channel anyway; if so, the |
377 | 0 | // background won't need to be copied. |
378 | 0 |
|
379 | 0 | if (doCopyBackground && visual != target_visual && |
380 | 0 | !FormatConversionIsExact(screen, visual, target_format)) { |
381 | 0 | doCopyBackground = false; |
382 | 0 | } |
383 | 0 | } |
384 | 0 |
|
385 | 0 | if (supportsAlternateVisual && !drawIsOpaque && |
386 | 0 | (screen != target_screen || |
387 | 0 | !(doCopyBackground || VisualHasAlpha(screen, visual)))) { |
388 | 0 | // Try to find a visual with an alpha channel. |
389 | 0 | Screen *visualScreen = |
390 | 0 | supportsAlternateScreen ? target_screen : screen; |
391 | 0 | Visual *argbVisual = |
392 | 0 | gfxXlibSurface::FindVisual(visualScreen, |
393 | 0 | SurfaceFormat::A8R8G8B8_UINT32); |
394 | 0 | if (argbVisual) { |
395 | 0 | visual = argbVisual; |
396 | 0 | screen = visualScreen; |
397 | 0 | } else if (!doCopyBackground && |
398 | 0 | gfxXlibSurface::DepthOfVisual(screen, visual) != 24) { |
399 | 0 | // Will need to do alpha extraction; prefer a 24-bit visual. |
400 | 0 | // No advantage in using the target screen. |
401 | 0 | Visual *rgb24Visual = |
402 | 0 | gfxXlibSurface::FindVisual(screen, |
403 | 0 | SurfaceFormat::X8R8G8B8_UINT32); |
404 | 0 | if (rgb24Visual) { |
405 | 0 | visual = rgb24Visual; |
406 | 0 | } |
407 | 0 | } |
408 | 0 | } |
409 | 0 |
|
410 | 0 | Drawable drawable = |
411 | 0 | (screen == target_screen && cairoTargetType == CAIRO_SURFACE_TYPE_XLIB) ? |
412 | 0 | cairo_xlib_surface_get_drawable (cairoTarget) : RootWindowOfScreen(screen); |
413 | 0 |
|
414 | 0 | cairo_surface_t *surface = |
415 | 0 | gfxXlibSurface::CreateCairoSurface(screen, visual, |
416 | 0 | IntSize(size.width, size.height), |
417 | 0 | drawable); |
418 | 0 | if (!surface) { |
419 | 0 | return nullptr; |
420 | 0 | } |
421 | 0 | |
422 | 0 | if (drawIsOpaque || |
423 | 0 | cairo_surface_get_content(surface) == CAIRO_CONTENT_COLOR_ALPHA) { |
424 | 0 | NATIVE_DRAWING_NOTE(drawIsOpaque ? |
425 | 0 | ", SIMPLE OPAQUE\n" : ", SIMPLE WITH ALPHA"); |
426 | 0 | *method = eSimple; |
427 | 0 | } else if (doCopyBackground) { |
428 | 0 | NATIVE_DRAWING_NOTE(", COPY BACKGROUND\n"); |
429 | 0 | *method = eCopyBackground; |
430 | 0 | } else { |
431 | 0 | NATIVE_DRAWING_NOTE(", SLOW ALPHA EXTRACTION\n"); |
432 | 0 | *method = eAlphaExtraction; |
433 | 0 | } |
434 | 0 |
|
435 | 0 | return surface; |
436 | 0 | } |
437 | | |
438 | | bool |
439 | | gfxXlibNativeRenderer::DrawOntoTempSurface(cairo_surface_t *tempXlibSurface, |
440 | | IntPoint offset) |
441 | 0 | { |
442 | 0 | cairo_surface_flush(tempXlibSurface); |
443 | 0 | /* no clipping is needed because the callback can't draw outside the native |
444 | 0 | surface anyway */ |
445 | 0 | nsresult rv = DrawWithXlib(tempXlibSurface, offset, nullptr, 0); |
446 | 0 | cairo_surface_mark_dirty(tempXlibSurface); |
447 | 0 | return NS_SUCCEEDED(rv); |
448 | 0 | } |
449 | | |
450 | | static already_AddRefed<gfxImageSurface> |
451 | | CopyXlibSurfaceToImage(cairo_surface_t *tempXlibSurface, |
452 | | IntSize size, |
453 | | gfxImageFormat format) |
454 | 0 | { |
455 | 0 | RefPtr<gfxImageSurface> result = new gfxImageSurface(size, format); |
456 | 0 |
|
457 | 0 | cairo_t* copyCtx = cairo_create(result->CairoSurface()); |
458 | 0 | cairo_set_source_surface(copyCtx, tempXlibSurface, 0, 0); |
459 | 0 | cairo_set_operator(copyCtx, CAIRO_OPERATOR_SOURCE); |
460 | 0 | cairo_paint(copyCtx); |
461 | 0 | cairo_destroy(copyCtx); |
462 | 0 |
|
463 | 0 | return result.forget(); |
464 | 0 | } |
465 | | |
466 | | void |
467 | | gfxXlibNativeRenderer::Draw(gfxContext* ctx, IntSize size, |
468 | | uint32_t flags, Screen *screen, Visual *visual) |
469 | 0 | { |
470 | 0 | Matrix matrix = ctx->CurrentMatrix(); |
471 | 0 |
|
472 | 0 | // We can only draw direct or onto a copied background if pixels align and |
473 | 0 | // native drawing is compatible with the current operator. (The matrix is |
474 | 0 | // actually also pixel-exact for flips and right-angle rotations, which |
475 | 0 | // would permit copying the background but not drawing direct.) |
476 | 0 | bool matrixIsIntegerTranslation = !matrix.HasNonIntegerTranslation(); |
477 | 0 | bool canDrawOverBackground = matrixIsIntegerTranslation && |
478 | 0 | ctx->CurrentOp() == CompositionOp::OP_OVER; |
479 | 0 |
|
480 | 0 | // The padding of 0.5 for non-pixel-exact transformations used here is |
481 | 0 | // the same as what _cairo_pattern_analyze_filter uses. |
482 | 0 | const gfxFloat filterRadius = 0.5; |
483 | 0 | gfxRect affectedRect(0.0, 0.0, size.width, size.height); |
484 | 0 | if (!matrixIsIntegerTranslation) { |
485 | 0 | // The filter footprint means that the affected rectangle is a |
486 | 0 | // little larger than the drawingRect; |
487 | 0 | affectedRect.Inflate(filterRadius); |
488 | 0 |
|
489 | 0 | NATIVE_DRAWING_NOTE("FALLBACK: matrix not integer translation"); |
490 | 0 | } else if (!canDrawOverBackground) { |
491 | 0 | NATIVE_DRAWING_NOTE("FALLBACK: unsupported operator"); |
492 | 0 | } |
493 | 0 |
|
494 | 0 | DrawTarget* drawTarget = ctx->GetDrawTarget(); |
495 | 0 | if (!drawTarget) { |
496 | 0 | gfxCriticalError() << "gfxContext without a DrawTarget"; |
497 | 0 | return; |
498 | 0 | } |
499 | 0 |
|
500 | 0 | // Clipping to the region affected by drawing allows us to consider only |
501 | 0 | // the portions of the clip region that will be affected by drawing. |
502 | 0 | gfxRect clipExtents; |
503 | 0 | { |
504 | 0 | gfxContextAutoSaveRestore autoSR(ctx); |
505 | 0 | ctx->Clip(affectedRect); |
506 | 0 |
|
507 | 0 | clipExtents = ctx->GetClipExtents(); |
508 | 0 | if (clipExtents.IsEmpty()) { |
509 | 0 | return; // nothing to do |
510 | 0 | } |
511 | 0 | if (canDrawOverBackground && |
512 | 0 | DrawDirect(drawTarget, size, flags, screen, visual)) { |
513 | 0 | return; |
514 | 0 | } |
515 | 0 | } |
516 | 0 | |
517 | 0 | IntRect drawingRect(IntPoint(0, 0), size); |
518 | 0 | // Drawing need only be performed within the clip extents |
519 | 0 | // (and padding for the filter). |
520 | 0 | if (!matrixIsIntegerTranslation) { |
521 | 0 | // The source surface may need to be a little larger than the clip |
522 | 0 | // extents due to the filter footprint. |
523 | 0 | clipExtents.Inflate(filterRadius); |
524 | 0 | } |
525 | 0 | clipExtents.RoundOut(); |
526 | 0 |
|
527 | 0 | IntRect intExtents(int32_t(clipExtents.X()), |
528 | 0 | int32_t(clipExtents.Y()), |
529 | 0 | int32_t(clipExtents.Width()), |
530 | 0 | int32_t(clipExtents.Height())); |
531 | 0 | drawingRect.IntersectRect(drawingRect, intExtents); |
532 | 0 |
|
533 | 0 | gfxPoint offset(drawingRect.x, drawingRect.y); |
534 | 0 |
|
535 | 0 | DrawingMethod method; |
536 | 0 | Matrix dtTransform = drawTarget->GetTransform(); |
537 | 0 | gfxPoint deviceTranslation = gfxPoint(dtTransform._31, dtTransform._32); |
538 | 0 | cairo_t* cairo = static_cast<cairo_t*> |
539 | 0 | (drawTarget->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT)); |
540 | 0 | cairo_surface_t* cairoTarget = cairo ? cairo_get_group_target(cairo) : nullptr; |
541 | 0 | cairo_surface_t* tempXlibSurface = |
542 | 0 | CreateTempXlibSurface(cairoTarget, drawTarget, size, |
543 | 0 | canDrawOverBackground, flags, screen, visual, |
544 | 0 | &method); |
545 | 0 | if (!tempXlibSurface) { |
546 | 0 | return; |
547 | 0 | } |
548 | 0 | |
549 | 0 | bool drawIsOpaque = (flags & DRAW_IS_OPAQUE) != 0; |
550 | 0 | if (!drawIsOpaque) { |
551 | 0 | cairo_t* tmpCtx = cairo_create(tempXlibSurface); |
552 | 0 | if (method == eCopyBackground) { |
553 | 0 | NS_ASSERTION(cairoTarget, "eCopyBackground only used when there's a cairoTarget"); |
554 | 0 | cairo_set_operator(tmpCtx, CAIRO_OPERATOR_SOURCE); |
555 | 0 | gfxPoint pt = -(offset + deviceTranslation); |
556 | 0 | cairo_set_source_surface(tmpCtx, cairoTarget, pt.x, pt.y); |
557 | 0 | // The copy from the tempXlibSurface to the target context should |
558 | 0 | // use operator SOURCE, but that would need a mask to bound the |
559 | 0 | // operation. Here we only copy opaque backgrounds so operator |
560 | 0 | // OVER will behave like SOURCE masked by the surface. |
561 | 0 | NS_ASSERTION(cairo_surface_get_content(tempXlibSurface) == CAIRO_CONTENT_COLOR, |
562 | 0 | "Don't copy background with a transparent surface"); |
563 | 0 | } else { |
564 | 0 | cairo_set_operator(tmpCtx, CAIRO_OPERATOR_CLEAR); |
565 | 0 | } |
566 | 0 | cairo_paint(tmpCtx); |
567 | 0 | cairo_destroy(tmpCtx); |
568 | 0 | } |
569 | 0 |
|
570 | 0 | if (!DrawOntoTempSurface(tempXlibSurface, -drawingRect.TopLeft())) { |
571 | 0 | cairo_surface_destroy(tempXlibSurface); |
572 | 0 | return; |
573 | 0 | } |
574 | 0 | |
575 | 0 | SurfaceFormat moz2DFormat = |
576 | 0 | cairo_surface_get_content(tempXlibSurface) == CAIRO_CONTENT_COLOR ? |
577 | 0 | SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8; |
578 | 0 | if (method != eAlphaExtraction) { |
579 | 0 | RefPtr<SourceSurface> sourceSurface = |
580 | 0 | Factory::CreateSourceSurfaceForCairoSurface(tempXlibSurface, size, moz2DFormat); |
581 | 0 | if (sourceSurface) { |
582 | 0 | drawTarget->DrawSurface(sourceSurface, |
583 | 0 | Rect(offset.x, offset.y, size.width, size.height), |
584 | 0 | Rect(0, 0, size.width, size.height)); |
585 | 0 | } |
586 | 0 | cairo_surface_destroy(tempXlibSurface); |
587 | 0 | return; |
588 | 0 | } |
589 | 0 |
|
590 | 0 | RefPtr<gfxImageSurface> blackImage = |
591 | 0 | CopyXlibSurfaceToImage(tempXlibSurface, size, SurfaceFormat::A8R8G8B8_UINT32); |
592 | 0 |
|
593 | 0 | cairo_t* tmpCtx = cairo_create(tempXlibSurface); |
594 | 0 | cairo_set_source_rgba(tmpCtx, 1.0, 1.0, 1.0, 1.0); |
595 | 0 | cairo_set_operator(tmpCtx, CAIRO_OPERATOR_SOURCE); |
596 | 0 | cairo_paint(tmpCtx); |
597 | 0 | cairo_destroy(tmpCtx); |
598 | 0 | DrawOntoTempSurface(tempXlibSurface, -drawingRect.TopLeft()); |
599 | 0 | RefPtr<gfxImageSurface> whiteImage = |
600 | 0 | CopyXlibSurfaceToImage(tempXlibSurface, size, SurfaceFormat::X8R8G8B8_UINT32); |
601 | 0 |
|
602 | 0 | if (blackImage->CairoStatus() == CAIRO_STATUS_SUCCESS && |
603 | 0 | whiteImage->CairoStatus() == CAIRO_STATUS_SUCCESS) { |
604 | 0 | if (!gfxAlphaRecovery::RecoverAlpha(blackImage, whiteImage)) { |
605 | 0 | cairo_surface_destroy(tempXlibSurface); |
606 | 0 | return; |
607 | 0 | } |
608 | 0 | |
609 | 0 | gfxASurface* paintSurface = blackImage; |
610 | 0 | RefPtr<SourceSurface> sourceSurface = |
611 | 0 | Factory::CreateSourceSurfaceForCairoSurface(paintSurface->CairoSurface(), |
612 | 0 | size, moz2DFormat); |
613 | 0 | if (sourceSurface) { |
614 | 0 | drawTarget->DrawSurface(sourceSurface, |
615 | 0 | Rect(offset.x, offset.y, size.width, size.height), |
616 | 0 | Rect(0, 0, size.width, size.height)); |
617 | 0 | } |
618 | 0 | } |
619 | 0 | cairo_surface_destroy(tempXlibSurface); |
620 | 0 | } |