Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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/. */
6 #include "gfxXlibNativeRenderer.h"
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"
18 using namespace mozilla;
19 using namespace mozilla::gfx;
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
28 /* We have four basic strategies available:
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.
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.
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.
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.
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.
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.
58 Sure would be nice to have an X extension or GL to do this for us on the
59 server...
60 */
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 }
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;
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 }
89 /* the clip is always in surface backend coordinates (i.e. native backend coords) */
90 clips = cliplist->rectangles;
92 for (i = 0; i < cliplist->num_rectangles; ++i) {
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 }
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 }
111 NS_ASSERTION(bounds.Contains(rect),
112 "Was expecting to be clipped to bounds");
114 if (i >= max_rectangles) {
115 retval = false;
116 NATIVE_DRAWING_NOTE("FALLBACK: unsupported clip rectangle count");
117 goto FINISH;
118 }
120 rectangles[i] = rect;
121 }
123 *need_clip = true;
124 *num_rectangles = cliplist->num_rectangles;
126 FINISH:
127 cairo_rectangle_list_destroy (cliplist);
129 return retval;
130 }
132 #define MAX_STATIC_CLIP_RECTANGLES 50
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 }
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 }
154 bool direct = DrawCairo(borrowed.mCairo, size, flags, screen, visual);
155 borrowed.Finish();
157 return direct;
158 }
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 }
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);
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));
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 }
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)));
202 bool needs_clip = true;
203 nsIntRect rectangles[MAX_STATIC_CLIP_RECTANGLES];
204 int rect_count = 0;
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;
219 /* Stop now if everything is clipped out */
220 if (needs_clip && rect_count == 0)
221 return true;
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 }
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 }
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 }
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 }
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;
291 XRenderPictFormat *visualFormat =
292 XRenderFindVisualFormat(DisplayOfScreen(screen), visual);
294 if (visualFormat->type != PictTypeDirect )
295 return false;
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 }
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 };
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");
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);
328 cairo_surface_type_t cairoTargetType =
329 cairoTarget ? cairo_surface_get_type (cairoTarget) : (cairo_surface_type_t)0xFF;
331 Screen *target_screen = cairoTargetType == CAIRO_SURFACE_TYPE_XLIB ?
332 cairo_xlib_surface_get_screen (cairoTarget) : screen;
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;
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;
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 }
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.
382 if (doCopyBackground && visual != target_visual &&
383 !FormatConversionIsExact(screen, visual, target_format)) {
384 doCopyBackground = false;
385 }
386 }
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 }
413 Drawable drawable =
414 (screen == target_screen && cairoTargetType == CAIRO_SURFACE_TYPE_XLIB) ?
415 cairo_xlib_surface_get_drawable (cairoTarget) : RootWindowOfScreen(screen);
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 }
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 }
438 return surface;
439 }
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 }
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);
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);
466 return result.forget();
467 }
469 void
470 gfxXlibNativeRenderer::Draw(gfxContext* ctx, nsIntSize size,
471 uint32_t flags, Screen *screen, Visual *visual)
472 {
473 gfxMatrix matrix = ctx->CurrentMatrix();
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;
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);
492 NATIVE_DRAWING_NOTE("FALLBACK: matrix not integer translation");
493 } else if (!canDrawOverBackground) {
494 NATIVE_DRAWING_NOTE("FALLBACK: unsupported operator");
495 }
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);
504 clipExtents = ctx->GetClipExtents();
505 if (clipExtents.IsEmpty())
506 return; // nothing to do
508 if (canDrawOverBackground &&
509 DrawDirect(ctx, size, flags, screen, visual))
510 return;
511 }
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();
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);
529 gfxPoint offset(drawingRect.x, drawingRect.y);
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 }
546 cairo_surface_t* tempXlibSurface =
547 CreateTempXlibSurface(cairoTarget, drawTarget, size,
548 canDrawOverBackground, flags, screen, visual,
549 &method);
550 if (!tempXlibSurface)
551 return;
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 }
574 if (!DrawOntoTempSurface(tempXlibSurface, -drawingRect.TopLeft())) {
575 cairo_surface_destroy(tempXlibSurface);
576 return;
577 }
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 }
605 nsRefPtr<gfxImageSurface> blackImage =
606 CopyXlibSurfaceToImage(tempXlibSurface, size, gfxImageFormat::ARGB32);
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);
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 }
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 }