michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "gfxXlibNativeRenderer.h" michael@0: michael@0: #include "gfxXlibSurface.h" michael@0: #include "gfxImageSurface.h" michael@0: #include "gfxContext.h" michael@0: #include "gfxPlatform.h" michael@0: #include "gfxAlphaRecovery.h" michael@0: #include "cairo-xlib.h" michael@0: #include "cairo-xlib-xrender.h" michael@0: #include "mozilla/gfx/BorrowedContext.h" michael@0: #include "gfx2DGlue.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::gfx; michael@0: michael@0: #if 0 michael@0: #include michael@0: #define NATIVE_DRAWING_NOTE(m) fprintf(stderr, m) michael@0: #else michael@0: #define NATIVE_DRAWING_NOTE(m) do {} while (0) michael@0: #endif michael@0: michael@0: /* We have four basic strategies available: michael@0: michael@0: 1) 'direct': If the target is an xlib surface, and other conditions are met, michael@0: we can pass the underlying drawable directly to the callback. michael@0: michael@0: 2) 'simple': If the drawing is opaque, or we can draw to a surface with an michael@0: alpha channel, then we can create a temporary xlib surface, pass its michael@0: underlying drawable to the callback, and composite the result using michael@0: cairo. michael@0: michael@0: 3) 'copy-background': If the drawing is not opaque but the target is michael@0: opaque, and we can draw to a surface with format such that pixel michael@0: conversion to and from the target format is exact, we can create a michael@0: temporary xlib surface, copy the background from the target, pass the michael@0: underlying drawable to the callback, and copy back to the target. michael@0: michael@0: This strategy is not used if the pixel format conversion is not exact, michael@0: because that would mean that drawing intended to be very transparent michael@0: messes with other content. michael@0: michael@0: The strategy is prefered over simple for non-opaque drawing and opaque michael@0: targets on the same screen as compositing without alpha is a simpler michael@0: operation. michael@0: michael@0: 4) 'alpha-extraction': create a temporary xlib surface, fill with black, michael@0: pass its underlying drawable to the callback, copy the results to a michael@0: cairo image surface, repeat with a white background, update the on-black michael@0: image alpha values by comparing the two images, then paint the on-black michael@0: image using cairo. michael@0: michael@0: Sure would be nice to have an X extension or GL to do this for us on the michael@0: server... michael@0: */ michael@0: michael@0: static cairo_bool_t michael@0: _convert_coord_to_int (double coord, int32_t *v) michael@0: { michael@0: *v = (int32_t)coord; michael@0: /* XXX allow some tolerance here? */ michael@0: return *v == coord; michael@0: } michael@0: michael@0: static bool michael@0: _get_rectangular_clip (cairo_t *cr, michael@0: const nsIntRect& bounds, michael@0: bool *need_clip, michael@0: nsIntRect *rectangles, int max_rectangles, michael@0: int *num_rectangles) michael@0: { michael@0: cairo_rectangle_list_t *cliplist; michael@0: cairo_rectangle_t *clips; michael@0: int i; michael@0: bool retval = true; michael@0: michael@0: cliplist = cairo_copy_clip_rectangle_list (cr); michael@0: if (cliplist->status != CAIRO_STATUS_SUCCESS) { michael@0: retval = false; michael@0: NATIVE_DRAWING_NOTE("FALLBACK: non-rectangular clip"); michael@0: goto FINISH; michael@0: } michael@0: michael@0: /* the clip is always in surface backend coordinates (i.e. native backend coords) */ michael@0: clips = cliplist->rectangles; michael@0: michael@0: for (i = 0; i < cliplist->num_rectangles; ++i) { michael@0: michael@0: nsIntRect rect; michael@0: if (!_convert_coord_to_int (clips[i].x, &rect.x) || michael@0: !_convert_coord_to_int (clips[i].y, &rect.y) || michael@0: !_convert_coord_to_int (clips[i].width, &rect.width) || michael@0: !_convert_coord_to_int (clips[i].height, &rect.height)) michael@0: { michael@0: retval = false; michael@0: NATIVE_DRAWING_NOTE("FALLBACK: non-integer clip"); michael@0: goto FINISH; michael@0: } michael@0: michael@0: if (rect.IsEqualInterior(bounds)) { michael@0: /* the bounds are entirely inside the clip region so we don't need to clip. */ michael@0: *need_clip = false; michael@0: goto FINISH; michael@0: } michael@0: michael@0: NS_ASSERTION(bounds.Contains(rect), michael@0: "Was expecting to be clipped to bounds"); michael@0: michael@0: if (i >= max_rectangles) { michael@0: retval = false; michael@0: NATIVE_DRAWING_NOTE("FALLBACK: unsupported clip rectangle count"); michael@0: goto FINISH; michael@0: } michael@0: michael@0: rectangles[i] = rect; michael@0: } michael@0: michael@0: *need_clip = true; michael@0: *num_rectangles = cliplist->num_rectangles; michael@0: michael@0: FINISH: michael@0: cairo_rectangle_list_destroy (cliplist); michael@0: michael@0: return retval; michael@0: } michael@0: michael@0: #define MAX_STATIC_CLIP_RECTANGLES 50 michael@0: michael@0: /** michael@0: * Try the direct path. michael@0: * @return True if we took the direct path michael@0: */ michael@0: bool michael@0: gfxXlibNativeRenderer::DrawDirect(gfxContext *ctx, nsIntSize size, michael@0: uint32_t flags, michael@0: Screen *screen, Visual *visual) michael@0: { michael@0: if (ctx->IsCairo()) { michael@0: return DrawCairo(ctx->GetCairo(), size, flags, screen, visual); michael@0: } michael@0: michael@0: // We need to actually borrow the context because we want to read out the michael@0: // clip rectangles. michael@0: BorrowedCairoContext borrowed(ctx->GetDrawTarget()); michael@0: if (!borrowed.mCairo) { michael@0: return false; michael@0: } michael@0: michael@0: bool direct = DrawCairo(borrowed.mCairo, size, flags, screen, visual); michael@0: borrowed.Finish(); michael@0: michael@0: return direct; michael@0: } michael@0: michael@0: bool michael@0: gfxXlibNativeRenderer::DrawCairo(cairo_t* cr, nsIntSize size, michael@0: uint32_t flags, michael@0: Screen *screen, Visual *visual) michael@0: { michael@0: /* Check that the target surface is an xlib surface. */ michael@0: cairo_surface_t *target = cairo_get_group_target (cr); michael@0: if (cairo_surface_get_type (target) != CAIRO_SURFACE_TYPE_XLIB) { michael@0: NATIVE_DRAWING_NOTE("FALLBACK: non-X surface"); michael@0: return false; michael@0: } michael@0: michael@0: cairo_matrix_t matrix; michael@0: cairo_get_matrix (cr, &matrix); michael@0: double device_offset_x, device_offset_y; michael@0: cairo_surface_get_device_offset (target, &device_offset_x, &device_offset_y); michael@0: michael@0: /* Draw() checked that the matrix contained only a very-close-to-integer michael@0: translation. Here (and in several other places and thebes) device michael@0: offsets are assumed to be integer. */ michael@0: NS_ASSERTION(int32_t(device_offset_x) == device_offset_x && michael@0: int32_t(device_offset_y) == device_offset_y, michael@0: "Expected integer device offsets"); michael@0: nsIntPoint offset(NS_lroundf(matrix.x0 + device_offset_x), michael@0: NS_lroundf(matrix.y0 + device_offset_y)); michael@0: michael@0: int max_rectangles = 0; michael@0: if (flags & DRAW_SUPPORTS_CLIP_RECT) { michael@0: max_rectangles = 1; michael@0: } michael@0: if (flags & DRAW_SUPPORTS_CLIP_LIST) { michael@0: max_rectangles = MAX_STATIC_CLIP_RECTANGLES; michael@0: } michael@0: michael@0: /* The client won't draw outside the surface so consider this when michael@0: analysing clip rectangles. */ michael@0: nsIntRect bounds(offset, size); michael@0: bounds.IntersectRect(bounds, michael@0: nsIntRect(0, 0, michael@0: cairo_xlib_surface_get_width(target), michael@0: cairo_xlib_surface_get_height(target))); michael@0: michael@0: bool needs_clip = true; michael@0: nsIntRect rectangles[MAX_STATIC_CLIP_RECTANGLES]; michael@0: int rect_count = 0; michael@0: michael@0: /* Check that the clip is rectangular and aligned on unit boundaries. */ michael@0: /* Temporarily set the matrix for _get_rectangular_clip. It's basically michael@0: the identity matrix, but we must adjust for the fact that our michael@0: offset-rect is in device coordinates. */ michael@0: cairo_identity_matrix (cr); michael@0: cairo_translate (cr, -device_offset_x, -device_offset_y); michael@0: bool have_rectangular_clip = michael@0: _get_rectangular_clip (cr, bounds, &needs_clip, michael@0: rectangles, max_rectangles, &rect_count); michael@0: cairo_set_matrix (cr, &matrix); michael@0: if (!have_rectangular_clip) michael@0: return false; michael@0: michael@0: /* Stop now if everything is clipped out */ michael@0: if (needs_clip && rect_count == 0) michael@0: return true; michael@0: michael@0: /* Check that the screen is supported. michael@0: Visuals belong to screens, so, if alternate visuals are not supported, michael@0: then alternate screens cannot be supported. */ michael@0: bool supports_alternate_visual = michael@0: (flags & DRAW_SUPPORTS_ALTERNATE_VISUAL) != 0; michael@0: bool supports_alternate_screen = supports_alternate_visual && michael@0: (flags & DRAW_SUPPORTS_ALTERNATE_SCREEN); michael@0: if (!supports_alternate_screen && michael@0: cairo_xlib_surface_get_screen (target) != screen) { michael@0: NATIVE_DRAWING_NOTE("FALLBACK: non-default screen"); michael@0: return false; michael@0: } michael@0: michael@0: /* Check that there is a visual */ michael@0: Visual *target_visual = cairo_xlib_surface_get_visual (target); michael@0: if (!target_visual) { michael@0: NATIVE_DRAWING_NOTE("FALLBACK: no Visual for surface"); michael@0: return false; michael@0: } michael@0: /* Check that the visual is supported */ michael@0: if (!supports_alternate_visual && target_visual != visual) { michael@0: // Only the format of the visual is important (not the GLX properties) michael@0: // for Xlib or XRender drawing. michael@0: XRenderPictFormat *target_format = michael@0: cairo_xlib_surface_get_xrender_format (target); michael@0: if (!target_format || michael@0: (target_format != michael@0: XRenderFindVisualFormat (DisplayOfScreen(screen), visual))) { michael@0: NATIVE_DRAWING_NOTE("FALLBACK: unsupported Visual"); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: /* we're good to go! */ michael@0: NATIVE_DRAWING_NOTE("TAKING FAST PATH\n"); michael@0: cairo_surface_flush (target); michael@0: nsresult rv = DrawWithXlib(target, michael@0: offset, rectangles, michael@0: needs_clip ? rect_count : 0); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: cairo_surface_mark_dirty (target); michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: static bool michael@0: VisualHasAlpha(Screen *screen, Visual *visual) { michael@0: // There may be some other visuals format with alpha but usually this is michael@0: // the only one we care about. michael@0: return visual->c_class == TrueColor && michael@0: visual->bits_per_rgb == 8 && michael@0: visual->red_mask == 0xff0000 && michael@0: visual->green_mask == 0xff00 && michael@0: visual->blue_mask == 0xff && michael@0: gfxXlibSurface::DepthOfVisual(screen, visual) == 32; michael@0: } michael@0: michael@0: // Returns whether pixel conversion between visual and format is exact (in michael@0: // both directions). michael@0: static bool michael@0: FormatConversionIsExact(Screen *screen, Visual *visual, XRenderPictFormat *format) { michael@0: if (!format || michael@0: visual->c_class != TrueColor || michael@0: format->type != PictTypeDirect || michael@0: gfxXlibSurface::DepthOfVisual(screen, visual) != format->depth) michael@0: return false; michael@0: michael@0: XRenderPictFormat *visualFormat = michael@0: XRenderFindVisualFormat(DisplayOfScreen(screen), visual); michael@0: michael@0: if (visualFormat->type != PictTypeDirect ) michael@0: return false; michael@0: michael@0: const XRenderDirectFormat& a = visualFormat->direct; michael@0: const XRenderDirectFormat& b = format->direct; michael@0: return a.redMask == b.redMask && michael@0: a.greenMask == b.greenMask && michael@0: a.blueMask == b.blueMask; michael@0: } michael@0: michael@0: // The 3 non-direct strategies described above. michael@0: // The surface format and strategy are inter-dependent. michael@0: enum DrawingMethod { michael@0: eSimple, michael@0: eCopyBackground, michael@0: eAlphaExtraction michael@0: }; michael@0: michael@0: static cairo_surface_t* michael@0: CreateTempXlibSurface (cairo_surface_t* cairoTarget, michael@0: DrawTarget* drawTarget, michael@0: nsIntSize size, michael@0: bool canDrawOverBackground, michael@0: uint32_t flags, Screen *screen, Visual *visual, michael@0: DrawingMethod *method) michael@0: { michael@0: NS_ASSERTION(cairoTarget || drawTarget, "Must have some type"); michael@0: michael@0: bool drawIsOpaque = (flags & gfxXlibNativeRenderer::DRAW_IS_OPAQUE) != 0; michael@0: bool supportsAlternateVisual = michael@0: (flags & gfxXlibNativeRenderer::DRAW_SUPPORTS_ALTERNATE_VISUAL) != 0; michael@0: bool supportsAlternateScreen = supportsAlternateVisual && michael@0: (flags & gfxXlibNativeRenderer::DRAW_SUPPORTS_ALTERNATE_SCREEN); michael@0: michael@0: cairo_surface_type_t cairoTargetType = michael@0: cairoTarget ? cairo_surface_get_type (cairoTarget) : (cairo_surface_type_t)0xFF; michael@0: michael@0: Screen *target_screen = cairoTargetType == CAIRO_SURFACE_TYPE_XLIB ? michael@0: cairo_xlib_surface_get_screen (cairoTarget) : screen; michael@0: michael@0: // When the background has an alpha channel, we need to draw with an alpha michael@0: // channel anyway, so there is no need to copy the background. If michael@0: // doCopyBackground is set here, we'll also need to check below that the michael@0: // background can copied without any loss in format conversions. michael@0: bool doCopyBackground = !drawIsOpaque && canDrawOverBackground && michael@0: cairoTarget && cairo_surface_get_content (cairoTarget) == CAIRO_CONTENT_COLOR; michael@0: michael@0: if (supportsAlternateScreen && screen != target_screen && drawIsOpaque) { michael@0: // Prefer a visual on the target screen. michael@0: // (If !drawIsOpaque, we'll need doCopyBackground or an alpha channel.) michael@0: visual = DefaultVisualOfScreen(target_screen); michael@0: screen = target_screen; michael@0: michael@0: } else if (doCopyBackground || (supportsAlternateVisual && drawIsOpaque)) { michael@0: // Analyse the pixel formats either to check whether we can michael@0: // doCopyBackground or to see if we can find a better visual for michael@0: // opaque drawing. michael@0: Visual *target_visual = nullptr; michael@0: XRenderPictFormat *target_format = nullptr; michael@0: if (cairoTargetType == CAIRO_SURFACE_TYPE_XLIB) { michael@0: target_visual = cairo_xlib_surface_get_visual (cairoTarget); michael@0: target_format = cairo_xlib_surface_get_xrender_format (cairoTarget); michael@0: } else if (cairoTargetType == CAIRO_SURFACE_TYPE_IMAGE || drawTarget) { michael@0: gfxImageFormat imageFormat = michael@0: drawTarget ? SurfaceFormatToImageFormat(drawTarget->GetFormat()) : michael@0: (gfxImageFormat)cairo_image_surface_get_format(cairoTarget); michael@0: target_visual = gfxXlibSurface::FindVisual(screen, imageFormat); michael@0: Display *dpy = DisplayOfScreen(screen); michael@0: if (target_visual) { michael@0: target_format = XRenderFindVisualFormat(dpy, target_visual); michael@0: } else { michael@0: target_format = michael@0: gfxXlibSurface::FindRenderFormat(dpy, imageFormat); michael@0: } michael@0: } michael@0: michael@0: if (supportsAlternateVisual && michael@0: (supportsAlternateScreen || screen == target_screen)) { michael@0: if (target_visual) { michael@0: visual = target_visual; michael@0: screen = target_screen; michael@0: } michael@0: } michael@0: // Could try harder to match formats across screens for background michael@0: // copying when !supportsAlternateScreen, if we cared. Preferably michael@0: // we'll find a visual below with an alpha channel anyway; if so, the michael@0: // background won't need to be copied. michael@0: michael@0: if (doCopyBackground && visual != target_visual && michael@0: !FormatConversionIsExact(screen, visual, target_format)) { michael@0: doCopyBackground = false; michael@0: } michael@0: } michael@0: michael@0: if (supportsAlternateVisual && !drawIsOpaque && michael@0: (screen != target_screen || michael@0: !(doCopyBackground || VisualHasAlpha(screen, visual)))) { michael@0: // Try to find a visual with an alpha channel. michael@0: Screen *visualScreen = michael@0: supportsAlternateScreen ? target_screen : screen; michael@0: Visual *argbVisual = michael@0: gfxXlibSurface::FindVisual(visualScreen, michael@0: gfxImageFormat::ARGB32); michael@0: if (argbVisual) { michael@0: visual = argbVisual; michael@0: screen = visualScreen; michael@0: } else if (!doCopyBackground && michael@0: gfxXlibSurface::DepthOfVisual(screen, visual) != 24) { michael@0: // Will need to do alpha extraction; prefer a 24-bit visual. michael@0: // No advantage in using the target screen. michael@0: Visual *rgb24Visual = michael@0: gfxXlibSurface::FindVisual(screen, michael@0: gfxImageFormat::RGB24); michael@0: if (rgb24Visual) { michael@0: visual = rgb24Visual; michael@0: } michael@0: } michael@0: } michael@0: michael@0: Drawable drawable = michael@0: (screen == target_screen && cairoTargetType == CAIRO_SURFACE_TYPE_XLIB) ? michael@0: cairo_xlib_surface_get_drawable (cairoTarget) : RootWindowOfScreen(screen); michael@0: michael@0: cairo_surface_t *surface = michael@0: gfxXlibSurface::CreateCairoSurface(screen, visual, michael@0: gfxIntSize(size.width, size.height), michael@0: drawable); michael@0: if (!surface) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (drawIsOpaque || michael@0: cairo_surface_get_content(surface) == CAIRO_CONTENT_COLOR_ALPHA) { michael@0: NATIVE_DRAWING_NOTE(drawIsOpaque ? michael@0: ", SIMPLE OPAQUE\n" : ", SIMPLE WITH ALPHA"); michael@0: *method = eSimple; michael@0: } else if (doCopyBackground) { michael@0: NATIVE_DRAWING_NOTE(", COPY BACKGROUND\n"); michael@0: *method = eCopyBackground; michael@0: } else { michael@0: NATIVE_DRAWING_NOTE(", SLOW ALPHA EXTRACTION\n"); michael@0: *method = eAlphaExtraction; michael@0: } michael@0: michael@0: return surface; michael@0: } michael@0: michael@0: bool michael@0: gfxXlibNativeRenderer::DrawOntoTempSurface(cairo_surface_t *tempXlibSurface, michael@0: nsIntPoint offset) michael@0: { michael@0: cairo_surface_flush(tempXlibSurface); michael@0: /* no clipping is needed because the callback can't draw outside the native michael@0: surface anyway */ michael@0: nsresult rv = DrawWithXlib(tempXlibSurface, offset, nullptr, 0); michael@0: cairo_surface_mark_dirty(tempXlibSurface); michael@0: return NS_SUCCEEDED(rv); michael@0: } michael@0: michael@0: static already_AddRefed michael@0: CopyXlibSurfaceToImage(cairo_surface_t *tempXlibSurface, michael@0: gfxIntSize size, michael@0: gfxImageFormat format) michael@0: { michael@0: nsRefPtr result = new gfxImageSurface(size, format); michael@0: michael@0: cairo_t* copyCtx = cairo_create(result->CairoSurface()); michael@0: cairo_set_source_surface(copyCtx, tempXlibSurface, 0, 0); michael@0: cairo_set_operator(copyCtx, CAIRO_OPERATOR_SOURCE); michael@0: cairo_paint(copyCtx); michael@0: cairo_destroy(copyCtx); michael@0: michael@0: return result.forget(); michael@0: } michael@0: michael@0: void michael@0: gfxXlibNativeRenderer::Draw(gfxContext* ctx, nsIntSize size, michael@0: uint32_t flags, Screen *screen, Visual *visual) michael@0: { michael@0: gfxMatrix matrix = ctx->CurrentMatrix(); michael@0: michael@0: // We can only draw direct or onto a copied background if pixels align and michael@0: // native drawing is compatible with the current operator. (The matrix is michael@0: // actually also pixel-exact for flips and right-angle rotations, which michael@0: // would permit copying the background but not drawing direct.) michael@0: bool matrixIsIntegerTranslation = !matrix.HasNonIntegerTranslation(); michael@0: bool canDrawOverBackground = matrixIsIntegerTranslation && michael@0: ctx->CurrentOperator() == gfxContext::OPERATOR_OVER; michael@0: michael@0: // The padding of 0.5 for non-pixel-exact transformations used here is michael@0: // the same as what _cairo_pattern_analyze_filter uses. michael@0: const gfxFloat filterRadius = 0.5; michael@0: gfxRect affectedRect(0.0, 0.0, size.width, size.height); michael@0: if (!matrixIsIntegerTranslation) { michael@0: // The filter footprint means that the affected rectangle is a michael@0: // little larger than the drawingRect; michael@0: affectedRect.Inflate(filterRadius); michael@0: michael@0: NATIVE_DRAWING_NOTE("FALLBACK: matrix not integer translation"); michael@0: } else if (!canDrawOverBackground) { michael@0: NATIVE_DRAWING_NOTE("FALLBACK: unsupported operator"); michael@0: } michael@0: michael@0: // Clipping to the region affected by drawing allows us to consider only michael@0: // the portions of the clip region that will be affected by drawing. michael@0: gfxRect clipExtents; michael@0: { michael@0: gfxContextAutoSaveRestore autoSR(ctx); michael@0: ctx->Clip(affectedRect); michael@0: michael@0: clipExtents = ctx->GetClipExtents(); michael@0: if (clipExtents.IsEmpty()) michael@0: return; // nothing to do michael@0: michael@0: if (canDrawOverBackground && michael@0: DrawDirect(ctx, size, flags, screen, visual)) michael@0: return; michael@0: } michael@0: michael@0: nsIntRect drawingRect(nsIntPoint(0, 0), size); michael@0: // Drawing need only be performed within the clip extents michael@0: // (and padding for the filter). michael@0: if (!matrixIsIntegerTranslation) { michael@0: // The source surface may need to be a little larger than the clip michael@0: // extents due to the filter footprint. michael@0: clipExtents.Inflate(filterRadius); michael@0: } michael@0: clipExtents.RoundOut(); michael@0: michael@0: nsIntRect intExtents(int32_t(clipExtents.X()), michael@0: int32_t(clipExtents.Y()), michael@0: int32_t(clipExtents.Width()), michael@0: int32_t(clipExtents.Height())); michael@0: drawingRect.IntersectRect(drawingRect, intExtents); michael@0: michael@0: gfxPoint offset(drawingRect.x, drawingRect.y); michael@0: michael@0: DrawingMethod method; michael@0: cairo_surface_t* cairoTarget = nullptr; michael@0: DrawTarget* drawTarget = nullptr; michael@0: gfxPoint deviceTranslation; michael@0: if (ctx->IsCairo()) { michael@0: cairoTarget = cairo_get_group_target(ctx->GetCairo()); michael@0: deviceTranslation = ctx->CurrentMatrix().GetTranslation(); michael@0: } else { michael@0: drawTarget = ctx->GetDrawTarget(); michael@0: Matrix dtTransform = drawTarget->GetTransform(); michael@0: deviceTranslation = gfxPoint(dtTransform._31, dtTransform._32); michael@0: cairoTarget = static_cast michael@0: (drawTarget->GetNativeSurface(NativeSurfaceType::CAIRO_SURFACE)); michael@0: } michael@0: michael@0: cairo_surface_t* tempXlibSurface = michael@0: CreateTempXlibSurface(cairoTarget, drawTarget, size, michael@0: canDrawOverBackground, flags, screen, visual, michael@0: &method); michael@0: if (!tempXlibSurface) michael@0: return; michael@0: michael@0: bool drawIsOpaque = (flags & DRAW_IS_OPAQUE) != 0; michael@0: if (!drawIsOpaque) { michael@0: cairo_t* tmpCtx = cairo_create(tempXlibSurface); michael@0: if (method == eCopyBackground) { michael@0: NS_ASSERTION(cairoTarget, "eCopyBackground only used when there's a cairoTarget"); michael@0: cairo_set_operator(tmpCtx, CAIRO_OPERATOR_SOURCE); michael@0: gfxPoint pt = -(offset + deviceTranslation); michael@0: cairo_set_source_surface(tmpCtx, cairoTarget, pt.x, pt.y); michael@0: // The copy from the tempXlibSurface to the target context should michael@0: // use operator SOURCE, but that would need a mask to bound the michael@0: // operation. Here we only copy opaque backgrounds so operator michael@0: // OVER will behave like SOURCE masked by the surface. michael@0: NS_ASSERTION(cairo_surface_get_content(tempXlibSurface) == CAIRO_CONTENT_COLOR, michael@0: "Don't copy background with a transparent surface"); michael@0: } else { michael@0: cairo_set_operator(tmpCtx, CAIRO_OPERATOR_CLEAR); michael@0: } michael@0: cairo_paint(tmpCtx); michael@0: cairo_destroy(tmpCtx); michael@0: } michael@0: michael@0: if (!DrawOntoTempSurface(tempXlibSurface, -drawingRect.TopLeft())) { michael@0: cairo_surface_destroy(tempXlibSurface); michael@0: return; michael@0: } michael@0: michael@0: SurfaceFormat moz2DFormat = michael@0: cairo_surface_get_content(tempXlibSurface) == CAIRO_CONTENT_COLOR ? michael@0: SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8; michael@0: if (method != eAlphaExtraction) { michael@0: if (drawTarget) { michael@0: NativeSurface native; michael@0: native.mFormat = moz2DFormat; michael@0: native.mType = NativeSurfaceType::CAIRO_SURFACE; michael@0: native.mSurface = tempXlibSurface; michael@0: native.mSize = ToIntSize(size); michael@0: RefPtr sourceSurface = michael@0: drawTarget->CreateSourceSurfaceFromNativeSurface(native); michael@0: if (sourceSurface) { michael@0: drawTarget->DrawSurface(sourceSurface, michael@0: Rect(offset.x, offset.y, size.width, size.height), michael@0: Rect(0, 0, size.width, size.height)); michael@0: } michael@0: } else { michael@0: nsRefPtr tmpSurf = gfxASurface::Wrap(tempXlibSurface); michael@0: ctx->SetSource(tmpSurf, offset); michael@0: ctx->Paint(); michael@0: } michael@0: cairo_surface_destroy(tempXlibSurface); michael@0: return; michael@0: } michael@0: michael@0: nsRefPtr blackImage = michael@0: CopyXlibSurfaceToImage(tempXlibSurface, size, gfxImageFormat::ARGB32); michael@0: michael@0: cairo_t* tmpCtx = cairo_create(tempXlibSurface); michael@0: cairo_set_source_rgba(tmpCtx, 1.0, 1.0, 1.0, 1.0); michael@0: cairo_set_operator(tmpCtx, CAIRO_OPERATOR_SOURCE); michael@0: cairo_paint(tmpCtx); michael@0: cairo_destroy(tmpCtx); michael@0: DrawOntoTempSurface(tempXlibSurface, -drawingRect.TopLeft()); michael@0: nsRefPtr whiteImage = michael@0: CopyXlibSurfaceToImage(tempXlibSurface, size, gfxImageFormat::RGB24); michael@0: michael@0: if (blackImage->CairoStatus() == CAIRO_STATUS_SUCCESS && michael@0: whiteImage->CairoStatus() == CAIRO_STATUS_SUCCESS) { michael@0: if (!gfxAlphaRecovery::RecoverAlpha(blackImage, whiteImage)) { michael@0: cairo_surface_destroy(tempXlibSurface); michael@0: return; michael@0: } michael@0: michael@0: gfxASurface* paintSurface = blackImage; michael@0: if (drawTarget) { michael@0: NativeSurface native; michael@0: native.mFormat = moz2DFormat; michael@0: native.mType = NativeSurfaceType::CAIRO_SURFACE; michael@0: native.mSurface = paintSurface->CairoSurface(); michael@0: native.mSize = ToIntSize(size); michael@0: RefPtr sourceSurface = michael@0: drawTarget->CreateSourceSurfaceFromNativeSurface(native); michael@0: if (sourceSurface) { michael@0: drawTarget->DrawSurface(sourceSurface, michael@0: Rect(offset.x, offset.y, size.width, size.height), michael@0: Rect(0, 0, size.width, size.height)); michael@0: } michael@0: } else { michael@0: ctx->SetSource(paintSurface, offset); michael@0: ctx->Paint(); michael@0: } michael@0: } michael@0: cairo_surface_destroy(tempXlibSurface); michael@0: }