/** * Try the direct path. * @return True if we took the direct path */ bool gfxXlibNativeRenderer::DrawDirect(gfxContext *ctx, nsIntSize size, PRUint32 flags, Screen *screen, Visual *visual) { cairo_t *cr = ctx->GetCairo(); /* Check that the target surface is an xlib surface. */ cairo_surface_t *target = cairo_get_group_target (cr); if (cairo_surface_get_type (target) != CAIRO_SURFACE_TYPE_XLIB) { NATIVE_DRAWING_NOTE("FALLBACK: non-X surface"); return false; } cairo_matrix_t matrix; cairo_get_matrix (cr, &matrix); double device_offset_x, device_offset_y; cairo_surface_get_device_offset (target, &device_offset_x, &device_offset_y); /* Draw() checked that the matrix contained only a very-close-to-integer translation. Here (and in several other places and thebes) device offsets are assumed to be integer. */ NS_ASSERTION(PRInt32(device_offset_x) == device_offset_x && PRInt32(device_offset_y) == device_offset_y, "Expected integer device offsets"); nsIntPoint offset(NS_lroundf(matrix.x0 + device_offset_x), NS_lroundf(matrix.y0 + device_offset_y)); int max_rectangles = 0; if (flags & DRAW_SUPPORTS_CLIP_RECT) { max_rectangles = 1; } if (flags & DRAW_SUPPORTS_CLIP_LIST) { max_rectangles = MAX_STATIC_CLIP_RECTANGLES; } /* The client won't draw outside the surface so consider this when analysing clip rectangles. */ nsIntRect bounds(offset, size); bounds.IntersectRect(bounds, nsIntRect(0, 0, cairo_xlib_surface_get_width(target), cairo_xlib_surface_get_height(target))); bool needs_clip = true; nsIntRect rectangles[MAX_STATIC_CLIP_RECTANGLES]; int rect_count = 0; /* Check that the clip is rectangular and aligned on unit boundaries. */ /* Temporarily set the matrix for _get_rectangular_clip. It's basically the identity matrix, but we must adjust for the fact that our offset-rect is in device coordinates. */ cairo_identity_matrix (cr); cairo_translate (cr, -device_offset_x, -device_offset_y); bool have_rectangular_clip = _get_rectangular_clip (cr, bounds, &needs_clip, rectangles, max_rectangles, &rect_count); cairo_set_matrix (cr, &matrix); if (!have_rectangular_clip) return false; /* Stop now if everything is clipped out */ if (needs_clip && rect_count == 0) return true; /* Check that the screen is supported. Visuals belong to screens, so, if alternate visuals are not supported, then alternate screens cannot be supported. */ bool supports_alternate_visual = (flags & DRAW_SUPPORTS_ALTERNATE_VISUAL) != 0; bool supports_alternate_screen = supports_alternate_visual && (flags & DRAW_SUPPORTS_ALTERNATE_SCREEN); if (!supports_alternate_screen && cairo_xlib_surface_get_screen (target) != screen) { NATIVE_DRAWING_NOTE("FALLBACK: non-default screen"); return false; } /* Check that there is a visual */ Visual *target_visual = cairo_xlib_surface_get_visual (target); if (!target_visual) { NATIVE_DRAWING_NOTE("FALLBACK: no Visual for surface"); return false; } /* Check that the visual is supported */ if (!supports_alternate_visual && target_visual != visual) { // Only the format of the visual is important (not the GLX properties) // for Xlib or XRender drawing. XRenderPictFormat *target_format = cairo_xlib_surface_get_xrender_format (target); if (!target_format || (target_format != XRenderFindVisualFormat (DisplayOfScreen(screen), visual))) { NATIVE_DRAWING_NOTE("FALLBACK: unsupported Visual"); return false; } } /* we're good to go! */ NATIVE_DRAWING_NOTE("TAKING FAST PATH\n"); cairo_surface_flush (target); nsRefPtr<gfxASurface> surface = gfxASurface::Wrap(target); nsresult rv = DrawWithXlib(static_cast<gfxXlibSurface*>(surface.get()), offset, rectangles, needs_clip ? rect_count : 0); if (NS_SUCCEEDED(rv)) { cairo_surface_mark_dirty (target); return true; } return false; }
/* NB: These are pixels, not twips. */ NS_IMETHODIMP nsThebesImage::Draw(nsIRenderingContext &aContext, const gfxRect &aSourceRect, const gfxRect &aSubimageRect, const gfxRect &aDestRect) { if (NS_UNLIKELY(aDestRect.IsEmpty())) { NS_ERROR("nsThebesImage::Draw zero dest size - please fix caller."); return NS_OK; } nsThebesRenderingContext *thebesRC = static_cast<nsThebesRenderingContext*>(&aContext); gfxContext *ctx = thebesRC->ThebesContext(); #if 0 fprintf (stderr, "nsThebesImage::Draw src [%f %f %f %f] dest [%f %f %f %f] trans: [%f %f] dec: [%f %f]\n", aSourceRect.pos.x, aSourceRect.pos.y, aSourceRect.size.width, aSourceRect.size.height, aDestRect.pos.x, aDestRect.pos.y, aDestRect.size.width, aDestRect.size.height, ctx->CurrentMatrix().GetTranslation().x, ctx->CurrentMatrix().GetTranslation().y, mDecoded.x, mDecoded.y, mDecoded.width, mDecoded.height); #endif if (mSinglePixel) { // if a == 0, it's a noop if (mSinglePixelColor.a == 0.0) return NS_OK; // otherwise gfxContext::GraphicsOperator op = ctx->CurrentOperator(); if (op == gfxContext::OPERATOR_OVER && mSinglePixelColor.a == 1.0) ctx->SetOperator(gfxContext::OPERATOR_SOURCE); ctx->SetDeviceColor(mSinglePixelColor); ctx->NewPath(); ctx->Rectangle(aDestRect, PR_TRUE); ctx->Fill(); ctx->SetOperator(op); return NS_OK; } gfxFloat xscale = aDestRect.size.width / aSourceRect.size.width; gfxFloat yscale = aDestRect.size.height / aSourceRect.size.height; gfxRect srcRect(aSourceRect); gfxRect subimageRect(aSubimageRect); gfxRect destRect(aDestRect); if (!GetIsImageComplete()) { gfxRect decoded = gfxRect(mDecoded.x, mDecoded.y, mDecoded.width, mDecoded.height); srcRect = srcRect.Intersect(decoded); subimageRect = subimageRect.Intersect(decoded); // This happens when mDecoded.width or height is zero. bug 368427. if (NS_UNLIKELY(srcRect.size.width == 0 || srcRect.size.height == 0)) return NS_OK; destRect.pos.x += (srcRect.pos.x - aSourceRect.pos.x)*xscale; destRect.pos.y += (srcRect.pos.y - aSourceRect.pos.y)*yscale; destRect.size.width = srcRect.size.width * xscale; destRect.size.height = srcRect.size.height * yscale; } // if either rectangle is empty now (possibly after the image complete check) if (srcRect.IsEmpty() || destRect.IsEmpty()) return NS_OK; // Reject over-wide or over-tall images. if (!AllowedImageSize(destRect.size.width + 1, destRect.size.height + 1)) return NS_ERROR_FAILURE; // Expand the subimageRect to place its edges on integer coordinates. // Basically, if we're allowed to sample part of a pixel we can // sample the whole pixel. subimageRect.RoundOut(); nsRefPtr<gfxPattern> pat; PRBool ctxHasNonTranslation = ctx->CurrentMatrix().HasNonTranslation(); if ((xscale == 1.0 && yscale == 1.0 && !ctxHasNonTranslation) || subimageRect == gfxRect(0, 0, mWidth, mHeight)) { // No need to worry about sampling outside the subimage rectangle, // so no need for a temporary // XXX should we also check for situations where the source rect // is well inside the subimage so we can't sample outside? pat = new gfxPattern(ThebesSurface()); } else { // Because of the RoundOut above, the subimageRect has // integer width and height. gfxIntSize size(PRInt32(subimageRect.Width()), PRInt32(subimageRect.Height())); nsRefPtr<gfxASurface> temp = gfxPlatform::GetPlatform()->CreateOffscreenSurface(size, mFormat); if (!temp || temp->CairoStatus() != 0) return NS_ERROR_FAILURE; gfxContext tempctx(temp); tempctx.SetSource(ThebesSurface(), -subimageRect.pos); tempctx.SetOperator(gfxContext::OPERATOR_SOURCE); tempctx.Paint(); pat = new gfxPattern(temp); srcRect.MoveBy(-subimageRect.pos); } /* See bug 364968 to understand the necessity of this goop; we basically * have to pre-downscale any image that would fall outside of a scaled 16-bit * coordinate space. */ gfxFloat deviceX, deviceY; nsRefPtr<gfxASurface> currentTarget = ctx->CurrentSurface(&deviceX, &deviceY); // Quartz's matrix limits are much larger than pixman so don't use a temporary // context there so we preserve scaled image caching if (currentTarget->GetType() != gfxASurface::SurfaceTypeQuartz && (aDestRect.pos.x * (1.0 / xscale) >= 32768.0 || aDestRect.pos.y * (1.0 / yscale) >= 32768.0)) { gfxIntSize dim(NS_lroundf(destRect.size.width), NS_lroundf(destRect.size.height)); // nothing to do in this case if (dim.width == 0 || dim.height == 0) return NS_OK; nsRefPtr<gfxASurface> temp = gfxPlatform::GetPlatform()->CreateOffscreenSurface (dim, mFormat); if (!temp || temp->CairoStatus() != 0) return NS_ERROR_FAILURE; gfxContext tempctx(temp); gfxMatrix mat; mat.Translate(srcRect.pos); mat.Scale(1.0 / xscale, 1.0 / yscale); pat->SetMatrix(mat); tempctx.SetPattern(pat); tempctx.SetOperator(gfxContext::OPERATOR_SOURCE); tempctx.NewPath(); tempctx.Rectangle(gfxRect(0.0, 0.0, dim.width, dim.height)); tempctx.Fill(); pat = new gfxPattern(temp); srcRect.pos.x = 0.0; srcRect.pos.y = 0.0; srcRect.size.width = dim.width; srcRect.size.height = dim.height; xscale = 1.0; yscale = 1.0; } gfxMatrix mat; mat.Translate(srcRect.pos); mat.Scale(1.0/xscale, 1.0/yscale); /* Translate the start point of the image (srcRect.pos) * to coincide with the destination rectangle origin */ mat.Translate(-destRect.pos); pat->SetMatrix(mat); nsRefPtr<gfxASurface> target = ctx->CurrentSurface(); switch (target->GetType()) { case gfxASurface::SurfaceTypeXlib: case gfxASurface::SurfaceTypeXcb: // See bug 324698. This is a workaround for EXTEND_PAD not being // implemented correctly on linux in the X server. // // Set the filter to CAIRO_FILTER_FAST if we're scaling up -- otherwise, // pixman's sampling will sample transparency for the outside edges and we'll // get blurry edges. CAIRO_EXTEND_PAD would also work here, if // available // // This effectively disables smooth upscaling for images. if (xscale > 1.0 || yscale > 1.0 || ctxHasNonTranslation) pat->SetFilter(0); break; case gfxASurface::SurfaceTypeQuartz: case gfxASurface::SurfaceTypeQuartzImage: // Do nothing, Mac seems to be OK. Really? break; default: // turn on EXTEND_PAD. // This is what we really want for all surface types, if the // implementation was universally good. if (xscale != 1.0 || yscale != 1.0 || ctxHasNonTranslation) pat->SetExtend(gfxPattern::EXTEND_PAD); break; } gfxContext::GraphicsOperator op = ctx->CurrentOperator(); if (op == gfxContext::OPERATOR_OVER && mFormat == gfxASurface::ImageFormatRGB24) ctx->SetOperator(gfxContext::OPERATOR_SOURCE); ctx->NewPath(); ctx->SetPattern(pat); ctx->Rectangle(destRect); ctx->Fill(); ctx->SetOperator(op); ctx->SetDeviceColor(gfxRGBA(0,0,0,0)); return NS_OK; }