Line data Source code
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 : #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 0 : _convert_coord_to_int (double coord, int32_t *v)
65 : {
66 0 : *v = (int32_t)coord;
67 : /* XXX allow some tolerance here? */
68 0 : return *v == coord;
69 : }
70 :
71 : static bool
72 0 : _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 : {
78 : cairo_rectangle_list_t *cliplist;
79 : cairo_rectangle_t *clips;
80 : int i;
81 0 : bool retval = true;
82 :
83 0 : cliplist = cairo_copy_clip_rectangle_list (cr);
84 0 : if (cliplist->status != CAIRO_STATUS_SUCCESS) {
85 0 : retval = false;
86 : NATIVE_DRAWING_NOTE("FALLBACK: non-rectangular clip");
87 0 : goto FINISH;
88 : }
89 :
90 : /* the clip is always in surface backend coordinates (i.e. native backend coords) */
91 0 : clips = cliplist->rectangles;
92 :
93 0 : for (i = 0; i < cliplist->num_rectangles; ++i) {
94 :
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 : {
101 0 : retval = false;
102 : NATIVE_DRAWING_NOTE("FALLBACK: non-integer clip");
103 0 : goto FINISH;
104 : }
105 :
106 0 : if (rect.IsEqualInterior(bounds)) {
107 : /* 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 : }
111 :
112 0 : NS_ASSERTION(bounds.Contains(rect),
113 : "Was expecting to be clipped to bounds");
114 :
115 0 : if (i >= max_rectangles) {
116 0 : retval = false;
117 : NATIVE_DRAWING_NOTE("FALLBACK: unsupported clip rectangle count");
118 0 : goto FINISH;
119 : }
120 :
121 0 : rectangles[i] = rect;
122 : }
123 :
124 0 : *need_clip = true;
125 0 : *num_rectangles = cliplist->num_rectangles;
126 :
127 : FINISH:
128 0 : cairo_rectangle_list_destroy (cliplist);
129 :
130 0 : return retval;
131 : }
132 :
133 : #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 0 : gfxXlibNativeRenderer::DrawDirect(DrawTarget* aDT, IntSize size,
141 : uint32_t flags,
142 : Screen *screen, Visual *visual)
143 : {
144 : // We need to actually borrow the context because we want to read out the
145 : // clip rectangles.
146 0 : BorrowedCairoContext borrowed(aDT);
147 0 : if (!borrowed.mCairo) {
148 0 : return false;
149 : }
150 :
151 0 : bool direct = DrawCairo(borrowed.mCairo, size, flags, screen, visual);
152 0 : borrowed.Finish();
153 :
154 0 : return direct;
155 : }
156 :
157 : bool
158 0 : gfxXlibNativeRenderer::DrawCairo(cairo_t* cr, IntSize size,
159 : uint32_t flags,
160 : Screen *screen, Visual *visual)
161 : {
162 : /* 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 : NATIVE_DRAWING_NOTE("FALLBACK: non-X surface");
166 0 : return false;
167 : }
168 :
169 : cairo_matrix_t matrix;
170 0 : cairo_get_matrix (cr, &matrix);
171 : double device_offset_x, device_offset_y;
172 0 : cairo_surface_get_device_offset (target, &device_offset_x, &device_offset_y);
173 :
174 : /* Draw() checked that the matrix contained only a very-close-to-integer
175 : translation. Here (and in several other places and thebes) device
176 : offsets are assumed to be integer. */
177 0 : NS_ASSERTION(int32_t(device_offset_x) == device_offset_x &&
178 : int32_t(device_offset_y) == device_offset_y,
179 : "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 :
183 0 : int max_rectangles = 0;
184 0 : if (flags & DRAW_SUPPORTS_CLIP_RECT) {
185 0 : max_rectangles = 1;
186 : }
187 0 : if (flags & DRAW_SUPPORTS_CLIP_LIST) {
188 0 : max_rectangles = MAX_STATIC_CLIP_RECTANGLES;
189 : }
190 :
191 : /* The client won't draw outside the surface so consider this when
192 : analysing clip rectangles. */
193 0 : IntRect bounds(offset, size);
194 : bounds.IntersectRect(bounds,
195 0 : IntRect(0, 0,
196 : cairo_xlib_surface_get_width(target),
197 0 : cairo_xlib_surface_get_height(target)));
198 :
199 0 : bool needs_clip = true;
200 0 : IntRect rectangles[MAX_STATIC_CLIP_RECTANGLES];
201 0 : int rect_count = 0;
202 :
203 : /* Check that the clip is rectangular and aligned on unit boundaries. */
204 : /* Temporarily set the matrix for _get_rectangular_clip. It's basically
205 : the identity matrix, but we must adjust for the fact that our
206 : offset-rect is in device coordinates. */
207 0 : cairo_identity_matrix (cr);
208 0 : cairo_translate (cr, -device_offset_x, -device_offset_y);
209 : bool have_rectangular_clip =
210 : _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 :
216 : /* Stop now if everything is clipped out */
217 0 : if (needs_clip && rect_count == 0)
218 0 : return true;
219 :
220 : /* Check that the screen is supported.
221 : Visuals belong to screens, so, if alternate visuals are not supported,
222 : then alternate screens cannot be supported. */
223 : 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 : NATIVE_DRAWING_NOTE("FALLBACK: non-default screen");
230 0 : return false;
231 : }
232 :
233 : /* Check that there is a visual */
234 0 : Visual *target_visual = cairo_xlib_surface_get_visual (target);
235 0 : if (!target_visual) {
236 : NATIVE_DRAWING_NOTE("FALLBACK: no Visual for surface");
237 0 : return false;
238 : }
239 : /* Check that the visual is supported */
240 0 : if (!supports_alternate_visual && target_visual != visual) {
241 : // Only the format of the visual is important (not the GLX properties)
242 : // for Xlib or XRender drawing.
243 : XRenderPictFormat *target_format =
244 0 : cairo_xlib_surface_get_xrender_format (target);
245 0 : if (!target_format ||
246 : (target_format !=
247 0 : XRenderFindVisualFormat (DisplayOfScreen(screen), visual))) {
248 : NATIVE_DRAWING_NOTE("FALLBACK: unsupported Visual");
249 0 : return false;
250 : }
251 : }
252 :
253 : /* we're good to go! */
254 : NATIVE_DRAWING_NOTE("TAKING FAST PATH\n");
255 0 : cairo_surface_flush (target);
256 0 : nsresult rv = DrawWithXlib(target,
257 : 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 : }
263 0 : return false;
264 : }
265 :
266 : static bool
267 0 : VisualHasAlpha(Screen *screen, Visual *visual) {
268 : // There may be some other visuals format with alpha but usually this is
269 : // 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 : }
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 :
288 : XRenderPictFormat *visualFormat =
289 0 : XRenderFindVisualFormat(DisplayOfScreen(screen), visual);
290 :
291 0 : if (visualFormat->type != PictTypeDirect )
292 0 : return false;
293 :
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 : }
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 0 : 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 : {
317 0 : NS_ASSERTION(cairoTarget || drawTarget, "Must have some type");
318 :
319 0 : bool drawIsOpaque = (flags & gfxXlibNativeRenderer::DRAW_IS_OPAQUE) != 0;
320 : 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 :
325 : cairo_surface_type_t cairoTargetType =
326 0 : cairoTarget ? cairo_surface_get_type (cairoTarget) : (cairo_surface_type_t)0xFF;
327 :
328 0 : Screen *target_screen = cairoTargetType == CAIRO_SURFACE_TYPE_XLIB ?
329 0 : cairo_xlib_surface_get_screen (cairoTarget) : screen;
330 :
331 : // When the background has an alpha channel, we need to draw with an alpha
332 : // channel anyway, so there is no need to copy the background. If
333 : // doCopyBackground is set here, we'll also need to check below that the
334 : // 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 :
338 0 : if (supportsAlternateScreen && screen != target_screen && drawIsOpaque) {
339 : // Prefer a visual on the target screen.
340 : // (If !drawIsOpaque, we'll need doCopyBackground or an alpha channel.)
341 0 : visual = DefaultVisualOfScreen(target_screen);
342 0 : screen = target_screen;
343 :
344 0 : } else if (doCopyBackground || (supportsAlternateVisual && drawIsOpaque)) {
345 : // Analyse the pixel formats either to check whether we can
346 : // doCopyBackground or to see if we can find a better visual for
347 : // 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 : 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 : } else {
362 : target_format =
363 0 : gfxXlibSurface::FindRenderFormat(dpy, imageFormat);
364 : }
365 : }
366 :
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 : }
373 : }
374 : // Could try harder to match formats across screens for background
375 : // copying when !supportsAlternateScreen, if we cared. Preferably
376 : // we'll find a visual below with an alpha channel anyway; if so, the
377 : // background won't need to be copied.
378 :
379 0 : if (doCopyBackground && visual != target_visual &&
380 0 : !FormatConversionIsExact(screen, visual, target_format)) {
381 0 : doCopyBackground = false;
382 : }
383 : }
384 :
385 0 : if (supportsAlternateVisual && !drawIsOpaque &&
386 0 : (screen != target_screen ||
387 0 : !(doCopyBackground || VisualHasAlpha(screen, visual)))) {
388 : // Try to find a visual with an alpha channel.
389 : Screen *visualScreen =
390 0 : supportsAlternateScreen ? target_screen : screen;
391 : Visual *argbVisual =
392 : 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 : // Will need to do alpha extraction; prefer a 24-bit visual.
400 : // No advantage in using the target screen.
401 : Visual *rgb24Visual =
402 : gfxXlibSurface::FindVisual(screen,
403 0 : SurfaceFormat::X8R8G8B8_UINT32);
404 0 : if (rgb24Visual) {
405 0 : visual = rgb24Visual;
406 : }
407 : }
408 : }
409 :
410 : Drawable drawable =
411 0 : (screen == target_screen && cairoTargetType == CAIRO_SURFACE_TYPE_XLIB) ?
412 0 : cairo_xlib_surface_get_drawable (cairoTarget) : RootWindowOfScreen(screen);
413 :
414 : cairo_surface_t *surface =
415 : gfxXlibSurface::CreateCairoSurface(screen, visual,
416 0 : IntSize(size.width, size.height),
417 0 : drawable);
418 0 : if (!surface) {
419 0 : return nullptr;
420 : }
421 :
422 0 : if (drawIsOpaque ||
423 0 : cairo_surface_get_content(surface) == CAIRO_CONTENT_COLOR_ALPHA) {
424 : NATIVE_DRAWING_NOTE(drawIsOpaque ?
425 : ", SIMPLE OPAQUE\n" : ", SIMPLE WITH ALPHA");
426 0 : *method = eSimple;
427 0 : } else if (doCopyBackground) {
428 : NATIVE_DRAWING_NOTE(", COPY BACKGROUND\n");
429 0 : *method = eCopyBackground;
430 : } else {
431 : NATIVE_DRAWING_NOTE(", SLOW ALPHA EXTRACTION\n");
432 0 : *method = eAlphaExtraction;
433 : }
434 :
435 0 : return surface;
436 : }
437 :
438 : bool
439 0 : gfxXlibNativeRenderer::DrawOntoTempSurface(cairo_surface_t *tempXlibSurface,
440 : IntPoint offset)
441 : {
442 0 : cairo_surface_flush(tempXlibSurface);
443 : /* no clipping is needed because the callback can't draw outside the native
444 : 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 : }
449 :
450 : static already_AddRefed<gfxImageSurface>
451 0 : CopyXlibSurfaceToImage(cairo_surface_t *tempXlibSurface,
452 : IntSize size,
453 : gfxImageFormat format)
454 : {
455 0 : RefPtr<gfxImageSurface> result = new gfxImageSurface(size, format);
456 :
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 :
463 0 : return result.forget();
464 : }
465 :
466 : void
467 0 : gfxXlibNativeRenderer::Draw(gfxContext* ctx, IntSize size,
468 : uint32_t flags, Screen *screen, Visual *visual)
469 : {
470 0 : gfxMatrix matrix = ctx->CurrentMatrix();
471 :
472 : // We can only draw direct or onto a copied background if pixels align and
473 : // native drawing is compatible with the current operator. (The matrix is
474 : // actually also pixel-exact for flips and right-angle rotations, which
475 : // 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 :
480 : // The padding of 0.5 for non-pixel-exact transformations used here is
481 : // 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 : // The filter footprint means that the affected rectangle is a
486 : // little larger than the drawingRect;
487 0 : affectedRect.Inflate(filterRadius);
488 :
489 : NATIVE_DRAWING_NOTE("FALLBACK: matrix not integer translation");
490 0 : } else if (!canDrawOverBackground) {
491 : NATIVE_DRAWING_NOTE("FALLBACK: unsupported operator");
492 : }
493 :
494 0 : DrawTarget* drawTarget = ctx->GetDrawTarget();
495 0 : if (!drawTarget) {
496 0 : gfxCriticalError() << "gfxContext without a DrawTarget";
497 0 : return;
498 : }
499 :
500 : // Clipping to the region affected by drawing allows us to consider only
501 : // the portions of the clip region that will be affected by drawing.
502 0 : gfxRect clipExtents;
503 : {
504 0 : gfxContextAutoSaveRestore autoSR(ctx);
505 0 : ctx->Clip(affectedRect);
506 :
507 0 : clipExtents = ctx->GetClipExtents();
508 0 : if (clipExtents.IsEmpty()) {
509 0 : return; // nothing to do
510 : }
511 0 : if (canDrawOverBackground &&
512 0 : DrawDirect(drawTarget, size, flags, screen, visual)) {
513 0 : return;
514 : }
515 : }
516 :
517 0 : IntRect drawingRect(IntPoint(0, 0), size);
518 : // Drawing need only be performed within the clip extents
519 : // (and padding for the filter).
520 0 : if (!matrixIsIntegerTranslation) {
521 : // The source surface may need to be a little larger than the clip
522 : // extents due to the filter footprint.
523 0 : clipExtents.Inflate(filterRadius);
524 : }
525 0 : clipExtents.RoundOut();
526 :
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 :
533 0 : gfxPoint offset(drawingRect.x, drawingRect.y);
534 :
535 : DrawingMethod method;
536 0 : Matrix dtTransform = drawTarget->GetTransform();
537 0 : gfxPoint deviceTranslation = gfxPoint(dtTransform._31, dtTransform._32);
538 : 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 : cairo_surface_t* tempXlibSurface =
542 0 : CreateTempXlibSurface(cairoTarget, drawTarget, size,
543 : canDrawOverBackground, flags, screen, visual,
544 0 : &method);
545 0 : if (!tempXlibSurface) {
546 0 : return;
547 : }
548 :
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 : // The copy from the tempXlibSurface to the target context should
558 : // use operator SOURCE, but that would need a mask to bound the
559 : // operation. Here we only copy opaque backgrounds so operator
560 : // OVER will behave like SOURCE masked by the surface.
561 0 : NS_ASSERTION(cairo_surface_get_content(tempXlibSurface) == CAIRO_CONTENT_COLOR,
562 : "Don't copy background with a transparent surface");
563 : } else {
564 0 : cairo_set_operator(tmpCtx, CAIRO_OPERATOR_CLEAR);
565 : }
566 0 : cairo_paint(tmpCtx);
567 0 : cairo_destroy(tmpCtx);
568 : }
569 :
570 0 : if (!DrawOntoTempSurface(tempXlibSurface, -drawingRect.TopLeft())) {
571 0 : cairo_surface_destroy(tempXlibSurface);
572 0 : return;
573 : }
574 :
575 : SurfaceFormat moz2DFormat =
576 0 : cairo_surface_get_content(tempXlibSurface) == CAIRO_CONTENT_COLOR ?
577 0 : SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8;
578 0 : if (method != eAlphaExtraction) {
579 : 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 : }
586 0 : cairo_surface_destroy(tempXlibSurface);
587 0 : return;
588 : }
589 :
590 : RefPtr<gfxImageSurface> blackImage =
591 0 : CopyXlibSurfaceToImage(tempXlibSurface, size, SurfaceFormat::A8R8G8B8_UINT32);
592 :
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 : RefPtr<gfxImageSurface> whiteImage =
600 0 : CopyXlibSurfaceToImage(tempXlibSurface, size, SurfaceFormat::X8R8G8B8_UINT32);
601 :
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 : }
608 :
609 0 : gfxASurface* paintSurface = blackImage;
610 : 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 : }
618 : }
619 0 : cairo_surface_destroy(tempXlibSurface);
620 : }
|