gfx/thebes/gfxXlibNativeRenderer.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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 }

mercurial