1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/thebes/gfxXlibNativeRenderer.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,644 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "gfxXlibNativeRenderer.h" 1.10 + 1.11 +#include "gfxXlibSurface.h" 1.12 +#include "gfxImageSurface.h" 1.13 +#include "gfxContext.h" 1.14 +#include "gfxPlatform.h" 1.15 +#include "gfxAlphaRecovery.h" 1.16 +#include "cairo-xlib.h" 1.17 +#include "cairo-xlib-xrender.h" 1.18 +#include "mozilla/gfx/BorrowedContext.h" 1.19 +#include "gfx2DGlue.h" 1.20 + 1.21 +using namespace mozilla; 1.22 +using namespace mozilla::gfx; 1.23 + 1.24 +#if 0 1.25 +#include <stdio.h> 1.26 +#define NATIVE_DRAWING_NOTE(m) fprintf(stderr, m) 1.27 +#else 1.28 +#define NATIVE_DRAWING_NOTE(m) do {} while (0) 1.29 +#endif 1.30 + 1.31 +/* We have four basic strategies available: 1.32 + 1.33 + 1) 'direct': If the target is an xlib surface, and other conditions are met, 1.34 + we can pass the underlying drawable directly to the callback. 1.35 + 1.36 + 2) 'simple': If the drawing is opaque, or we can draw to a surface with an 1.37 + alpha channel, then we can create a temporary xlib surface, pass its 1.38 + underlying drawable to the callback, and composite the result using 1.39 + cairo. 1.40 + 1.41 + 3) 'copy-background': If the drawing is not opaque but the target is 1.42 + opaque, and we can draw to a surface with format such that pixel 1.43 + conversion to and from the target format is exact, we can create a 1.44 + temporary xlib surface, copy the background from the target, pass the 1.45 + underlying drawable to the callback, and copy back to the target. 1.46 + 1.47 + This strategy is not used if the pixel format conversion is not exact, 1.48 + because that would mean that drawing intended to be very transparent 1.49 + messes with other content. 1.50 + 1.51 + The strategy is prefered over simple for non-opaque drawing and opaque 1.52 + targets on the same screen as compositing without alpha is a simpler 1.53 + operation. 1.54 + 1.55 + 4) 'alpha-extraction': create a temporary xlib surface, fill with black, 1.56 + pass its underlying drawable to the callback, copy the results to a 1.57 + cairo image surface, repeat with a white background, update the on-black 1.58 + image alpha values by comparing the two images, then paint the on-black 1.59 + image using cairo. 1.60 + 1.61 + Sure would be nice to have an X extension or GL to do this for us on the 1.62 + server... 1.63 +*/ 1.64 + 1.65 +static cairo_bool_t 1.66 +_convert_coord_to_int (double coord, int32_t *v) 1.67 +{ 1.68 + *v = (int32_t)coord; 1.69 + /* XXX allow some tolerance here? */ 1.70 + return *v == coord; 1.71 +} 1.72 + 1.73 +static bool 1.74 +_get_rectangular_clip (cairo_t *cr, 1.75 + const nsIntRect& bounds, 1.76 + bool *need_clip, 1.77 + nsIntRect *rectangles, int max_rectangles, 1.78 + int *num_rectangles) 1.79 +{ 1.80 + cairo_rectangle_list_t *cliplist; 1.81 + cairo_rectangle_t *clips; 1.82 + int i; 1.83 + bool retval = true; 1.84 + 1.85 + cliplist = cairo_copy_clip_rectangle_list (cr); 1.86 + if (cliplist->status != CAIRO_STATUS_SUCCESS) { 1.87 + retval = false; 1.88 + NATIVE_DRAWING_NOTE("FALLBACK: non-rectangular clip"); 1.89 + goto FINISH; 1.90 + } 1.91 + 1.92 + /* the clip is always in surface backend coordinates (i.e. native backend coords) */ 1.93 + clips = cliplist->rectangles; 1.94 + 1.95 + for (i = 0; i < cliplist->num_rectangles; ++i) { 1.96 + 1.97 + nsIntRect rect; 1.98 + if (!_convert_coord_to_int (clips[i].x, &rect.x) || 1.99 + !_convert_coord_to_int (clips[i].y, &rect.y) || 1.100 + !_convert_coord_to_int (clips[i].width, &rect.width) || 1.101 + !_convert_coord_to_int (clips[i].height, &rect.height)) 1.102 + { 1.103 + retval = false; 1.104 + NATIVE_DRAWING_NOTE("FALLBACK: non-integer clip"); 1.105 + goto FINISH; 1.106 + } 1.107 + 1.108 + if (rect.IsEqualInterior(bounds)) { 1.109 + /* the bounds are entirely inside the clip region so we don't need to clip. */ 1.110 + *need_clip = false; 1.111 + goto FINISH; 1.112 + } 1.113 + 1.114 + NS_ASSERTION(bounds.Contains(rect), 1.115 + "Was expecting to be clipped to bounds"); 1.116 + 1.117 + if (i >= max_rectangles) { 1.118 + retval = false; 1.119 + NATIVE_DRAWING_NOTE("FALLBACK: unsupported clip rectangle count"); 1.120 + goto FINISH; 1.121 + } 1.122 + 1.123 + rectangles[i] = rect; 1.124 + } 1.125 + 1.126 + *need_clip = true; 1.127 + *num_rectangles = cliplist->num_rectangles; 1.128 + 1.129 +FINISH: 1.130 + cairo_rectangle_list_destroy (cliplist); 1.131 + 1.132 + return retval; 1.133 +} 1.134 + 1.135 +#define MAX_STATIC_CLIP_RECTANGLES 50 1.136 + 1.137 +/** 1.138 + * Try the direct path. 1.139 + * @return True if we took the direct path 1.140 + */ 1.141 +bool 1.142 +gfxXlibNativeRenderer::DrawDirect(gfxContext *ctx, nsIntSize size, 1.143 + uint32_t flags, 1.144 + Screen *screen, Visual *visual) 1.145 +{ 1.146 + if (ctx->IsCairo()) { 1.147 + return DrawCairo(ctx->GetCairo(), size, flags, screen, visual); 1.148 + } 1.149 + 1.150 + // We need to actually borrow the context because we want to read out the 1.151 + // clip rectangles. 1.152 + BorrowedCairoContext borrowed(ctx->GetDrawTarget()); 1.153 + if (!borrowed.mCairo) { 1.154 + return false; 1.155 + } 1.156 + 1.157 + bool direct = DrawCairo(borrowed.mCairo, size, flags, screen, visual); 1.158 + borrowed.Finish(); 1.159 + 1.160 + return direct; 1.161 +} 1.162 + 1.163 +bool 1.164 +gfxXlibNativeRenderer::DrawCairo(cairo_t* cr, nsIntSize size, 1.165 + uint32_t flags, 1.166 + Screen *screen, Visual *visual) 1.167 +{ 1.168 + /* Check that the target surface is an xlib surface. */ 1.169 + cairo_surface_t *target = cairo_get_group_target (cr); 1.170 + if (cairo_surface_get_type (target) != CAIRO_SURFACE_TYPE_XLIB) { 1.171 + NATIVE_DRAWING_NOTE("FALLBACK: non-X surface"); 1.172 + return false; 1.173 + } 1.174 + 1.175 + cairo_matrix_t matrix; 1.176 + cairo_get_matrix (cr, &matrix); 1.177 + double device_offset_x, device_offset_y; 1.178 + cairo_surface_get_device_offset (target, &device_offset_x, &device_offset_y); 1.179 + 1.180 + /* Draw() checked that the matrix contained only a very-close-to-integer 1.181 + translation. Here (and in several other places and thebes) device 1.182 + offsets are assumed to be integer. */ 1.183 + NS_ASSERTION(int32_t(device_offset_x) == device_offset_x && 1.184 + int32_t(device_offset_y) == device_offset_y, 1.185 + "Expected integer device offsets"); 1.186 + nsIntPoint offset(NS_lroundf(matrix.x0 + device_offset_x), 1.187 + NS_lroundf(matrix.y0 + device_offset_y)); 1.188 + 1.189 + int max_rectangles = 0; 1.190 + if (flags & DRAW_SUPPORTS_CLIP_RECT) { 1.191 + max_rectangles = 1; 1.192 + } 1.193 + if (flags & DRAW_SUPPORTS_CLIP_LIST) { 1.194 + max_rectangles = MAX_STATIC_CLIP_RECTANGLES; 1.195 + } 1.196 + 1.197 + /* The client won't draw outside the surface so consider this when 1.198 + analysing clip rectangles. */ 1.199 + nsIntRect bounds(offset, size); 1.200 + bounds.IntersectRect(bounds, 1.201 + nsIntRect(0, 0, 1.202 + cairo_xlib_surface_get_width(target), 1.203 + cairo_xlib_surface_get_height(target))); 1.204 + 1.205 + bool needs_clip = true; 1.206 + nsIntRect rectangles[MAX_STATIC_CLIP_RECTANGLES]; 1.207 + int rect_count = 0; 1.208 + 1.209 + /* Check that the clip is rectangular and aligned on unit boundaries. */ 1.210 + /* Temporarily set the matrix for _get_rectangular_clip. It's basically 1.211 + the identity matrix, but we must adjust for the fact that our 1.212 + offset-rect is in device coordinates. */ 1.213 + cairo_identity_matrix (cr); 1.214 + cairo_translate (cr, -device_offset_x, -device_offset_y); 1.215 + bool have_rectangular_clip = 1.216 + _get_rectangular_clip (cr, bounds, &needs_clip, 1.217 + rectangles, max_rectangles, &rect_count); 1.218 + cairo_set_matrix (cr, &matrix); 1.219 + if (!have_rectangular_clip) 1.220 + return false; 1.221 + 1.222 + /* Stop now if everything is clipped out */ 1.223 + if (needs_clip && rect_count == 0) 1.224 + return true; 1.225 + 1.226 + /* Check that the screen is supported. 1.227 + Visuals belong to screens, so, if alternate visuals are not supported, 1.228 + then alternate screens cannot be supported. */ 1.229 + bool supports_alternate_visual = 1.230 + (flags & DRAW_SUPPORTS_ALTERNATE_VISUAL) != 0; 1.231 + bool supports_alternate_screen = supports_alternate_visual && 1.232 + (flags & DRAW_SUPPORTS_ALTERNATE_SCREEN); 1.233 + if (!supports_alternate_screen && 1.234 + cairo_xlib_surface_get_screen (target) != screen) { 1.235 + NATIVE_DRAWING_NOTE("FALLBACK: non-default screen"); 1.236 + return false; 1.237 + } 1.238 + 1.239 + /* Check that there is a visual */ 1.240 + Visual *target_visual = cairo_xlib_surface_get_visual (target); 1.241 + if (!target_visual) { 1.242 + NATIVE_DRAWING_NOTE("FALLBACK: no Visual for surface"); 1.243 + return false; 1.244 + } 1.245 + /* Check that the visual is supported */ 1.246 + if (!supports_alternate_visual && target_visual != visual) { 1.247 + // Only the format of the visual is important (not the GLX properties) 1.248 + // for Xlib or XRender drawing. 1.249 + XRenderPictFormat *target_format = 1.250 + cairo_xlib_surface_get_xrender_format (target); 1.251 + if (!target_format || 1.252 + (target_format != 1.253 + XRenderFindVisualFormat (DisplayOfScreen(screen), visual))) { 1.254 + NATIVE_DRAWING_NOTE("FALLBACK: unsupported Visual"); 1.255 + return false; 1.256 + } 1.257 + } 1.258 + 1.259 + /* we're good to go! */ 1.260 + NATIVE_DRAWING_NOTE("TAKING FAST PATH\n"); 1.261 + cairo_surface_flush (target); 1.262 + nsresult rv = DrawWithXlib(target, 1.263 + offset, rectangles, 1.264 + needs_clip ? rect_count : 0); 1.265 + if (NS_SUCCEEDED(rv)) { 1.266 + cairo_surface_mark_dirty (target); 1.267 + return true; 1.268 + } 1.269 + return false; 1.270 +} 1.271 + 1.272 +static bool 1.273 +VisualHasAlpha(Screen *screen, Visual *visual) { 1.274 + // There may be some other visuals format with alpha but usually this is 1.275 + // the only one we care about. 1.276 + return visual->c_class == TrueColor && 1.277 + visual->bits_per_rgb == 8 && 1.278 + visual->red_mask == 0xff0000 && 1.279 + visual->green_mask == 0xff00 && 1.280 + visual->blue_mask == 0xff && 1.281 + gfxXlibSurface::DepthOfVisual(screen, visual) == 32; 1.282 +} 1.283 + 1.284 +// Returns whether pixel conversion between visual and format is exact (in 1.285 +// both directions). 1.286 +static bool 1.287 +FormatConversionIsExact(Screen *screen, Visual *visual, XRenderPictFormat *format) { 1.288 + if (!format || 1.289 + visual->c_class != TrueColor || 1.290 + format->type != PictTypeDirect || 1.291 + gfxXlibSurface::DepthOfVisual(screen, visual) != format->depth) 1.292 + return false; 1.293 + 1.294 + XRenderPictFormat *visualFormat = 1.295 + XRenderFindVisualFormat(DisplayOfScreen(screen), visual); 1.296 + 1.297 + if (visualFormat->type != PictTypeDirect ) 1.298 + return false; 1.299 + 1.300 + const XRenderDirectFormat& a = visualFormat->direct; 1.301 + const XRenderDirectFormat& b = format->direct; 1.302 + return a.redMask == b.redMask && 1.303 + a.greenMask == b.greenMask && 1.304 + a.blueMask == b.blueMask; 1.305 +} 1.306 + 1.307 +// The 3 non-direct strategies described above. 1.308 +// The surface format and strategy are inter-dependent. 1.309 +enum DrawingMethod { 1.310 + eSimple, 1.311 + eCopyBackground, 1.312 + eAlphaExtraction 1.313 +}; 1.314 + 1.315 +static cairo_surface_t* 1.316 +CreateTempXlibSurface (cairo_surface_t* cairoTarget, 1.317 + DrawTarget* drawTarget, 1.318 + nsIntSize size, 1.319 + bool canDrawOverBackground, 1.320 + uint32_t flags, Screen *screen, Visual *visual, 1.321 + DrawingMethod *method) 1.322 +{ 1.323 + NS_ASSERTION(cairoTarget || drawTarget, "Must have some type"); 1.324 + 1.325 + bool drawIsOpaque = (flags & gfxXlibNativeRenderer::DRAW_IS_OPAQUE) != 0; 1.326 + bool supportsAlternateVisual = 1.327 + (flags & gfxXlibNativeRenderer::DRAW_SUPPORTS_ALTERNATE_VISUAL) != 0; 1.328 + bool supportsAlternateScreen = supportsAlternateVisual && 1.329 + (flags & gfxXlibNativeRenderer::DRAW_SUPPORTS_ALTERNATE_SCREEN); 1.330 + 1.331 + cairo_surface_type_t cairoTargetType = 1.332 + cairoTarget ? cairo_surface_get_type (cairoTarget) : (cairo_surface_type_t)0xFF; 1.333 + 1.334 + Screen *target_screen = cairoTargetType == CAIRO_SURFACE_TYPE_XLIB ? 1.335 + cairo_xlib_surface_get_screen (cairoTarget) : screen; 1.336 + 1.337 + // When the background has an alpha channel, we need to draw with an alpha 1.338 + // channel anyway, so there is no need to copy the background. If 1.339 + // doCopyBackground is set here, we'll also need to check below that the 1.340 + // background can copied without any loss in format conversions. 1.341 + bool doCopyBackground = !drawIsOpaque && canDrawOverBackground && 1.342 + cairoTarget && cairo_surface_get_content (cairoTarget) == CAIRO_CONTENT_COLOR; 1.343 + 1.344 + if (supportsAlternateScreen && screen != target_screen && drawIsOpaque) { 1.345 + // Prefer a visual on the target screen. 1.346 + // (If !drawIsOpaque, we'll need doCopyBackground or an alpha channel.) 1.347 + visual = DefaultVisualOfScreen(target_screen); 1.348 + screen = target_screen; 1.349 + 1.350 + } else if (doCopyBackground || (supportsAlternateVisual && drawIsOpaque)) { 1.351 + // Analyse the pixel formats either to check whether we can 1.352 + // doCopyBackground or to see if we can find a better visual for 1.353 + // opaque drawing. 1.354 + Visual *target_visual = nullptr; 1.355 + XRenderPictFormat *target_format = nullptr; 1.356 + if (cairoTargetType == CAIRO_SURFACE_TYPE_XLIB) { 1.357 + target_visual = cairo_xlib_surface_get_visual (cairoTarget); 1.358 + target_format = cairo_xlib_surface_get_xrender_format (cairoTarget); 1.359 + } else if (cairoTargetType == CAIRO_SURFACE_TYPE_IMAGE || drawTarget) { 1.360 + gfxImageFormat imageFormat = 1.361 + drawTarget ? SurfaceFormatToImageFormat(drawTarget->GetFormat()) : 1.362 + (gfxImageFormat)cairo_image_surface_get_format(cairoTarget); 1.363 + target_visual = gfxXlibSurface::FindVisual(screen, imageFormat); 1.364 + Display *dpy = DisplayOfScreen(screen); 1.365 + if (target_visual) { 1.366 + target_format = XRenderFindVisualFormat(dpy, target_visual); 1.367 + } else { 1.368 + target_format = 1.369 + gfxXlibSurface::FindRenderFormat(dpy, imageFormat); 1.370 + } 1.371 + } 1.372 + 1.373 + if (supportsAlternateVisual && 1.374 + (supportsAlternateScreen || screen == target_screen)) { 1.375 + if (target_visual) { 1.376 + visual = target_visual; 1.377 + screen = target_screen; 1.378 + } 1.379 + } 1.380 + // Could try harder to match formats across screens for background 1.381 + // copying when !supportsAlternateScreen, if we cared. Preferably 1.382 + // we'll find a visual below with an alpha channel anyway; if so, the 1.383 + // background won't need to be copied. 1.384 + 1.385 + if (doCopyBackground && visual != target_visual && 1.386 + !FormatConversionIsExact(screen, visual, target_format)) { 1.387 + doCopyBackground = false; 1.388 + } 1.389 + } 1.390 + 1.391 + if (supportsAlternateVisual && !drawIsOpaque && 1.392 + (screen != target_screen || 1.393 + !(doCopyBackground || VisualHasAlpha(screen, visual)))) { 1.394 + // Try to find a visual with an alpha channel. 1.395 + Screen *visualScreen = 1.396 + supportsAlternateScreen ? target_screen : screen; 1.397 + Visual *argbVisual = 1.398 + gfxXlibSurface::FindVisual(visualScreen, 1.399 + gfxImageFormat::ARGB32); 1.400 + if (argbVisual) { 1.401 + visual = argbVisual; 1.402 + screen = visualScreen; 1.403 + } else if (!doCopyBackground && 1.404 + gfxXlibSurface::DepthOfVisual(screen, visual) != 24) { 1.405 + // Will need to do alpha extraction; prefer a 24-bit visual. 1.406 + // No advantage in using the target screen. 1.407 + Visual *rgb24Visual = 1.408 + gfxXlibSurface::FindVisual(screen, 1.409 + gfxImageFormat::RGB24); 1.410 + if (rgb24Visual) { 1.411 + visual = rgb24Visual; 1.412 + } 1.413 + } 1.414 + } 1.415 + 1.416 + Drawable drawable = 1.417 + (screen == target_screen && cairoTargetType == CAIRO_SURFACE_TYPE_XLIB) ? 1.418 + cairo_xlib_surface_get_drawable (cairoTarget) : RootWindowOfScreen(screen); 1.419 + 1.420 + cairo_surface_t *surface = 1.421 + gfxXlibSurface::CreateCairoSurface(screen, visual, 1.422 + gfxIntSize(size.width, size.height), 1.423 + drawable); 1.424 + if (!surface) { 1.425 + return nullptr; 1.426 + } 1.427 + 1.428 + if (drawIsOpaque || 1.429 + cairo_surface_get_content(surface) == CAIRO_CONTENT_COLOR_ALPHA) { 1.430 + NATIVE_DRAWING_NOTE(drawIsOpaque ? 1.431 + ", SIMPLE OPAQUE\n" : ", SIMPLE WITH ALPHA"); 1.432 + *method = eSimple; 1.433 + } else if (doCopyBackground) { 1.434 + NATIVE_DRAWING_NOTE(", COPY BACKGROUND\n"); 1.435 + *method = eCopyBackground; 1.436 + } else { 1.437 + NATIVE_DRAWING_NOTE(", SLOW ALPHA EXTRACTION\n"); 1.438 + *method = eAlphaExtraction; 1.439 + } 1.440 + 1.441 + return surface; 1.442 +} 1.443 + 1.444 +bool 1.445 +gfxXlibNativeRenderer::DrawOntoTempSurface(cairo_surface_t *tempXlibSurface, 1.446 + nsIntPoint offset) 1.447 +{ 1.448 + cairo_surface_flush(tempXlibSurface); 1.449 + /* no clipping is needed because the callback can't draw outside the native 1.450 + surface anyway */ 1.451 + nsresult rv = DrawWithXlib(tempXlibSurface, offset, nullptr, 0); 1.452 + cairo_surface_mark_dirty(tempXlibSurface); 1.453 + return NS_SUCCEEDED(rv); 1.454 +} 1.455 + 1.456 +static already_AddRefed<gfxImageSurface> 1.457 +CopyXlibSurfaceToImage(cairo_surface_t *tempXlibSurface, 1.458 + gfxIntSize size, 1.459 + gfxImageFormat format) 1.460 +{ 1.461 + nsRefPtr<gfxImageSurface> result = new gfxImageSurface(size, format); 1.462 + 1.463 + cairo_t* copyCtx = cairo_create(result->CairoSurface()); 1.464 + cairo_set_source_surface(copyCtx, tempXlibSurface, 0, 0); 1.465 + cairo_set_operator(copyCtx, CAIRO_OPERATOR_SOURCE); 1.466 + cairo_paint(copyCtx); 1.467 + cairo_destroy(copyCtx); 1.468 + 1.469 + return result.forget(); 1.470 +} 1.471 + 1.472 +void 1.473 +gfxXlibNativeRenderer::Draw(gfxContext* ctx, nsIntSize size, 1.474 + uint32_t flags, Screen *screen, Visual *visual) 1.475 +{ 1.476 + gfxMatrix matrix = ctx->CurrentMatrix(); 1.477 + 1.478 + // We can only draw direct or onto a copied background if pixels align and 1.479 + // native drawing is compatible with the current operator. (The matrix is 1.480 + // actually also pixel-exact for flips and right-angle rotations, which 1.481 + // would permit copying the background but not drawing direct.) 1.482 + bool matrixIsIntegerTranslation = !matrix.HasNonIntegerTranslation(); 1.483 + bool canDrawOverBackground = matrixIsIntegerTranslation && 1.484 + ctx->CurrentOperator() == gfxContext::OPERATOR_OVER; 1.485 + 1.486 + // The padding of 0.5 for non-pixel-exact transformations used here is 1.487 + // the same as what _cairo_pattern_analyze_filter uses. 1.488 + const gfxFloat filterRadius = 0.5; 1.489 + gfxRect affectedRect(0.0, 0.0, size.width, size.height); 1.490 + if (!matrixIsIntegerTranslation) { 1.491 + // The filter footprint means that the affected rectangle is a 1.492 + // little larger than the drawingRect; 1.493 + affectedRect.Inflate(filterRadius); 1.494 + 1.495 + NATIVE_DRAWING_NOTE("FALLBACK: matrix not integer translation"); 1.496 + } else if (!canDrawOverBackground) { 1.497 + NATIVE_DRAWING_NOTE("FALLBACK: unsupported operator"); 1.498 + } 1.499 + 1.500 + // Clipping to the region affected by drawing allows us to consider only 1.501 + // the portions of the clip region that will be affected by drawing. 1.502 + gfxRect clipExtents; 1.503 + { 1.504 + gfxContextAutoSaveRestore autoSR(ctx); 1.505 + ctx->Clip(affectedRect); 1.506 + 1.507 + clipExtents = ctx->GetClipExtents(); 1.508 + if (clipExtents.IsEmpty()) 1.509 + return; // nothing to do 1.510 + 1.511 + if (canDrawOverBackground && 1.512 + DrawDirect(ctx, size, flags, screen, visual)) 1.513 + return; 1.514 + } 1.515 + 1.516 + nsIntRect drawingRect(nsIntPoint(0, 0), size); 1.517 + // Drawing need only be performed within the clip extents 1.518 + // (and padding for the filter). 1.519 + if (!matrixIsIntegerTranslation) { 1.520 + // The source surface may need to be a little larger than the clip 1.521 + // extents due to the filter footprint. 1.522 + clipExtents.Inflate(filterRadius); 1.523 + } 1.524 + clipExtents.RoundOut(); 1.525 + 1.526 + nsIntRect intExtents(int32_t(clipExtents.X()), 1.527 + int32_t(clipExtents.Y()), 1.528 + int32_t(clipExtents.Width()), 1.529 + int32_t(clipExtents.Height())); 1.530 + drawingRect.IntersectRect(drawingRect, intExtents); 1.531 + 1.532 + gfxPoint offset(drawingRect.x, drawingRect.y); 1.533 + 1.534 + DrawingMethod method; 1.535 + cairo_surface_t* cairoTarget = nullptr; 1.536 + DrawTarget* drawTarget = nullptr; 1.537 + gfxPoint deviceTranslation; 1.538 + if (ctx->IsCairo()) { 1.539 + cairoTarget = cairo_get_group_target(ctx->GetCairo()); 1.540 + deviceTranslation = ctx->CurrentMatrix().GetTranslation(); 1.541 + } else { 1.542 + drawTarget = ctx->GetDrawTarget(); 1.543 + Matrix dtTransform = drawTarget->GetTransform(); 1.544 + deviceTranslation = gfxPoint(dtTransform._31, dtTransform._32); 1.545 + cairoTarget = static_cast<cairo_surface_t*> 1.546 + (drawTarget->GetNativeSurface(NativeSurfaceType::CAIRO_SURFACE)); 1.547 + } 1.548 + 1.549 + cairo_surface_t* tempXlibSurface = 1.550 + CreateTempXlibSurface(cairoTarget, drawTarget, size, 1.551 + canDrawOverBackground, flags, screen, visual, 1.552 + &method); 1.553 + if (!tempXlibSurface) 1.554 + return; 1.555 + 1.556 + bool drawIsOpaque = (flags & DRAW_IS_OPAQUE) != 0; 1.557 + if (!drawIsOpaque) { 1.558 + cairo_t* tmpCtx = cairo_create(tempXlibSurface); 1.559 + if (method == eCopyBackground) { 1.560 + NS_ASSERTION(cairoTarget, "eCopyBackground only used when there's a cairoTarget"); 1.561 + cairo_set_operator(tmpCtx, CAIRO_OPERATOR_SOURCE); 1.562 + gfxPoint pt = -(offset + deviceTranslation); 1.563 + cairo_set_source_surface(tmpCtx, cairoTarget, pt.x, pt.y); 1.564 + // The copy from the tempXlibSurface to the target context should 1.565 + // use operator SOURCE, but that would need a mask to bound the 1.566 + // operation. Here we only copy opaque backgrounds so operator 1.567 + // OVER will behave like SOURCE masked by the surface. 1.568 + NS_ASSERTION(cairo_surface_get_content(tempXlibSurface) == CAIRO_CONTENT_COLOR, 1.569 + "Don't copy background with a transparent surface"); 1.570 + } else { 1.571 + cairo_set_operator(tmpCtx, CAIRO_OPERATOR_CLEAR); 1.572 + } 1.573 + cairo_paint(tmpCtx); 1.574 + cairo_destroy(tmpCtx); 1.575 + } 1.576 + 1.577 + if (!DrawOntoTempSurface(tempXlibSurface, -drawingRect.TopLeft())) { 1.578 + cairo_surface_destroy(tempXlibSurface); 1.579 + return; 1.580 + } 1.581 + 1.582 + SurfaceFormat moz2DFormat = 1.583 + cairo_surface_get_content(tempXlibSurface) == CAIRO_CONTENT_COLOR ? 1.584 + SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8; 1.585 + if (method != eAlphaExtraction) { 1.586 + if (drawTarget) { 1.587 + NativeSurface native; 1.588 + native.mFormat = moz2DFormat; 1.589 + native.mType = NativeSurfaceType::CAIRO_SURFACE; 1.590 + native.mSurface = tempXlibSurface; 1.591 + native.mSize = ToIntSize(size); 1.592 + RefPtr<SourceSurface> sourceSurface = 1.593 + drawTarget->CreateSourceSurfaceFromNativeSurface(native); 1.594 + if (sourceSurface) { 1.595 + drawTarget->DrawSurface(sourceSurface, 1.596 + Rect(offset.x, offset.y, size.width, size.height), 1.597 + Rect(0, 0, size.width, size.height)); 1.598 + } 1.599 + } else { 1.600 + nsRefPtr<gfxASurface> tmpSurf = gfxASurface::Wrap(tempXlibSurface); 1.601 + ctx->SetSource(tmpSurf, offset); 1.602 + ctx->Paint(); 1.603 + } 1.604 + cairo_surface_destroy(tempXlibSurface); 1.605 + return; 1.606 + } 1.607 + 1.608 + nsRefPtr<gfxImageSurface> blackImage = 1.609 + CopyXlibSurfaceToImage(tempXlibSurface, size, gfxImageFormat::ARGB32); 1.610 + 1.611 + cairo_t* tmpCtx = cairo_create(tempXlibSurface); 1.612 + cairo_set_source_rgba(tmpCtx, 1.0, 1.0, 1.0, 1.0); 1.613 + cairo_set_operator(tmpCtx, CAIRO_OPERATOR_SOURCE); 1.614 + cairo_paint(tmpCtx); 1.615 + cairo_destroy(tmpCtx); 1.616 + DrawOntoTempSurface(tempXlibSurface, -drawingRect.TopLeft()); 1.617 + nsRefPtr<gfxImageSurface> whiteImage = 1.618 + CopyXlibSurfaceToImage(tempXlibSurface, size, gfxImageFormat::RGB24); 1.619 + 1.620 + if (blackImage->CairoStatus() == CAIRO_STATUS_SUCCESS && 1.621 + whiteImage->CairoStatus() == CAIRO_STATUS_SUCCESS) { 1.622 + if (!gfxAlphaRecovery::RecoverAlpha(blackImage, whiteImage)) { 1.623 + cairo_surface_destroy(tempXlibSurface); 1.624 + return; 1.625 + } 1.626 + 1.627 + gfxASurface* paintSurface = blackImage; 1.628 + if (drawTarget) { 1.629 + NativeSurface native; 1.630 + native.mFormat = moz2DFormat; 1.631 + native.mType = NativeSurfaceType::CAIRO_SURFACE; 1.632 + native.mSurface = paintSurface->CairoSurface(); 1.633 + native.mSize = ToIntSize(size); 1.634 + RefPtr<SourceSurface> sourceSurface = 1.635 + drawTarget->CreateSourceSurfaceFromNativeSurface(native); 1.636 + if (sourceSurface) { 1.637 + drawTarget->DrawSurface(sourceSurface, 1.638 + Rect(offset.x, offset.y, size.width, size.height), 1.639 + Rect(0, 0, size.width, size.height)); 1.640 + } 1.641 + } else { 1.642 + ctx->SetSource(paintSurface, offset); 1.643 + ctx->Paint(); 1.644 + } 1.645 + } 1.646 + cairo_surface_destroy(tempXlibSurface); 1.647 +}