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