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