gfx/thebes/gfxXlibNativeRenderer.cpp

changeset 0
6474c204b198
     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 +}

mercurial