Bug 1043560 - Refactor the imgIContainer::Draw API. r=tn,dholbert,jwatt,mwu,mattwoodrow,roc sr=jrmuizel

--HG--
extra : rebase_source : b5ed02cb200ece12a07328613dca217e9d975703
This commit is contained in:
Seth Fowler 2014-08-22 13:12:38 -07:00
parent eae5445a6a
commit 71d94ea626
36 changed files with 1209 additions and 916 deletions

View File

@ -9,4 +9,4 @@
# (Fuzzy necessary due to pixel-wise comparison of different JPEGs.
# The vast majority of the fuzziness comes from Linux and WinXP.)
fuzzy(1,149) == bug917595-iframe-1.html bug917595-1-ref.html
fuzzy(2,446) == bug917595-exif-rotated.jpg bug917595-pixel-rotated.jpg
fuzzy(2,640) == bug917595-exif-rotated.jpg bug917595-pixel-rotated.jpg

View File

@ -401,11 +401,12 @@ private:
class MOZ_STACK_CLASS AutoSVGRenderingState
{
public:
AutoSVGRenderingState(const SVGImageContext* aSVGContext,
AutoSVGRenderingState(const Maybe<SVGImageContext>& aSVGContext,
float aFrameTime,
dom::SVGSVGElement* aRootElem
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: mHaveOverrides(!!aSVGContext)
: mHaveOverrides(aSVGContext.isSome() &&
aSVGContext->GetPreserveAspectRatio().isSome())
, mRootElem(aRootElem)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
@ -415,7 +416,7 @@ public:
// XXXdholbert We should technically be overriding the helper doc's clip
// and overflow properties here, too. See bug 272288 comment 36.
mRootElem->SetImageOverridePreserveAspectRatio(
aSVGContext->GetPreserveAspectRatio());
*aSVGContext->GetPreserveAspectRatio());
}
mOriginalTime = mRootElem->GetCurrentTime();

View File

@ -43,6 +43,7 @@
#include "nsTArray.h"
#include "ImageEncoder.h"
#include "ImageRegion.h"
#include "gfxContext.h"
#include "gfxASurface.h"
@ -97,6 +98,7 @@
#include "GLContext.h"
#include "GLContextProvider.h"
#include "SVGContentUtils.h"
#include "SVGImageContext.h"
#include "nsIScreenManager.h"
#undef free // apparently defined by some windows header, clashing with a free()
@ -126,6 +128,7 @@ using namespace mozilla;
using namespace mozilla::CanvasUtils;
using namespace mozilla::css;
using namespace mozilla::gfx;
using namespace mozilla::image;
using namespace mozilla::ipc;
using namespace mozilla::layers;
@ -3431,8 +3434,10 @@ CanvasRenderingContext2D::DrawImage(const HTMLImageOrCanvasOrVideoElement& image
DrawSurfaceOptions(filter),
DrawOptions(CurrentState().globalAlpha, UsedOperation()));
} else {
DrawDirectlyToCanvas(drawInfo, &bounds, dx, dy, dw, dh,
sx, sy, sw, sh, imgSize);
DrawDirectlyToCanvas(drawInfo, &bounds,
mgfx::Rect(dx, dy, dw, dh),
mgfx::Rect(sx, sy, sw, sh),
imgSize);
}
RedrawUser(gfxRect(dx, dy, dw, dh));
@ -3441,40 +3446,48 @@ CanvasRenderingContext2D::DrawImage(const HTMLImageOrCanvasOrVideoElement& image
void
CanvasRenderingContext2D::DrawDirectlyToCanvas(
const nsLayoutUtils::DirectDrawInfo& image,
mgfx::Rect* bounds, double dx, double dy,
double dw, double dh, double sx, double sy,
double sw, double sh, gfxIntSize imgSize)
mgfx::Rect* bounds,
mgfx::Rect dest,
mgfx::Rect src,
gfxIntSize imgSize)
{
gfxMatrix contextMatrix;
MOZ_ASSERT(src.width > 0 && src.height > 0,
"Need positive source width and height");
gfxMatrix contextMatrix;
AdjustedTarget tempTarget(this, bounds->IsEmpty() ? nullptr: bounds);
// get any already existing transforms on the context. Include transformations used for context shadow
// Get any existing transforms on the context, including transformations used
// for context shadow.
if (tempTarget) {
Matrix matrix = tempTarget->GetTransform();
contextMatrix = gfxMatrix(matrix._11, matrix._12, matrix._21,
matrix._22, matrix._31, matrix._32);
}
gfxSize contextScale(contextMatrix.ScaleFactors(true));
gfxMatrix transformMatrix;
transformMatrix.Translate(gfxPoint(sx, sy));
if (dw > 0 && dh > 0) {
transformMatrix.Scale(sw/dw, sh/dh);
}
transformMatrix.Translate(gfxPoint(-dx, -dy));
// Scale the dest rect to include the context scale.
dest.Scale(contextScale.width, contextScale.height);
// Scale the image size to the dest rect, and adjust the source rect to match.
gfxSize scale(dest.width / src.width, dest.height / src.height);
nsIntSize scaledImageSize(std::ceil(imgSize.width * scale.width),
std::ceil(imgSize.height * scale.height));
src.Scale(scale.width, scale.height);
nsRefPtr<gfxContext> context = new gfxContext(tempTarget);
context->SetMatrix(contextMatrix);
context->Scale(1.0 / contextScale.width, 1.0 / contextScale.height);
context->Translate(gfxPoint(dest.x - src.x, dest.y - src.y));
// FLAG_CLAMP is added for increased performance
// FLAG_CLAMP is added for increased performance, since we never tile here.
uint32_t modifiedFlags = image.mDrawingFlags | imgIContainer::FLAG_CLAMP;
nsresult rv = image.mImgContainer->
Draw(context, GraphicsFilter::FILTER_GOOD, transformMatrix,
gfxRect(gfxPoint(dx, dy), gfxIntSize(dw, dh)),
nsIntRect(nsIntPoint(0, 0), gfxIntSize(imgSize.width, imgSize.height)),
gfxIntSize(imgSize.width, imgSize.height), nullptr, image.mWhichFrame,
modifiedFlags);
Draw(context, scaledImageSize,
ImageRegion::Create(gfxRect(src.x, src.y, src.width, src.height)),
image.mWhichFrame, GraphicsFilter::FILTER_GOOD,
Nothing(), modifiedFlags);
NS_ENSURE_SUCCESS_VOID(rv);
}

View File

@ -665,9 +665,10 @@ protected:
uint8_t optional_argc, mozilla::ErrorResult& error);
void DrawDirectlyToCanvas(const nsLayoutUtils::DirectDrawInfo& image,
mozilla::gfx::Rect* bounds, double dx, double dy,
double dw, double dh, double sx, double sy,
double sw, double sh, gfxIntSize imgSize);
mozilla::gfx::Rect* bounds,
mozilla::gfx::Rect dest,
mozilla::gfx::Rect src,
gfxIntSize imgSize);
nsString& GetFont()
{

View File

@ -14,6 +14,7 @@
#include "mozilla/Base64.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/DataSurfaceHelpers.h"
#include "mozilla/Maybe.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Vector.h"
#include "nsComponentManagerUtils.h"
@ -27,6 +28,7 @@
#include "ycbcr_to_rgb565.h"
#include "GeckoProfiler.h"
#include "ImageContainer.h"
#include "ImageRegion.h"
#include "gfx2DGlue.h"
#include "gfxPrefs.h"
@ -35,6 +37,7 @@
#endif
using namespace mozilla;
using namespace mozilla::image;
using namespace mozilla::layers;
using namespace mozilla::gfx;
@ -367,28 +370,19 @@ OptimalFillOperator()
static already_AddRefed<gfxDrawable>
CreateSamplingRestrictedDrawable(gfxDrawable* aDrawable,
gfxContext* aContext,
const gfxMatrix& aUserSpaceToImageSpace,
const gfxRect& aSourceRect,
const gfxRect& aSubimage,
const ImageRegion& aRegion,
const SurfaceFormat aFormat)
{
PROFILER_LABEL("gfxUtils", "CreateSamplingRestricedDrawable",
js::ProfileEntry::Category::GRAPHICS);
gfxRect userSpaceClipExtents = aContext->GetClipExtents();
// This isn't optimal --- if aContext has a rotation then GetClipExtents
// will have to do a bounding-box computation, and TransformBounds might
// too, so we could get a better result if we computed image space clip
// extents in one go --- but it doesn't really matter and this is easier
// to understand.
gfxRect imageSpaceClipExtents =
aUserSpaceToImageSpace.TransformBounds(userSpaceClipExtents);
gfxRect clipExtents = aContext->GetClipExtents();
// Inflate by one pixel because bilinear filtering will sample at most
// one pixel beyond the computed image pixel coordinate.
imageSpaceClipExtents.Inflate(1.0);
clipExtents.Inflate(1.0);
gfxRect needed = imageSpaceClipExtents.Intersect(aSourceRect);
needed = needed.Intersect(aSubimage);
gfxRect needed = aRegion.IntersectAndRestrict(clipExtents);
needed.RoundOut();
// if 'needed' is empty, nothing will be drawn since aFill
@ -483,19 +477,17 @@ private:
};
static gfxMatrix
DeviceToImageTransform(gfxContext* aContext,
const gfxMatrix& aUserSpaceToImageSpace)
DeviceToImageTransform(gfxContext* aContext)
{
gfxFloat deviceX, deviceY;
nsRefPtr<gfxASurface> currentTarget =
aContext->CurrentSurface(&deviceX, &deviceY);
gfxMatrix currentMatrix = aContext->CurrentMatrix();
gfxMatrix deviceToUser = currentMatrix;
gfxMatrix deviceToUser = aContext->CurrentMatrix();
if (!deviceToUser.Invert()) {
return gfxMatrix(0, 0, 0, 0, 0, 0); // singular
}
deviceToUser.Translate(-gfxPoint(-deviceX, -deviceY));
return deviceToUser * aUserSpaceToImageSpace;
return deviceToUser;
}
/* These heuristics are based on Source/WebCore/platform/graphics/skia/ImageSkia.cpp:computeResamplingMode() */
@ -566,37 +558,36 @@ static GraphicsFilter ReduceResamplingFilter(GraphicsFilter aFilter,
#endif
/* static */ void
gfxUtils::DrawPixelSnapped(gfxContext* aContext,
gfxDrawable* aDrawable,
const gfxMatrix& aUserSpaceToImageSpace,
const gfxRect& aSubimage,
const gfxRect& aSourceRect,
const gfxRect& aImageRect,
const gfxRect& aFill,
gfxUtils::DrawPixelSnapped(gfxContext* aContext,
gfxDrawable* aDrawable,
const gfxSize& aImageSize,
const ImageRegion& aRegion,
const SurfaceFormat aFormat,
GraphicsFilter aFilter,
uint32_t aImageFlags)
GraphicsFilter aFilter,
uint32_t aImageFlags)
{
PROFILER_LABEL("gfxUtils", "DrawPixelSnapped",
js::ProfileEntry::Category::GRAPHICS);
bool doTile = !aImageRect.Contains(aSourceRect) &&
gfxRect imageRect(gfxPoint(0, 0), aImageSize);
gfxRect region(aRegion.Rect());
bool doTile = !imageRect.Contains(region) &&
!(aImageFlags & imgIContainer::FLAG_CLAMP);
nsRefPtr<gfxASurface> currentTarget = aContext->CurrentSurface();
gfxMatrix deviceSpaceToImageSpace =
DeviceToImageTransform(aContext, aUserSpaceToImageSpace);
gfxMatrix deviceSpaceToImageSpace = DeviceToImageTransform(aContext);
AutoCairoPixmanBugWorkaround workaround(aContext, deviceSpaceToImageSpace,
aFill, currentTarget);
region, currentTarget);
if (!workaround.Succeeded())
return;
nsRefPtr<gfxDrawable> drawable = aDrawable;
aFilter = ReduceResamplingFilter(aFilter, aImageRect.Width(), aImageRect.Height(), aSourceRect.Width(), aSourceRect.Height());
gfxMatrix userSpaceToImageSpace = aUserSpaceToImageSpace;
aFilter = ReduceResamplingFilter(aFilter,
imageRect.Width(), imageRect.Height(),
region.Width(), region.Height());
// On Mobile, we don't ever want to do this; it has the potential for
// allocating very large temporary surfaces, especially since we'll
@ -607,13 +598,11 @@ gfxUtils::DrawPixelSnapped(gfxContext* aContext,
// translations, then we assume no resampling will occur so there's
// nothing to do.
// XXX if only we had source-clipping in cairo!
if (aContext->CurrentMatrix().HasNonIntegerTranslation() ||
aUserSpaceToImageSpace.HasNonIntegerTranslation()) {
if (doTile || !aSubimage.Contains(aImageRect)) {
if (aContext->CurrentMatrix().HasNonIntegerTranslation()) {
if (doTile || !aRegion.RestrictionContains(imageRect)) {
nsRefPtr<gfxDrawable> restrictedDrawable =
CreateSamplingRestrictedDrawable(aDrawable, aContext,
aUserSpaceToImageSpace, aSourceRect,
aSubimage, aFormat);
aRegion, aFormat);
if (restrictedDrawable) {
drawable.swap(restrictedDrawable);
}
@ -625,7 +614,7 @@ gfxUtils::DrawPixelSnapped(gfxContext* aContext,
}
#endif
drawable->Draw(aContext, aFill, doTile, aFilter, userSpaceToImageSpace);
drawable->Draw(aContext, aRegion.Rect(), doTile, aFilter);
}
/* static */ int

View File

@ -25,6 +25,9 @@ namespace mozilla {
namespace layers {
struct PlanarYCbCrData;
}
namespace image {
class ImageRegion;
}
}
class gfxUtils {
@ -35,6 +38,7 @@ public:
typedef mozilla::gfx::Matrix Matrix;
typedef mozilla::gfx::SourceSurface SourceSurface;
typedef mozilla::gfx::SurfaceFormat SurfaceFormat;
typedef mozilla::image::ImageRegion ImageRegion;
/*
* Premultiply or Unpremultiply aSourceSurface, writing the result
@ -71,16 +75,13 @@ public:
* will tweak the rects and transforms that it gets from the pixel snapping
* algorithm before passing them on to this method.
*/
static void DrawPixelSnapped(gfxContext* aContext,
gfxDrawable* aDrawable,
const gfxMatrix& aUserSpaceToImageSpace,
const gfxRect& aSubimage,
const gfxRect& aSourceRect,
const gfxRect& aImageRect,
const gfxRect& aFill,
static void DrawPixelSnapped(gfxContext* aContext,
gfxDrawable* aDrawable,
const gfxSize& aImageSize,
const ImageRegion& aRegion,
const mozilla::gfx::SurfaceFormat aFormat,
GraphicsFilter aFilter,
uint32_t aImageFlags = imgIContainer::FLAG_NONE);
GraphicsFilter aFilter,
uint32_t aImageFlags = imgIContainer::FLAG_NONE);
/**
* Clip aContext to the region aRegion.

View File

@ -12,6 +12,7 @@
#include "gfxRect.h"
#include "GraphicsFilter.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/Maybe.h"
#include "mozilla/RefPtr.h"
#include "nsRect.h"
#include "nsSize.h"
@ -33,6 +34,7 @@ class SVGImageContext;
namespace mozilla {
namespace image {
class ImageRegion;
struct Orientation;
}
}
@ -50,10 +52,11 @@ native nsIntRectByVal(nsIntRect);
native nsSize(nsSize);
[ptr] native nsIFrame(nsIFrame);
[ptr] native ImageContainer(mozilla::layers::ImageContainer);
[ref] native ImageRegion(mozilla::image::ImageRegion);
[ptr] native LayerManager(mozilla::layers::LayerManager);
native Orientation(mozilla::image::Orientation);
[ref] native TimeStamp(mozilla::TimeStamp);
[ptr] native SVGImageContext(mozilla::SVGImageContext);
[ref] native MaybeSVGImageContext(mozilla::Maybe<mozilla::SVGImageContext>);
native TempRefSourceSurface(mozilla::TemporaryRef<mozilla::gfx::SourceSurface>);
native TempRefImgIContainer(already_AddRefed<imgIContainer>);
native nsIntSizeByVal(nsIntSize);
@ -66,7 +69,7 @@ native nsIntSizeByVal(nsIntSize);
*
* Internally, imgIContainer also manages animation of images.
*/
[scriptable, builtinclass, uuid(9c0f1884-dad9-4d63-a0e5-dda7b3b3416b)]
[scriptable, builtinclass, uuid(f8bb7671-5f36-490b-b828-3f4c6ad38665)]
interface imgIContainer : nsISupports
{
/**
@ -226,39 +229,113 @@ interface imgIContainer : nsISupports
[noscript] ImageContainer getImageContainer(in LayerManager aManager);
/**
* Draw a frame onto the context specified.
* Draw the requested frame of this image onto the context specified.
*
* Drawing an image involves scaling it to a certain size (which may be
* implemented as a "smart" scale by substituting an HQ-scaled frame or
* rendering at a high DPI), and then selecting a region of that image to
* draw. That region is drawn onto the graphics context and in the process
* transformed by the context matrix, which determines the final area that is
* filled. The basic process looks like this:
*
* +------------------+
* | Image |
* | |
* | intrinsic width |
* | X |
* | intrinsic height |
* +------------------+
* / \
* / \
* / (scale to aSize) \
* / \
* +----------------------------+
* | |
* | Scaled Image |
* | aSize.width X aSize.height |
* | |
* | +---------+ |
* | | aRegion | |
* | +---------+ |
* +-------(---------(----------+
* | |
* / \
* | (transform |
* / by aContext \
* | matrix) |
* / \
* +---------------------+
* | |
* | Fill Rect |
* | |
* +---------------------+
*
* The region may extend outside of the scaled image's boundaries. It's
* actually a region in tiled image space, which is formed by tiling the
* scaled image infinitely in every direction. Drawing with a region larger
* than the scaled image thus causes the filled area to contain multiple tiled
* copies of the image, which looks like this:
*
* ....................................................
* : : : :
* : Tile : Tile : Tile :
* : +------------[aRegion]------------+ :
* :........|.......:................:........|.......:
* : | : : | :
* : Ti|le : Scaled Image : Ti|le :
* : | : : | :
* :........|.......:................:........|.......:
* : +---------------------------------+ :
* : Ti|le : Tile : Ti|le :
* : / : : \ :
* :......(.........:................:..........).....:
* | |
* / \
* | (transform by aContext matrix) |
* / \
* +---------------------------------------------+
* | : : |
* |.....:.................................:.....|
* | : : |
* | : Tiled Fill : |
* | : : |
* |.....:.................................:.....|
* | : : |
* +---------------------------------------------+
*
*
* @param aContext The Thebes context to draw the image to.
* @param aFilter The filter to be used if we're scaling the image.
* @param aUserSpaceToImageSpace The transformation from user space (e.g.,
* appunits) to image space.
* @param aFill The area in the context to draw pixels to. When aFlags includes
* FLAG_CLAMP, the image will be extended to this area by clampling
* image sample coordinates. Otherwise, the image will be
* automatically tiled as necessary.
* @param aSubimage The area of the image, in pixels, that we are allowed to
* sample from.
* @param aViewportSize
* The size (in CSS pixels) of the viewport that would be available
* for the full image to occupy, if we were drawing the full image.
* (Note that we might not actually be drawing the full image -- we
* might be restricted by aSubimage -- but we still need the full
* image's viewport-size in order for SVG images with the "viewBox"
* attribute to position their content correctly.)
* @param aSVGContext If non-null, SVG-related rendering context such as
* overridden attributes on the image document's root <svg>
* node. Ignored for raster images.
* @param aSize The size to which the image should be scaled before drawing.
* This requirement may be satisfied using HQ scaled frames,
* selecting from different resolution layers, drawing at a
* higher DPI, or just performing additional scaling on the
* graphics context. Callers can use optimalImageSizeForDest()
* to determine the best choice for this parameter if they have
* no special size requirements.
* @param aRegion The region in tiled image space which will be drawn onto the
* graphics context. aRegion is in the coordinate space of the
* image after it has been scaled to aSize - that is, the image
* is scaled first, and then aRegion is applied. When aFlags
* includes FLAG_CLAMP, the image will be extended to this area
* by clamping image sample coordinates. Otherwise, the image
* will be automatically tiled as necessary. aRegion can also
* optionally contain a second region which restricts the set of
* pixels we're allowed to sample from when drawing; this is
* only of use to callers which need to draw with pixel snapping.
* @param aWhichFrame Frame specifier of the FRAME_* variety.
* @param aFilter The filter to be used if we're scaling the image.
* @param aSVGContext If specified, SVG-related rendering context, such as
* overridden attributes on the image document's root <svg>
* node, and the size of the viewport that the full image
* would occupy. Ignored for raster images.
* @param aFlags Flags of the FLAG_* variety
*/
[noscript] void draw(in gfxContext aContext,
in gfxGraphicsFilter aFilter,
[const] in gfxMatrix aUserSpaceToImageSpace,
[const] in gfxRect aFill,
[const] in nsIntRect aSubimage,
[const] in nsIntSize aViewportSize,
[const] in SVGImageContext aSVGContext,
[const] in nsIntSize aSize,
[const] in ImageRegion aRegion,
in uint32_t aWhichFrame,
in gfxGraphicsFilter aFilter,
[const] in MaybeSVGImageContext aSVGContext,
in uint32_t aFlags);
/*

View File

@ -3,7 +3,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <new> // Workaround for bug in VS10; see bug 981264.
#include <cmath>
#include <utility>
#include "gfxDrawable.h"
#include "gfxPlatform.h"
@ -11,17 +13,20 @@
#include "mozilla/gfx/2D.h"
#include "mozilla/RefPtr.h"
#include "ClippedImage.h"
#include "ImageRegion.h"
#include "Orientation.h"
#include "SVGImageContext.h"
#include "ClippedImage.h"
namespace mozilla {
using namespace gfx;
using layers::LayerManager;
using layers::ImageContainer;
using std::make_pair;
using std::modf;
using std::pair;
namespace image {
@ -29,12 +34,12 @@ class ClippedImageCachedSurface
{
public:
ClippedImageCachedSurface(TemporaryRef<SourceSurface> aSurface,
const nsIntSize& aViewportSize,
const SVGImageContext* aSVGContext,
const nsIntSize& aSize,
const Maybe<SVGImageContext>& aSVGContext,
float aFrame,
uint32_t aFlags)
: mSurface(aSurface)
, mViewportSize(aViewportSize)
, mSize(aSize)
, mFrame(aFrame)
, mFlags(aFlags)
{
@ -44,15 +49,13 @@ public:
}
}
bool Matches(const nsIntSize& aViewportSize,
const SVGImageContext* aSVGContext,
bool Matches(const nsIntSize& aSize,
const Maybe<SVGImageContext>& aSVGContext,
float aFrame,
uint32_t aFlags)
{
bool matchesSVGContext = (!aSVGContext && !mSVGContext) ||
(*aSVGContext == *mSVGContext);
return mViewportSize == aViewportSize &&
matchesSVGContext &&
return mSize == aSize &&
mSVGContext == aSVGContext &&
mFrame == aFrame &&
mFlags == aFlags;
}
@ -62,25 +65,23 @@ public:
}
private:
RefPtr<SourceSurface> mSurface;
const nsIntSize mViewportSize;
Maybe<SVGImageContext> mSVGContext;
const float mFrame;
const uint32_t mFlags;
RefPtr<SourceSurface> mSurface;
const nsIntSize mSize;
Maybe<SVGImageContext> mSVGContext;
const float mFrame;
const uint32_t mFlags;
};
class DrawSingleTileCallback : public gfxDrawingCallback
{
public:
DrawSingleTileCallback(ClippedImage* aImage,
const nsIntRect& aClip,
const nsIntSize& aViewportSize,
const SVGImageContext* aSVGContext,
const nsIntSize& aSize,
const Maybe<SVGImageContext>& aSVGContext,
uint32_t aWhichFrame,
uint32_t aFlags)
: mImage(aImage)
, mClip(aClip)
, mViewportSize(aViewportSize)
, mSize(aSize)
, mSVGContext(aSVGContext)
, mWhichFrame(aWhichFrame)
, mFlags(aFlags)
@ -93,21 +94,24 @@ public:
const GraphicsFilter& aFilter,
const gfxMatrix& aTransform)
{
MOZ_ASSERT(aTransform.IsIdentity(),
"Caller is probably CreateSamplingRestrictedDrawable, "
"which should not happen");
// Draw the image. |gfxCallbackDrawable| always calls this function with
// arguments that guarantee we never tile.
mImage->DrawSingleTile(aContext, aFilter, aTransform, aFillRect, mClip,
mViewportSize, mSVGContext, mWhichFrame, mFlags);
mImage->DrawSingleTile(aContext, mSize, ImageRegion::Create(aFillRect),
mWhichFrame, aFilter, mSVGContext, mFlags);
return true;
}
private:
nsRefPtr<ClippedImage> mImage;
const nsIntRect mClip;
const nsIntSize mViewportSize;
const SVGImageContext* mSVGContext;
const uint32_t mWhichFrame;
const uint32_t mFlags;
nsRefPtr<ClippedImage> mImage;
const nsIntSize mSize;
const Maybe<SVGImageContext>& mSVGContext;
const uint32_t mWhichFrame;
const uint32_t mFlags;
};
ClippedImage::ClippedImage(Image* aImage,
@ -220,12 +224,12 @@ NS_IMETHODIMP_(TemporaryRef<SourceSurface>)
ClippedImage::GetFrame(uint32_t aWhichFrame,
uint32_t aFlags)
{
return GetFrameInternal(mClip.Size(), nullptr, aWhichFrame, aFlags);
return GetFrameInternal(mClip.Size(), Nothing(), aWhichFrame, aFlags);
}
TemporaryRef<SourceSurface>
ClippedImage::GetFrameInternal(const nsIntSize& aViewportSize,
const SVGImageContext* aSVGContext,
ClippedImage::GetFrameInternal(const nsIntSize& aSize,
const Maybe<SVGImageContext>& aSVGContext,
uint32_t aWhichFrame,
uint32_t aFlags)
{
@ -234,13 +238,13 @@ ClippedImage::GetFrameInternal(const nsIntSize& aViewportSize,
}
float frameToDraw = InnerImage()->GetFrameIndex(aWhichFrame);
if (!mCachedSurface || !mCachedSurface->Matches(aViewportSize,
if (!mCachedSurface || !mCachedSurface->Matches(aSize,
aSVGContext,
frameToDraw,
aFlags)) {
// Create a surface to draw into.
RefPtr<DrawTarget> target = gfxPlatform::GetPlatform()->
CreateOffscreenContentDrawTarget(IntSize(mClip.width, mClip.height),
CreateOffscreenContentDrawTarget(IntSize(aSize.width, aSize.height),
SurfaceFormat::B8G8R8A8);
if (!target) {
NS_ERROR("Could not create a DrawTarget");
@ -251,22 +255,20 @@ ClippedImage::GetFrameInternal(const nsIntSize& aViewportSize,
// Create our callback.
nsRefPtr<gfxDrawingCallback> drawTileCallback =
new DrawSingleTileCallback(this, mClip, aViewportSize, aSVGContext, aWhichFrame, aFlags);
new DrawSingleTileCallback(this, aSize, aSVGContext, aWhichFrame, aFlags);
nsRefPtr<gfxDrawable> drawable =
new gfxCallbackDrawable(drawTileCallback, mClip.Size());
new gfxCallbackDrawable(drawTileCallback, aSize);
// Actually draw. The callback will end up invoking DrawSingleTile.
gfxRect imageRect(0, 0, mClip.width, mClip.height);
gfxUtils::DrawPixelSnapped(ctx, drawable, gfxMatrix(),
imageRect, imageRect, imageRect, imageRect,
gfxUtils::DrawPixelSnapped(ctx, drawable, aSize,
ImageRegion::Create(aSize),
SurfaceFormat::B8G8R8A8,
GraphicsFilter::FILTER_FAST);
GraphicsFilter::FILTER_FAST,
imgIContainer::FLAG_CLAMP);
// Cache the resulting surface.
mCachedSurface = new ClippedImageCachedSurface(target->Snapshot(),
aViewportSize,
aSVGContext,
frameToDraw,
mCachedSurface = new ClippedImageCachedSurface(target->Snapshot(), aSize,
aSVGContext, frameToDraw,
aFlags);
}
@ -291,123 +293,116 @@ ClippedImage::GetImageContainer(LayerManager* aManager, ImageContainer** _retval
return NS_OK;
}
bool
ClippedImage::MustCreateSurface(gfxContext* aContext,
const gfxMatrix& aTransform,
const gfxRect& aSourceRect,
const nsIntRect& aSubimage,
const uint32_t aFlags) const
static bool
MustCreateSurface(gfxContext* aContext,
const nsIntSize& aSize,
const ImageRegion& aRegion,
const uint32_t aFlags)
{
gfxRect gfxImageRect(0, 0, mClip.width, mClip.height);
nsIntRect intImageRect(0, 0, mClip.width, mClip.height);
bool willTile = !gfxImageRect.Contains(aSourceRect) &&
gfxRect imageRect(0, 0, aSize.width, aSize.height);
bool willTile = !imageRect.Contains(aRegion.Rect()) &&
!(aFlags & imgIContainer::FLAG_CLAMP);
bool willResample = (aContext->CurrentMatrix().HasNonIntegerTranslation() ||
aTransform.HasNonIntegerTranslation()) &&
(willTile || !aSubimage.Contains(intImageRect));
bool willResample = aContext->CurrentMatrix().HasNonIntegerTranslation() &&
(willTile || !aRegion.RestrictionContains(imageRect));
return willTile || willResample;
}
NS_IMETHODIMP
ClippedImage::Draw(gfxContext* aContext,
GraphicsFilter aFilter,
const gfxMatrix& aUserSpaceToImageSpace,
const gfxRect& aFill,
const nsIntRect& aSubimage,
const nsIntSize& aViewportSize,
const SVGImageContext* aSVGContext,
const nsIntSize& aSize,
const ImageRegion& aRegion,
uint32_t aWhichFrame,
GraphicsFilter aFilter,
const Maybe<SVGImageContext>& aSVGContext,
uint32_t aFlags)
{
if (!ShouldClip()) {
return InnerImage()->Draw(aContext, aFilter, aUserSpaceToImageSpace,
aFill, aSubimage, aViewportSize, aSVGContext,
aWhichFrame, aFlags);
return InnerImage()->Draw(aContext, aSize, aRegion, aWhichFrame,
aFilter, aSVGContext, aFlags);
}
// Check for tiling. If we need to tile then we need to create a
// gfxCallbackDrawable to handle drawing for us.
gfxRect sourceRect = aUserSpaceToImageSpace.Transform(aFill);
if (MustCreateSurface(aContext, aUserSpaceToImageSpace, sourceRect, aSubimage, aFlags)) {
if (MustCreateSurface(aContext, aSize, aRegion, aFlags)) {
// Create a temporary surface containing a single tile of this image.
// GetFrame will call DrawSingleTile internally.
RefPtr<SourceSurface> surface =
GetFrameInternal(aViewportSize, aSVGContext, aWhichFrame, aFlags);
GetFrameInternal(aSize, aSVGContext, aWhichFrame, aFlags);
NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);
// Create a drawable from that surface.
nsRefPtr<gfxSurfaceDrawable> drawable =
new gfxSurfaceDrawable(surface, gfxIntSize(mClip.width, mClip.height));
new gfxSurfaceDrawable(surface, aSize);
// Draw.
gfxRect imageRect(0, 0, mClip.width, mClip.height);
gfxRect subimage(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height);
gfxUtils::DrawPixelSnapped(aContext, drawable, aUserSpaceToImageSpace,
subimage, sourceRect, imageRect, aFill,
gfxUtils::DrawPixelSnapped(aContext, drawable, aSize, aRegion,
SurfaceFormat::B8G8R8A8, aFilter);
return NS_OK;
}
// Determine the appropriate subimage for the inner image.
nsIntRect innerSubimage(aSubimage);
innerSubimage.MoveBy(mClip.x, mClip.y);
innerSubimage.Intersect(mClip);
return DrawSingleTile(aContext, aFilter, aUserSpaceToImageSpace, aFill, innerSubimage,
aViewportSize, aSVGContext, aWhichFrame, aFlags);
return DrawSingleTile(aContext, aSize, aRegion, aWhichFrame,
aFilter, aSVGContext, aFlags);
}
gfxFloat
ClippedImage::ClampFactor(const gfxFloat aToClamp, const int aReference) const
static SVGImageContext
UnclipViewport(const SVGImageContext& aOldContext,
const pair<nsIntSize, nsIntSize>& aInnerAndClipSize)
{
return aToClamp > aReference ? aReference / aToClamp
: 1.0;
nsIntSize innerSize(aInnerAndClipSize.first);
nsIntSize clipSize(aInnerAndClipSize.second);
// Map the viewport to the inner image. (Note that we don't take the aSize
// parameter of Draw into account, just the clipping region.)
nsIntSize vSize(aOldContext.GetViewportSize());
vSize.width = ceil(vSize.width * double(innerSize.width) / clipSize.width);
vSize.height = ceil(vSize.height * double(innerSize.height) / clipSize.height);
return SVGImageContext(vSize,
aOldContext.GetPreserveAspectRatio());
}
nsresult
ClippedImage::DrawSingleTile(gfxContext* aContext,
GraphicsFilter aFilter,
const gfxMatrix& aUserSpaceToImageSpace,
const gfxRect& aFill,
const nsIntRect& aSubimage,
const nsIntSize& aViewportSize,
const SVGImageContext* aSVGContext,
const nsIntSize& aSize,
const ImageRegion& aRegion,
uint32_t aWhichFrame,
GraphicsFilter aFilter,
const Maybe<SVGImageContext>& aSVGContext,
uint32_t aFlags)
{
MOZ_ASSERT(!MustCreateSurface(aContext, aUserSpaceToImageSpace,
aUserSpaceToImageSpace.Transform(aFill),
aSubimage - nsIntPoint(mClip.x, mClip.y), aFlags),
"DrawSingleTile shouldn't need to create a surface");
MOZ_ASSERT(!MustCreateSurface(aContext, aSize, aRegion, aFlags),
"Shouldn't need to create a surface");
// Make the viewport reflect the original image's size.
nsIntSize viewportSize(aViewportSize);
int32_t imgWidth, imgHeight;
if (NS_SUCCEEDED(InnerImage()->GetWidth(&imgWidth)) &&
NS_SUCCEEDED(InnerImage()->GetHeight(&imgHeight))) {
viewportSize = nsIntSize(imgWidth, imgHeight);
gfxRect clip(mClip.x, mClip.y, mClip.width, mClip.height);
nsIntSize size(aSize), innerSize(aSize);
if (NS_SUCCEEDED(InnerImage()->GetWidth(&innerSize.width)) &&
NS_SUCCEEDED(InnerImage()->GetHeight(&innerSize.height))) {
double scaleX = aSize.width / clip.width;
double scaleY = aSize.height / clip.height;
// Map the clip and size to the scale requested by the caller.
clip.Scale(scaleX, scaleY);
size = innerSize;
size.Scale(scaleX, scaleY);
} else {
MOZ_ASSERT(false, "If ShouldClip() led us to draw then we should never get here");
}
// Add a translation to the transform to reflect the clipping region.
gfxMatrix transform =
aUserSpaceToImageSpace * gfxMatrix::Translation(mClip.x, mClip.y);
// We restrict our drawing to only the clipping region, and translate so that
// the clipping region is placed at the position the caller expects.
ImageRegion region(aRegion);
region.MoveBy(clip.x, clip.y);
region = region.Intersect(clip);
// "Clamp the source rectangle" to the clipping region's width and height.
// Really, this means modifying the transform to get the results we want.
gfxRect sourceRect = transform.Transform(aFill);
if (sourceRect.width > mClip.width || sourceRect.height > mClip.height) {
gfxMatrix clampSource = gfxMatrix::Translation(sourceRect.TopLeft());
clampSource.Scale(ClampFactor(sourceRect.width, mClip.width),
ClampFactor(sourceRect.height, mClip.height));
clampSource.Translate(-sourceRect.TopLeft());
transform *= clampSource;
}
gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
aContext->Multiply(gfxMatrix::Translation(-clip.x, -clip.y));
return InnerImage()->Draw(aContext, aFilter, transform, aFill, aSubimage,
viewportSize, aSVGContext, aWhichFrame, aFlags);
return InnerImage()->Draw(aContext, size, region,
aWhichFrame, aFilter,
aSVGContext.map(UnclipViewport,
make_pair(innerSize, mClip.Size())),
aFlags);
}
NS_IMETHODIMP

View File

@ -42,13 +42,11 @@ public:
NS_IMETHOD GetImageContainer(layers::LayerManager* aManager,
layers::ImageContainer** _retval) MOZ_OVERRIDE;
NS_IMETHOD Draw(gfxContext* aContext,
GraphicsFilter aFilter,
const gfxMatrix& aUserSpaceToImageSpace,
const gfxRect& aFill,
const nsIntRect& aSubimage,
const nsIntSize& aViewportSize,
const SVGImageContext* aSVGContext,
const nsIntSize& aSize,
const ImageRegion& aRegion,
uint32_t aWhichFrame,
GraphicsFilter aFilter,
const Maybe<SVGImageContext>& aSVGContext,
uint32_t aFlags) MOZ_OVERRIDE;
NS_IMETHOD RequestDiscard() MOZ_OVERRIDE;
NS_IMETHOD_(Orientation) GetOrientation() MOZ_OVERRIDE;
@ -65,25 +63,17 @@ protected:
private:
TemporaryRef<SourceSurface>
GetFrameInternal(const nsIntSize& aViewportSize,
const SVGImageContext* aSVGContext,
GetFrameInternal(const nsIntSize& aSize,
const Maybe<SVGImageContext>& aSVGContext,
uint32_t aWhichFrame,
uint32_t aFlags);
bool ShouldClip();
bool MustCreateSurface(gfxContext* aContext,
const gfxMatrix& aTransform,
const gfxRect& aSourceRect,
const nsIntRect& aSubimage,
const uint32_t aFlags) const;
gfxFloat ClampFactor(const gfxFloat aToClamp, const int aReference) const;
nsresult DrawSingleTile(gfxContext* aContext,
GraphicsFilter aFilter,
const gfxMatrix& aUserSpaceToImageSpace,
const gfxRect& aFill,
const nsIntRect& aSubimage,
const nsIntSize& aViewportSize,
const SVGImageContext* aSVGContext,
const nsIntSize& aSize,
const ImageRegion& aRegion,
uint32_t aWhichFrame,
GraphicsFilter aFilter,
const Maybe<SVGImageContext>& aSVGContext,
uint32_t aFlags);
// If we are forced to draw a temporary surface, we cache it here.

View File

@ -8,7 +8,9 @@
#include "gfxUtils.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/RefPtr.h"
#include "ImageRegion.h"
#include "Orientation.h"
#include "SVGImageContext.h"
#include "mozilla/MemoryReporting.h"
@ -217,10 +219,9 @@ DynamicImage::GetFrame(uint32_t aWhichFrame,
SurfaceFormat::B8G8R8A8);
nsRefPtr<gfxContext> context = new gfxContext(dt);
nsresult rv = Draw(context, GraphicsFilter::FILTER_NEAREST, gfxMatrix(),
gfxRect(0, 0, size.width, size.height),
nsIntRect(0, 0, size.width, size.height),
size, nullptr, aWhichFrame, aFlags);
nsresult rv = Draw(context, size, ImageRegion::Create(size),
aWhichFrame, GraphicsFilter::FILTER_NEAREST,
Nothing(), aFlags);
NS_ENSURE_SUCCESS(rv, nullptr);
return dt->Snapshot();
@ -243,23 +244,34 @@ DynamicImage::GetImageContainer(LayerManager* aManager, ImageContainer** _retval
NS_IMETHODIMP
DynamicImage::Draw(gfxContext* aContext,
GraphicsFilter aFilter,
const gfxMatrix& aUserSpaceToImageSpace,
const gfxRect& aFill,
const nsIntRect& aSubimage,
const nsIntSize& aViewportSize,
const SVGImageContext* aSVGContext,
const nsIntSize& aSize,
const ImageRegion& aRegion,
uint32_t aWhichFrame,
GraphicsFilter aFilter,
const Maybe<SVGImageContext>& aSVGContext,
uint32_t aFlags)
{
MOZ_ASSERT(!aSize.IsEmpty(), "Unexpected empty size");
gfxIntSize drawableSize(mDrawable->Size());
gfxRect imageRect(0, 0, drawableSize.width, drawableSize.height);
gfxRect sourceRect = aUserSpaceToImageSpace.TransformBounds(aFill);
gfxUtils::DrawPixelSnapped(aContext, mDrawable, aUserSpaceToImageSpace,
aSubimage, sourceRect, imageRect, aFill,
SurfaceFormat::B8G8R8A8, aFilter, aFlags);
if (aSize == drawableSize) {
gfxUtils::DrawPixelSnapped(aContext, mDrawable, drawableSize, aRegion,
SurfaceFormat::B8G8R8A8, aFilter);
return NS_OK;
}
gfxSize scale(double(aSize.width) / drawableSize.width,
double(aSize.height) / drawableSize.height);
ImageRegion region(aRegion);
region.Scale(1.0 / scale.width, 1.0 / scale.height);
gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
aContext->Multiply(gfxMatrix::Scaling(scale.width, scale.height));
gfxUtils::DrawPixelSnapped(aContext, mDrawable, drawableSize, region,
SurfaceFormat::B8G8R8A8, aFilter);
return NS_OK;
}

View File

@ -72,18 +72,15 @@ FrozenImage::GetImageContainer(layers::LayerManager* aManager,
NS_IMETHODIMP
FrozenImage::Draw(gfxContext* aContext,
GraphicsFilter aFilter,
const gfxMatrix& aUserSpaceToImageSpace,
const gfxRect& aFill,
const nsIntRect& aSubimage,
const nsIntSize& aViewportSize,
const SVGImageContext* aSVGContext,
const nsIntSize& aSize,
const ImageRegion& aRegion,
uint32_t /* aWhichFrame - ignored */,
GraphicsFilter aFilter,
const Maybe<SVGImageContext>& aSVGContext,
uint32_t aFlags)
{
return InnerImage()->Draw(aContext, aFilter, aUserSpaceToImageSpace,
aFill, aSubimage, aViewportSize, aSVGContext,
FRAME_FIRST, aFlags);
return InnerImage()->Draw(aContext, aSize, aRegion, FRAME_FIRST,
aFilter, aSVGContext, aFlags);
}
NS_IMETHODIMP_(void)

View File

@ -42,13 +42,11 @@ public:
NS_IMETHOD GetImageContainer(layers::LayerManager* aManager,
layers::ImageContainer** _retval) MOZ_OVERRIDE;
NS_IMETHOD Draw(gfxContext* aContext,
GraphicsFilter aFilter,
const gfxMatrix& aUserSpaceToImageSpace,
const gfxRect& aFill,
const nsIntRect& aSubimage,
const nsIntSize& aViewportSize,
const SVGImageContext* aSVGContext,
const nsIntSize& aSize,
const ImageRegion& aRegion,
uint32_t aWhichFrame,
GraphicsFilter aFilter,
const Maybe<SVGImageContext>& aSVGContext,
uint32_t aFlags) MOZ_OVERRIDE;
NS_IMETHOD_(void) RequestRefresh(const TimeStamp& aTime) MOZ_OVERRIDE;
NS_IMETHOD GetAnimationMode(uint16_t* aAnimationMode) MOZ_OVERRIDE;

158
image/src/ImageRegion.h Normal file
View File

@ -0,0 +1,158 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_IMAGELIB_IMAGE_REGION_H_
#define MOZILLA_IMAGELIB_IMAGE_REGION_H_
#include "gfxRect.h"
namespace mozilla {
namespace image {
/**
* An axis-aligned rectangle in tiled image space, with an optional sampling
* restriction rect. The drawing code ensures that if a sampling restriction
* rect is present, any pixels sampled during the drawing process are found
* within that rect.
*
* The sampling restriction rect exists primarily for callers which perform
* pixel snapping. Other callers should generally use one of the Create()
* overloads.
*/
class ImageRegion
{
public:
static ImageRegion Empty()
{
return ImageRegion(gfxRect());
}
static ImageRegion Create(const gfxRect& aRect)
{
return ImageRegion(aRect);
}
static ImageRegion Create(const gfxSize& aSize)
{
return ImageRegion(gfxRect(0, 0, aSize.width, aSize.height));
}
static ImageRegion Create(const nsIntSize& aSize)
{
return ImageRegion(gfxRect(0, 0, aSize.width, aSize.height));
}
static ImageRegion CreateWithSamplingRestriction(const gfxRect& aRect,
const gfxRect& aRestriction)
{
return ImageRegion(aRect, aRestriction);
}
bool IsRestricted() const { return mIsRestricted; }
const gfxRect& Rect() const { return mRect; }
const gfxRect& Restriction() const
{
MOZ_ASSERT(mIsRestricted);
return mRestriction;
}
bool RestrictionContains(const gfxRect& aRect) const
{
if (!mIsRestricted) {
return true;
}
return mRestriction.Contains(aRect);
}
ImageRegion Intersect(const gfxRect& aRect) const
{
if (mIsRestricted) {
return CreateWithSamplingRestriction(aRect.Intersect(mRect),
aRect.Intersect(mRestriction));
}
return Create(aRect.Intersect(mRect));
}
gfxRect IntersectAndRestrict(const gfxRect& aRect) const
{
gfxRect intersection = mRect.Intersect(aRect);
if (mIsRestricted) {
intersection = mRestriction.Intersect(intersection);
}
return intersection;
}
void MoveBy(gfxFloat dx, gfxFloat dy)
{
mRect.MoveBy(dx, dy);
if (mIsRestricted) {
mRestriction.MoveBy(dx, dy);
}
}
void Scale(gfxFloat sx, gfxFloat sy)
{
mRect.Scale(sx, sy);
if (mIsRestricted) {
mRestriction.Scale(sx, sy);
}
}
void TransformBy(const gfxMatrix& aMatrix)
{
mRect = aMatrix.Transform(mRect);
if (mIsRestricted) {
mRestriction = aMatrix.Transform(mRestriction);
}
}
void TransformBoundsBy(const gfxMatrix& aMatrix)
{
mRect = aMatrix.TransformBounds(mRect);
if (mIsRestricted) {
mRestriction = aMatrix.TransformBounds(mRestriction);
}
}
ImageRegion operator-(const gfxPoint& aPt) const
{
if (mIsRestricted) {
return CreateWithSamplingRestriction(mRect - aPt, mRestriction - aPt);
}
return Create(mRect - aPt);
}
ImageRegion operator+(const gfxPoint& aPt) const
{
if (mIsRestricted) {
return CreateWithSamplingRestriction(mRect + aPt, mRestriction + aPt);
}
return Create(mRect + aPt);
}
/* ImageRegion() : mIsRestricted(false) { } */
private:
explicit ImageRegion(const gfxRect& aRect)
: mRect(aRect)
, mIsRestricted(false)
{ }
ImageRegion(const gfxRect& aRect, const gfxRect& aRestriction)
: mRect(aRect)
, mRestriction(aRestriction)
, mIsRestricted(true)
{ }
gfxRect mRect;
gfxRect mRestriction;
bool mIsRestricted;
};
} // namespace image
} // namespace mozilla
#endif

View File

@ -222,18 +222,15 @@ ImageWrapper::GetImageContainer(LayerManager* aManager, ImageContainer** _retval
NS_IMETHODIMP
ImageWrapper::Draw(gfxContext* aContext,
GraphicsFilter aFilter,
const gfxMatrix& aUserSpaceToImageSpace,
const gfxRect& aFill,
const nsIntRect& aSubimage,
const nsIntSize& aViewportSize,
const SVGImageContext* aSVGContext,
const nsIntSize& aSize,
const ImageRegion& aRegion,
uint32_t aWhichFrame,
GraphicsFilter aFilter,
const Maybe<SVGImageContext>& aSVGContext,
uint32_t aFlags)
{
return mInnerImage->Draw(aContext, aFilter, aUserSpaceToImageSpace, aFill,
aSubimage, aViewportSize, aSVGContext, aWhichFrame,
aFlags);
return mInnerImage->Draw(aContext, aSize, aRegion, aWhichFrame,
aFilter, aSVGContext, aFlags);
}
NS_IMETHODIMP

View File

@ -5,9 +5,12 @@
#include <algorithm>
#include "gfx2DGlue.h"
#include "gfxDrawable.h"
#include "gfxPlatform.h"
#include "gfxUtils.h"
#include "ImageRegion.h"
#include "SVGImageContext.h"
#include "OrientedImage.h"
@ -26,12 +29,24 @@ NS_IMPL_ISUPPORTS_INHERITED0(OrientedImage, ImageWrapper)
nsIntRect
OrientedImage::FrameRect(uint32_t aWhichFrame)
{
if (mOrientation.SwapsWidthAndHeight()) {
nsIntRect innerRect = InnerImage()->FrameRect(aWhichFrame);
return nsIntRect(innerRect.x, innerRect.y, innerRect.height, innerRect.width);
} else {
return InnerImage()->FrameRect(aWhichFrame);
nsresult rv;
// Retrieve the frame rect of the inner image.
nsIntRect innerRect = InnerImage()->FrameRect(aWhichFrame);
if (mOrientation.IsIdentity()) {
return innerRect;
}
// Get the underlying image's dimensions.
nsIntSize size;
rv = InnerImage()->GetWidth(&size.width);
NS_ENSURE_SUCCESS(rv, innerRect);
rv = InnerImage()->GetHeight(&size.height);
NS_ENSURE_SUCCESS(rv, innerRect);
// Transform the frame rect.
gfxRect finalRect = OrientationMatrix(size).TransformBounds(innerRect);
return nsIntRect(finalRect.x, finalRect.y, finalRect.width, finalRect.height);
}
NS_IMETHODIMP
@ -89,14 +104,10 @@ OrientedImage::GetFrame(uint32_t aWhichFrame,
}
// Get the underlying dimensions.
int32_t width, height;
rv = InnerImage()->GetWidth(&width);
gfxIntSize size;
rv = InnerImage()->GetWidth(&size.width);
NS_ENSURE_SUCCESS(rv, nullptr);
rv = InnerImage()->GetHeight(&height);
NS_ENSURE_SUCCESS(rv, nullptr);
if (mOrientation.SwapsWidthAndHeight()) {
swap(width, height);
}
rv = InnerImage()->GetHeight(&size.height);
NS_ENSURE_SUCCESS(rv, nullptr);
// Determine an appropriate format for the surface.
@ -110,7 +121,7 @@ OrientedImage::GetFrame(uint32_t aWhichFrame,
// Create a surface to draw into.
RefPtr<DrawTarget> target =
gfxPlatform::GetPlatform()->
CreateOffscreenContentDrawTarget(IntSize(width, height), surfaceFormat);
CreateOffscreenContentDrawTarget(ToIntSize(size), surfaceFormat);
if (!target) {
NS_ERROR("Could not create a DrawTarget");
return nullptr;
@ -122,13 +133,13 @@ OrientedImage::GetFrame(uint32_t aWhichFrame,
InnerImage()->GetFrame(aWhichFrame, aFlags);
NS_ENSURE_TRUE(innerSurface, nullptr);
nsRefPtr<gfxDrawable> drawable =
new gfxSurfaceDrawable(innerSurface, gfxIntSize(width, height));
new gfxSurfaceDrawable(innerSurface, size);
// Draw.
nsRefPtr<gfxContext> ctx = new gfxContext(target);
gfxRect imageRect(0, 0, width, height);
gfxUtils::DrawPixelSnapped(ctx, drawable, OrientationMatrix(nsIntSize(width, height)),
imageRect, imageRect, imageRect, imageRect,
ctx->Multiply(OrientationMatrix(size));
gfxUtils::DrawPixelSnapped(ctx, drawable, size,
ImageRegion::Create(size),
surfaceFormat, GraphicsFilter::FILTER_FAST);
return target->Snapshot();
@ -151,104 +162,151 @@ OrientedImage::GetImageContainer(LayerManager* aManager, ImageContainer** _retva
return NS_OK;
}
gfxMatrix
OrientedImage::OrientationMatrix(const nsIntSize& aViewportSize)
struct MatrixBuilder
{
gfxMatrix matrix;
MatrixBuilder(bool aInvert) : mInvert(aInvert) { }
int32_t width, height;
if (InnerImage()->GetType() == imgIContainer::TYPE_VECTOR) {
width = mOrientation.SwapsWidthAndHeight() ? aViewportSize.height : aViewportSize.width;
height = mOrientation.SwapsWidthAndHeight() ? aViewportSize.width : aViewportSize.height;
gfxMatrix Build() { return mMatrix; }
void Scale(gfxFloat aX, gfxFloat aY) {
if (mInvert) {
mMatrix *= gfxMatrix::Scaling(1.0 / aX, 1.0 / aY);
} else {
nsresult rv = InnerImage()->GetWidth(&width);
rv = NS_FAILED(rv) ? rv : InnerImage()->GetHeight(&height);
if (NS_FAILED(rv)) {
// Fall back to identity if the width and height aren't available.
return matrix;
mMatrix.Scale(aX, aY);
}
}
void Rotate(gfxFloat aPhi) {
if (mInvert) {
mMatrix *= gfxMatrix::Rotation(-aPhi);
} else {
mMatrix.Rotate(aPhi);
}
}
void Translate(gfxPoint aDelta) {
if (mInvert) {
mMatrix *= gfxMatrix::Translation(-aDelta);
} else {
mMatrix.Translate(aDelta);
}
}
private:
gfxMatrix mMatrix;
bool mInvert;
};
/*
* OrientationMatrix() computes a matrix that applies the rotation and
* reflection specified by mOrientation, or that matrix's inverse if aInvert is
* true.
*
* @param aSize The scaled size of the inner image. (When outside code specifies
* the scaled size, as with imgIContainer::Draw and its aSize
* parameter, it's necessary to swap the width and height if
* mOrientation.SwapsWidthAndHeight() is true.)
* @param aInvert If true, compute the inverse of the orientation matrix. Prefer
* this approach to OrientationMatrix(..).Invert(), because it's
* more numerically accurate.
*/
gfxMatrix
OrientedImage::OrientationMatrix(const nsIntSize& aSize,
bool aInvert /* = false */)
{
MatrixBuilder builder(aInvert);
// Apply reflection, if present. (This logically happens second, but we
// apply it first because these transformations are all premultiplied.) A
// translation is necessary to place the image back in the first quadrant.
switch (mOrientation.flip) {
case Flip::Unflipped:
break;
case Flip::Horizontal:
if (mOrientation.SwapsWidthAndHeight()) {
builder.Translate(gfxPoint(aSize.height, 0));
} else {
builder.Translate(gfxPoint(aSize.width, 0));
}
}
builder.Scale(-1.0, 1.0);
break;
default:
MOZ_ASSERT(false, "Invalid flip value");
}
// Apply reflection, if present. (This logically happens second, but we
// apply it first because these transformations are all premultiplied.) A
// translation is necessary to place the image back in the first quadrant.
switch (mOrientation.flip) {
case Flip::Unflipped:
break;
case Flip::Horizontal:
if (mOrientation.SwapsWidthAndHeight()) {
// In the coordinate system after the rotation the reflection we want
// is across the x-axis rather than the y-axis.
matrix.Translate(gfxPoint(0, height));
matrix.Scale(1.0, -1.0);
} else {
matrix.Translate(gfxPoint(width, 0));
matrix.Scale(-1.0, 1.0);
}
break;
default:
MOZ_ASSERT(false, "Invalid flip value");
}
// Apply rotation, if present. Again, a translation is used to place the
// image back in the first quadrant.
switch (mOrientation.rotation) {
case Angle::D0:
break;
case Angle::D90:
builder.Translate(gfxPoint(aSize.height, 0));
builder.Rotate(-1.5 * M_PI);
break;
case Angle::D180:
builder.Translate(gfxPoint(aSize.width, aSize.height));
builder.Rotate(-1.0 * M_PI);
break;
case Angle::D270:
builder.Translate(gfxPoint(0, aSize.width));
builder.Rotate(-0.5 * M_PI);
break;
default:
MOZ_ASSERT(false, "Invalid rotation value");
}
// Apply rotation, if present. Again, a translation is used to place the
// image back in the first quadrant.
switch (mOrientation.rotation) {
case Angle::D0:
break;
case Angle::D90:
matrix.Translate(gfxPoint(0, height));
matrix.Rotate(-0.5 * M_PI);
break;
case Angle::D180:
matrix.Translate(gfxPoint(width, height));
matrix.Rotate(-1.0 * M_PI);
break;
case Angle::D270:
matrix.Translate(gfxPoint(width, 0));
matrix.Rotate(-1.5 * M_PI);
break;
default:
MOZ_ASSERT(false, "Invalid rotation value");
}
return builder.Build();
}
return matrix;
static SVGImageContext
OrientViewport(const SVGImageContext& aOldContext,
const Orientation& aOrientation)
{
nsIntSize viewportSize(aOldContext.GetViewportSize());
if (aOrientation.SwapsWidthAndHeight()) {
swap(viewportSize.width, viewportSize.height);
}
return SVGImageContext(viewportSize,
aOldContext.GetPreserveAspectRatio());
}
NS_IMETHODIMP
OrientedImage::Draw(gfxContext* aContext,
GraphicsFilter aFilter,
const gfxMatrix& aUserSpaceToImageSpace,
const gfxRect& aFill,
const nsIntRect& aSubimage,
const nsIntSize& aViewportSize,
const SVGImageContext* aSVGContext,
const nsIntSize& aSize,
const ImageRegion& aRegion,
uint32_t aWhichFrame,
GraphicsFilter aFilter,
const Maybe<SVGImageContext>& aSVGContext,
uint32_t aFlags)
{
if (mOrientation.IsIdentity()) {
return InnerImage()->Draw(aContext, aFilter, aUserSpaceToImageSpace,
aFill, aSubimage, aViewportSize, aSVGContext,
aWhichFrame, aFlags);
return InnerImage()->Draw(aContext, aSize, aRegion,
aWhichFrame, aFilter, aSVGContext, aFlags);
}
// Update the matrix to reorient the image.
gfxMatrix matrix(OrientationMatrix(aViewportSize));
gfxMatrix userSpaceToImageSpace(aUserSpaceToImageSpace * matrix);
// Update the subimage.
gfxRect gfxSubimage(matrix.TransformBounds(gfxRect(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height)));
nsIntRect subimage(gfxSubimage.x, gfxSubimage.y, gfxSubimage.width, gfxSubimage.height);
// Update the viewport size. (This could be done using TransformBounds but
// since it's only a size a swap is enough.)
nsIntSize viewportSize(aViewportSize);
// Update the image size to match the image's coordinate system. (This could
// be done using TransformBounds but since it's only a size a swap is enough.)
nsIntSize size(aSize);
if (mOrientation.SwapsWidthAndHeight()) {
swap(viewportSize.width, viewportSize.height);
swap(size.width, size.height);
}
return InnerImage()->Draw(aContext, aFilter, userSpaceToImageSpace,
aFill, subimage, viewportSize, aSVGContext,
aWhichFrame, aFlags);
// Update the matrix so that we transform the image into the orientation
// expected by the caller before drawing.
gfxMatrix matrix(OrientationMatrix(size));
gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
aContext->Multiply(matrix);
// The region is already in the orientation expected by the caller, but we
// need it to be in the image's coordinate system, so we transform it using
// the inverse of the orientation matrix.
gfxMatrix inverseMatrix(OrientationMatrix(size, /* aInvert = */ true));
ImageRegion region(aRegion);
region.TransformBoundsBy(inverseMatrix);
return InnerImage()->Draw(aContext, size, region, aWhichFrame, aFilter,
aSVGContext.map(OrientViewport, mOrientation),
aFlags);
}
nsIntSize
@ -277,19 +335,16 @@ OrientedImage::GetImageSpaceInvalidationRect(const nsIntRect& aRect)
return rect;
}
int32_t width, height;
nsresult rv = InnerImage()->GetWidth(&width);
rv = NS_FAILED(rv) ? rv : InnerImage()->GetHeight(&height);
nsIntSize innerSize;
nsresult rv = InnerImage()->GetWidth(&innerSize.width);
rv = NS_FAILED(rv) ? rv : InnerImage()->GetHeight(&innerSize.height);
if (NS_FAILED(rv)) {
// Fall back to identity if the width and height aren't available.
return rect;
}
// Transform the invalidation rect into the correct orientation.
gfxMatrix matrix(OrientationMatrix(nsIntSize(width, height)));
if (!matrix.Invert()) {
return nsIntRect();
}
gfxMatrix matrix(OrientationMatrix(innerSize, /* aInvert = */ true));
gfxRect invalidRect(matrix.TransformBounds(gfxRect(rect.x, rect.y,
rect.width, rect.height)));
invalidRect.RoundOut();

View File

@ -39,13 +39,11 @@ public:
NS_IMETHOD GetImageContainer(layers::LayerManager* aManager,
layers::ImageContainer** _retval) MOZ_OVERRIDE;
NS_IMETHOD Draw(gfxContext* aContext,
GraphicsFilter aFilter,
const gfxMatrix& aUserSpaceToImageSpace,
const gfxRect& aFill,
const nsIntRect& aSubimage,
const nsIntSize& aViewportSize,
const SVGImageContext* aSVGContext,
const nsIntSize& aSize,
const ImageRegion& aRegion,
uint32_t aWhichFrame,
GraphicsFilter aFilter,
const Maybe<SVGImageContext>& aSVGContext,
uint32_t aFlags) MOZ_OVERRIDE;
NS_IMETHOD_(nsIntRect) GetImageSpaceInvalidationRect(const nsIntRect& aRect) MOZ_OVERRIDE;
nsIntSize OptimalImageSizeForDest(const gfxSize& aDest,
@ -61,7 +59,7 @@ protected:
virtual ~OrientedImage() { }
gfxMatrix OrientationMatrix(const nsIntSize& aViewportSize);
gfxMatrix OrientationMatrix(const nsIntSize& aSize, bool aInvert = false);
private:
Orientation mOrientation;

View File

@ -18,6 +18,7 @@
#include "prenv.h"
#include "prsystem.h"
#include "ImageContainer.h"
#include "ImageRegion.h"
#include "Layers.h"
#include "nsPresContext.h"
#include "nsIThreadPool.h"
@ -185,21 +186,19 @@ DiscardingEnabled()
class ScaleRequest
{
public:
ScaleRequest(RasterImage* aImage, const gfxSize& aScale, imgFrame* aSrcFrame)
: scale(aScale)
ScaleRequest(RasterImage* aImage,
const nsIntSize& aSize,
imgFrame* aSrcFrame)
: dstSize(aSize)
, dstLocked(false)
, done(false)
, stopped(false)
{
MOZ_ASSERT(!aSrcFrame->GetIsPaletted());
MOZ_ASSERT(aScale.width > 0 && aScale.height > 0);
MOZ_ASSERT(aSize.width > 0 && aSize.height > 0);
weakImage = aImage;
srcRect = aSrcFrame->GetRect();
nsIntRect dstRect = srcRect;
dstRect.ScaleRoundOut(scale.width, scale.height);
dstSize = dstRect.Size();
}
// This can only be called on the main thread.
@ -277,11 +276,10 @@ public:
RefPtr<SourceSurface> dstSurface;
// Below are the values that may be touched on the scaling thread.
gfxSize scale;
uint8_t* srcData;
uint8_t* dstData;
nsIntRect srcRect;
gfxIntSize dstSize;
nsIntSize dstSize;
uint32_t srcStride;
uint32_t dstStride;
SurfaceFormat srcFormat;
@ -328,9 +326,11 @@ private: /* members */
class ScaleRunner : public nsRunnable
{
public:
ScaleRunner(RasterImage* aImage, const gfxSize& aScale, imgFrame* aSrcFrame)
ScaleRunner(RasterImage* aImage,
const nsIntSize& aSize,
imgFrame* aSrcFrame)
{
nsAutoPtr<ScaleRequest> request(new ScaleRequest(aImage, aScale, aSrcFrame));
nsAutoPtr<ScaleRequest> request(new ScaleRequest(aImage, aSize, aSrcFrame));
// Destination is unconditionally ARGB32 because that's what the scaler
// outputs.
@ -2508,7 +2508,7 @@ RasterImage::SyncDecode()
}
bool
RasterImage::CanQualityScale(const gfxSize& scale)
RasterImage::CanQualityScale(const gfx::Size& scale)
{
// If target size is 1:1 with original, don't scale.
if (scale.width == 1.0 && scale.height == 1.0)
@ -2525,8 +2525,7 @@ RasterImage::CanQualityScale(const gfxSize& scale)
}
bool
RasterImage::CanScale(GraphicsFilter aFilter,
gfxSize aScale, uint32_t aFlags)
RasterImage::CanScale(GraphicsFilter aFilter, gfx::Size aScale, uint32_t aFlags)
{
// The high-quality scaler requires Skia.
#ifdef MOZ_ENABLE_SKIA
@ -2550,7 +2549,7 @@ void
RasterImage::ScalingStart(ScaleRequest* request)
{
MOZ_ASSERT(request);
mScaleResult.scale = request->scale;
mScaleResult.scaledSize = request->dstSize;
mScaleResult.status = SCALE_PENDING;
mScaleRequest = request;
}
@ -2573,7 +2572,7 @@ RasterImage::ScalingDone(ScaleRequest* request, ScaleStatus status)
mScaleResult.status = SCALE_DONE;
mScaleResult.frame = scaledFrame;
mScaleResult.scale = request->scale;
mScaleResult.scaledSize = request->dstSize;
} else {
mScaleResult.status = SCALE_INVALID;
mScaleResult.frame = nullptr;
@ -2588,7 +2587,7 @@ RasterImage::ScalingDone(ScaleRequest* request, ScaleStatus status)
}
void
RasterImage::RequestScale(imgFrame* aFrame, gfxSize aScale)
RasterImage::RequestScale(imgFrame* aFrame, nsIntSize aSize)
{
// We can't store more than one scaled version of an image right now, so if
// there's more than one instance of this image, bail.
@ -2607,7 +2606,7 @@ RasterImage::RequestScale(imgFrame* aFrame, gfxSize aScale)
mScaleRequest->stopped = true;
}
nsRefPtr<ScaleRunner> runner = new ScaleRunner(this, aScale, aFrame);
nsRefPtr<ScaleRunner> runner = new ScaleRunner(this, aSize, aFrame);
if (runner->IsOK()) {
if (!sScaleWorkerThread) {
NS_NewNamedThread("Image Scaler", getter_AddRefs(sScaleWorkerThread));
@ -2623,22 +2622,18 @@ RasterImage::RequestScale(imgFrame* aFrame, gfxSize aScale)
bool
RasterImage::DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
gfxContext *aContext,
const nsIntSize& aSize,
const ImageRegion& aRegion,
GraphicsFilter aFilter,
const gfxMatrix &aUserSpaceToImageSpace,
const gfxRect &aFill,
const nsIntRect &aSubimage,
uint32_t aFlags)
{
imgFrame *frame = aFrame;
nsIntRect framerect = frame->GetRect();
gfxMatrix userSpaceToImageSpace = aUserSpaceToImageSpace;
gfxMatrix imageSpaceToUserSpace = aUserSpaceToImageSpace;
if (!imageSpaceToUserSpace.Invert()) {
return false;
}
gfxSize scale = imageSpaceToUserSpace.ScaleFactors(true);
nsIntRect subimage = aSubimage;
gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
RefPtr<SourceSurface> surf;
gfx::Size scale(double(aSize.width) / mSize.width,
double(aSize.height) / mSize.height);
if (CanScale(aFilter, scale, aFlags) && !frame->IsSinglePixel()) {
// If scale factor is still the same that we scaled for and
@ -2650,38 +2645,41 @@ RasterImage::DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
// The solution is to cache more than one scaled image frame
// for each RasterImage.
bool needScaleReq;
if (mScaleResult.status == SCALE_DONE && mScaleResult.scale == scale) {
if (mScaleResult.status == SCALE_DONE && mScaleResult.scaledSize == aSize) {
// Grab and hold the surface to make sure the OS didn't destroy it
surf = mScaleResult.frame->GetSurface();
needScaleReq = !surf;
if (surf) {
frame = mScaleResult.frame;
userSpaceToImageSpace *= gfxMatrix::Scaling(scale.width, scale.height);
// Since we're switching to a scaled image, we need to transform the
// area of the subimage to draw accordingly, since imgFrame::Draw()
// doesn't know about scaled frames.
subimage.ScaleRoundOut(scale.width, scale.height);
}
} else {
needScaleReq = !(mScaleResult.status == SCALE_PENDING &&
mScaleResult.scale == scale);
mScaleResult.scaledSize == aSize);
}
// If we're not waiting for exactly this result, and there's only one
// instance of this image on this page, ask for a scale.
if (needScaleReq) {
RequestScale(frame, scale);
RequestScale(frame, aSize);
}
}
ImageRegion region(aRegion);
// By now we may have a frame with the requested size. If not, we need to
// adjust the drawing parameters accordingly.
nsIntSize finalFrameSize(frame->GetRect().Size());
if (finalFrameSize != aSize) {
aContext->Multiply(gfxMatrix::Scaling(scale.width, scale.height));
region.Scale(1.0 / scale.width, 1.0 / scale.height);
}
nsIntMargin padding(framerect.y,
mSize.width - framerect.XMost(),
mSize.height - framerect.YMost(),
framerect.x);
return frame->Draw(aContext, aFilter, userSpaceToImageSpace,
aFill, padding, subimage, aFlags);
return frame->Draw(aContext, region, padding, aFilter, aFlags);
}
//******************************************************************************
@ -2695,14 +2693,12 @@ RasterImage::DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
* in uint32_t aWhichFrame,
* in uint32_t aFlags); */
NS_IMETHODIMP
RasterImage::Draw(gfxContext *aContext,
GraphicsFilter aFilter,
const gfxMatrix &aUserSpaceToImageSpace,
const gfxRect &aFill,
const nsIntRect &aSubimage,
const nsIntSize& /*aViewportSize - ignored*/,
const SVGImageContext* /*aSVGContext - ignored*/,
RasterImage::Draw(gfxContext* aContext,
const nsIntSize& aSize,
const ImageRegion& aRegion,
uint32_t aWhichFrame,
GraphicsFilter aFilter,
const Maybe<SVGImageContext>& /*aSVGContext - ignored*/,
uint32_t aFlags)
{
if (aWhichFrame > FRAME_MAX_VALUE)
@ -2773,9 +2769,8 @@ RasterImage::Draw(gfxContext *aContext,
return NS_OK; // Getting the frame (above) touches the image and kicks off decoding
}
bool drawn = DrawWithPreDownscaleIfNeeded(frame, aContext, aFilter,
aUserSpaceToImageSpace, aFill,
aSubimage, aFlags);
bool drawn = DrawWithPreDownscaleIfNeeded(frame, aContext, aSize,
aRegion, aFilter, aFlags);
if (!drawn) {
// The OS threw out some or all of our buffer. Start decoding again.
ForceDiscard();
@ -3684,11 +3679,11 @@ RasterImage::OptimalImageSizeForDest(const gfxSize& aDest, uint32_t aWhichFrame,
}
nsIntSize destSize(ceil(aDest.width), ceil(aDest.height));
gfxSize scale(double(destSize.width) / mSize.width,
double(destSize.height) / mSize.height);
gfx::Size scale(double(destSize.width) / mSize.width,
double(destSize.height) / mSize.height);
if (CanScale(aFilter, scale, aFlags)) {
if (mScaleResult.scale == scale) {
if (mScaleResult.scaledSize == destSize) {
if (mScaleResult.status == SCALE_DONE) {
return destSize; // We have an existing HQ scale for this size.
} else if (mScaleResult.status == SCALE_PENDING) {
@ -3702,7 +3697,7 @@ RasterImage::OptimalImageSizeForDest(const gfxSize& aDest, uint32_t aWhichFrame,
imgFrame* frame = GetDrawableImgFrame(frameIndex);
if (frame) {
RequestScale(frame, scale);
RequestScale(frame, destSize);
}
}

View File

@ -330,7 +330,7 @@ public:
private:
// Initiates an HQ scale for the given frame, if possible.
void RequestScale(imgFrame* aFrame, gfxSize aScale);
void RequestScale(imgFrame* aFrame, nsIntSize aScale);
already_AddRefed<imgStatusTracker> CurrentStatusTracker()
{
@ -558,10 +558,9 @@ private:
bool DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
gfxContext *aContext,
const nsIntSize& aSize,
const ImageRegion& aRegion,
GraphicsFilter aFilter,
const gfxMatrix &aUserSpaceToImageSpace,
const gfxRect &aFill,
const nsIntRect &aSubimage,
uint32_t aFlags);
TemporaryRef<gfx::SourceSurface> CopyFrame(uint32_t aWhichFrame,
@ -739,8 +738,8 @@ private: // data
bool IsDecodeFinished();
TimeStamp mDrawStartTime;
inline bool CanQualityScale(const gfxSize& scale);
inline bool CanScale(GraphicsFilter aFilter, gfxSize aScale, uint32_t aFlags);
inline bool CanQualityScale(const gfx::Size& scale);
inline bool CanScale(GraphicsFilter aFilter, gfx::Size aScale, uint32_t aFlags);
struct ScaleResult
{
@ -748,7 +747,7 @@ private: // data
: status(SCALE_INVALID)
{}
gfxSize scale;
nsIntSize scaledSize;
nsAutoPtr<imgFrame> frame;
ScaleStatus status;
};

View File

@ -10,6 +10,7 @@
#ifndef MOZILLA_IMAGELIB_SURFACECACHE_H_
#define MOZILLA_IMAGELIB_SURFACECACHE_H_
#include "mozilla/Maybe.h" // for Maybe
#include "mozilla/HashFunctions.h" // for HashGeneric and AddToHash
#include "gfxPoint.h" // for gfxSize
#include "nsCOMPtr.h" // for already_AddRefed
@ -46,28 +47,19 @@ class SurfaceKey
typedef gfx::IntSize IntSize;
public:
SurfaceKey(const IntSize& aSize,
const gfxSize aScale,
const SVGImageContext* aSVGContext,
const Maybe<SVGImageContext>& aSVGContext,
const float aAnimationTime,
const uint32_t aFlags)
: mSize(aSize)
, mScale(aScale)
, mSVGContextIsValid(aSVGContext != nullptr)
, mSVGContext(aSVGContext)
, mAnimationTime(aAnimationTime)
, mFlags(aFlags)
{
// XXX(seth): Would love to use Maybe<T> here, but see bug 913586.
if (mSVGContextIsValid)
mSVGContext = *aSVGContext;
}
{ }
bool operator==(const SurfaceKey& aOther) const
{
bool matchesSVGContext = aOther.mSVGContextIsValid == mSVGContextIsValid &&
(!mSVGContextIsValid || aOther.mSVGContext == mSVGContext);
return aOther.mSize == mSize &&
aOther.mScale == mScale &&
matchesSVGContext &&
aOther.mSVGContext == mSVGContext &&
aOther.mAnimationTime == mAnimationTime &&
aOther.mFlags == mFlags;
}
@ -75,8 +67,7 @@ public:
uint32_t Hash() const
{
uint32_t hash = HashGeneric(mSize.width, mSize.height);
hash = AddToHash(hash, mScale.width, mScale.height);
hash = AddToHash(hash, mSVGContextIsValid, mSVGContext.Hash());
hash = AddToHash(hash, mSVGContext.map(HashSIC).valueOr(0));
hash = AddToHash(hash, mAnimationTime, mFlags);
return hash;
}
@ -84,12 +75,14 @@ public:
IntSize Size() const { return mSize; }
private:
IntSize mSize;
gfxSize mScale;
SVGImageContext mSVGContext;
bool mSVGContextIsValid;
float mAnimationTime;
uint32_t mFlags;
static uint32_t HashSIC(const SVGImageContext& aSIC) {
return aSIC.Hash();
}
IntSize mSize;
Maybe<SVGImageContext> mSVGContext;
float mAnimationTime;
uint32_t mFlags;
};
/**

View File

@ -26,6 +26,7 @@
#include "nsStubDocumentObserver.h"
#include "nsSVGEffects.h" // for nsSVGRenderingObserver
#include "nsWindowMemoryReporter.h"
#include "ImageRegion.h"
#include "Orientation.h"
#include "SVGDocumentWrapper.h"
#include "nsIDOMEventListener.h"
@ -241,12 +242,12 @@ class SVGDrawingCallback : public gfxDrawingCallback {
public:
SVGDrawingCallback(SVGDocumentWrapper* aSVGDocumentWrapper,
const nsIntRect& aViewport,
const gfxSize& aScale,
uint32_t aImageFlags) :
mSVGDocumentWrapper(aSVGDocumentWrapper),
mViewport(aViewport),
mScale(aScale),
mImageFlags(aImageFlags)
const IntSize& aSize,
uint32_t aImageFlags)
: mSVGDocumentWrapper(aSVGDocumentWrapper)
, mViewport(aViewport)
, mSize(aSize)
, mImageFlags(aImageFlags)
{}
virtual bool operator()(gfxContext* aContext,
const gfxRect& aFillRect,
@ -254,9 +255,9 @@ public:
const gfxMatrix& aTransform);
private:
nsRefPtr<SVGDocumentWrapper> mSVGDocumentWrapper;
const nsIntRect mViewport;
const gfxSize mScale;
uint32_t mImageFlags;
const nsIntRect mViewport;
const IntSize mSize;
uint32_t mImageFlags;
};
// Based loosely on nsSVGIntegrationUtils' PaintFrameCallback::operator()
@ -283,13 +284,13 @@ SVGDrawingCallback::operator()(gfxContext* aContext,
aContext->Rectangle(aFillRect);
aContext->Clip();
gfxContextMatrixAutoSaveRestore contextMatrixRestorer(aContext);
gfxMatrix matrix = aTransform;
if (!matrix.Invert()) {
return false;
}
aContext->Multiply(matrix);
aContext->Scale(1.0 / mScale.width, 1.0 / mScale.height);
aContext->Scale(double(mSize.width) / mViewport.width,
double(mSize.height) / mViewport.height);
nsPresContext* presContext = presShell->GetPresContext();
MOZ_ASSERT(presContext, "pres shell w/out pres context");
@ -741,11 +742,10 @@ VectorImage::GetFrame(uint32_t aWhichFrame,
nsRefPtr<gfxContext> context = new gfxContext(dt);
nsresult rv = Draw(context, GraphicsFilter::FILTER_NEAREST, gfxMatrix(),
gfxRect(gfxPoint(0,0), gfxIntSize(imageIntSize.width,
imageIntSize.height)),
nsIntRect(nsIntPoint(0,0), imageIntSize),
imageIntSize, nullptr, aWhichFrame, aFlags);
nsresult rv = Draw(context, imageIntSize,
ImageRegion::Create(imageIntSize),
aWhichFrame, GraphicsFilter::FILTER_NEAREST,
Nothing(), aFlags);
NS_ENSURE_SUCCESS(rv, nullptr);
return dt->Snapshot();
@ -764,60 +764,32 @@ VectorImage::GetImageContainer(LayerManager* aManager,
struct SVGDrawingParameters
{
SVGDrawingParameters(gfxContext* aContext,
const nsIntSize& aSize,
const ImageRegion& aRegion,
GraphicsFilter aFilter,
const gfxMatrix& aUserSpaceToImageSpace,
const gfxRect& aFill,
const nsIntRect& aSubimage,
const nsIntSize& aViewportSize,
const SVGImageContext* aSVGContext,
const Maybe<SVGImageContext>& aSVGContext,
float aAnimationTime,
uint32_t aFlags)
: context(aContext)
, size(aSize.width, aSize.height)
, imageRect(0, 0, aSize.width, aSize.height)
, region(aRegion)
, filter(aFilter)
, fill(aFill)
, viewportSize(aViewportSize)
, animationTime(aAnimationTime)
, svgContext(aSVGContext)
, viewportSize(aSVGContext ? aSVGContext->GetViewportSize() : aSize)
, animationTime(aAnimationTime)
, flags(aFlags)
{
// gfxUtils::DrawPixelSnapped may rasterize this image to a temporary surface
// if we hit the tiling path. Unfortunately, the temporary surface isn't
// created at the size at which we'll ultimately draw, causing fuzzy output.
// To fix this we pre-apply the transform's scaling to the drawing parameters
// and remove the scaling from the transform, so the fact that temporary
// surfaces won't take the scaling into account doesn't matter. (Bug 600207.)
scale = aUserSpaceToImageSpace.ScaleFactors(true);
gfxPoint translation(aUserSpaceToImageSpace.GetTranslation());
{ }
// Remove the scaling from the transform.
gfxMatrix unscale;
unscale.Translate(gfxPoint(translation.x / scale.width,
translation.y / scale.height));
unscale.Scale(1.0 / scale.width, 1.0 / scale.height);
unscale.Translate(-translation);
userSpaceToImageSpace = aUserSpaceToImageSpace * unscale;
// Rescale drawing parameters.
IntSize drawableSize(aViewportSize.width / scale.width,
aViewportSize.height / scale.height);
sourceRect = userSpaceToImageSpace.Transform(aFill);
imageRect = IntRect(IntPoint(0, 0), drawableSize);
subimage = gfxRect(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height);
subimage.ScaleRoundOut(1.0 / scale.width, 1.0 / scale.height);
}
gfxContext* context;
GraphicsFilter filter;
gfxMatrix userSpaceToImageSpace;
gfxRect fill;
gfxRect subimage;
gfxRect sourceRect;
IntRect imageRect;
nsIntSize viewportSize;
gfxSize scale;
float animationTime;
const SVGImageContext* svgContext;
uint32_t flags;
gfxContext* context;
IntSize size;
IntRect imageRect;
ImageRegion region;
GraphicsFilter filter;
const Maybe<SVGImageContext>& svgContext;
nsIntSize viewportSize;
float animationTime;
uint32_t flags;
};
//******************************************************************************
@ -832,13 +804,11 @@ struct SVGDrawingParameters
* in uint32_t aFlags); */
NS_IMETHODIMP
VectorImage::Draw(gfxContext* aContext,
GraphicsFilter aFilter,
const gfxMatrix& aUserSpaceToImageSpace,
const gfxRect& aFill,
const nsIntRect& aSubimage,
const nsIntSize& aViewportSize,
const SVGImageContext* aSVGContext,
const nsIntSize& aSize,
const ImageRegion& aRegion,
uint32_t aWhichFrame,
GraphicsFilter aFilter,
const Maybe<SVGImageContext>& aSVGContext,
uint32_t aFlags)
{
if (aWhichFrame > FRAME_MAX_VALUE)
@ -866,20 +836,19 @@ VectorImage::Draw(gfxContext* aContext,
mSVGDocumentWrapper->GetRootSVGElem());
// Pack up the drawing parameters.
SVGDrawingParameters params(aContext, aFilter, aUserSpaceToImageSpace, aFill,
aSubimage, aViewportSize, aSVGContext, animTime, aFlags);
SVGDrawingParameters params(aContext, aSize, aRegion, aFilter,
aSVGContext, animTime, aFlags);
// Check the cache. (The FLAG_BYPASS_SURFACE_CACHE check here is just an
// optimization since the flags are part of the cache key and we never put
// surfaces in the cache if the flags contain FLAG_BYPASS_SURFACE_CACHE.)
nsRefPtr<gfxDrawable> drawable;
if (!(aFlags & FLAG_BYPASS_SURFACE_CACHE)) {
drawable =
SurfaceCache::Lookup(ImageKey(this),
SurfaceKey(params.imageRect.Size(), params.scale,
aSVGContext, animTime, aFlags));
if (aFlags & FLAG_BYPASS_SURFACE_CACHE) {
CreateDrawableAndShow(params);
return NS_OK;
}
nsRefPtr<gfxDrawable> drawable =
SurfaceCache::Lookup(ImageKey(this),
SurfaceKey(params.size, aSVGContext,
animTime, aFlags));
// Draw.
if (drawable) {
Show(drawable, params);
@ -899,24 +868,26 @@ VectorImage::CreateDrawableAndShow(const SVGDrawingParameters& aParams)
nsRefPtr<gfxDrawingCallback> cb =
new SVGDrawingCallback(mSVGDocumentWrapper,
nsIntRect(nsIntPoint(0, 0), aParams.viewportSize),
aParams.scale,
aParams.size,
aParams.flags);
nsRefPtr<gfxDrawable> svgDrawable =
new gfxCallbackDrawable(cb, ThebesIntSize(aParams.imageRect.Size()));
new gfxCallbackDrawable(cb, ThebesIntSize(aParams.size));
bool bypassCache = bool(aParams.flags & FLAG_BYPASS_SURFACE_CACHE) ||
// Refuse to cache animated images:
// XXX(seth): We may remove this restriction in bug 922893.
mHaveAnimations ||
// The image is too big to fit in the cache:
!SurfaceCache::CanHold(aParams.imageRect.Size());
!SurfaceCache::CanHold(aParams.size);
if (bypassCache)
return Show(svgDrawable, aParams);
// Try to create an offscreen surface.
RefPtr<gfx::DrawTarget> target =
gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(aParams.imageRect.Size(), gfx::SurfaceFormat::B8G8R8A8);
gfxPlatform::GetPlatform()->
CreateOffscreenContentDrawTarget(aParams.size,
gfx::SurfaceFormat::B8G8R8A8);
// If we couldn't create the draw target, it was probably because it would end
// up way too big. Generally it also wouldn't fit in the cache, but the prefs
@ -927,28 +898,25 @@ VectorImage::CreateDrawableAndShow(const SVGDrawingParameters& aParams)
nsRefPtr<gfxContext> ctx = new gfxContext(target);
// Actually draw. (We use FILTER_NEAREST since we never scale here.)
gfxUtils::DrawPixelSnapped(ctx, svgDrawable, gfxMatrix(),
ThebesIntRect(aParams.imageRect),
ThebesIntRect(aParams.imageRect),
ThebesIntRect(aParams.imageRect),
ThebesIntRect(aParams.imageRect),
nsIntRect imageRect(ThebesIntRect(aParams.imageRect));
gfxUtils::DrawPixelSnapped(ctx, svgDrawable,
ThebesIntSize(aParams.size),
ImageRegion::Create(imageRect),
SurfaceFormat::B8G8R8A8,
GraphicsFilter::FILTER_NEAREST, aParams.flags);
RefPtr<SourceSurface> surface = target->Snapshot();
// Attempt to cache the resulting surface.
SurfaceCache::Insert(surface,
ImageKey(this),
SurfaceKey(aParams.imageRect.Size(), aParams.scale,
aParams.svgContext, aParams.animationTime,
aParams.flags));
SurfaceCache::Insert(surface, ImageKey(this),
SurfaceKey(aParams.size, aParams.svgContext,
aParams.animationTime, aParams.flags));
// Draw. Note that if SurfaceCache::Insert failed for whatever reason,
// then |target| is all that is keeping the pixel data alive, so we have
// to draw before returning from this function.
nsRefPtr<gfxDrawable> drawable =
new gfxSurfaceDrawable(surface, ThebesIntSize(aParams.imageRect.Size()));
new gfxSurfaceDrawable(surface, ThebesIntSize(aParams.size));
Show(drawable, aParams);
}
@ -958,9 +926,8 @@ VectorImage::Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams)
{
MOZ_ASSERT(aDrawable, "Should have a gfxDrawable by now");
gfxUtils::DrawPixelSnapped(aParams.context, aDrawable,
aParams.userSpaceToImageSpace,
aParams.subimage, aParams.sourceRect,
ThebesIntRect(aParams.imageRect), aParams.fill,
ThebesIntSize(aParams.size),
aParams.region,
SurfaceFormat::B8G8R8A8,
aParams.filter, aParams.flags);

View File

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "imgFrame.h"
#include "ImageRegion.h"
#include "DiscardTracker.h"
#include "prenv.h"
@ -315,12 +316,10 @@ imgFrame::SurfaceWithFormat
imgFrame::SurfaceForDrawing(bool aDoPadding,
bool aDoPartialDecode,
bool aDoTile,
gfxContext* aContext,
const nsIntMargin& aPadding,
gfxMatrix& aUserSpaceToImageSpace,
gfxRect& aFill,
gfxRect& aSubimage,
gfxRect& aSourceRect,
gfxRect& aImageRect,
ImageRegion& aRegion,
SourceSurface* aSurface)
{
IntSize size(int32_t(aImageRect.Width()), int32_t(aImageRect.Height()));
@ -341,23 +340,15 @@ imgFrame::SurfaceForDrawing(bool aDoPadding,
if (!target)
return SurfaceWithFormat();
Rect fillRect(aFill.x, aFill.y, aFill.width, aFill.height);
// Fill 'available' with whatever we've got
if (mSinglePixel) {
target->FillRect(fillRect, ColorPattern(mSinglePixelColor),
target->FillRect(ToRect(aRegion.Rect()), ColorPattern(mSinglePixelColor),
DrawOptions(1.0f, CompositionOp::OP_SOURCE));
} else {
gfxMatrix imageSpaceToUserSpace = aUserSpaceToImageSpace;
imageSpaceToUserSpace.Invert();
SurfacePattern pattern(aSurface,
ExtendMode::REPEAT,
Matrix(imageSpaceToUserSpace._11,
imageSpaceToUserSpace._21,
imageSpaceToUserSpace._12,
imageSpaceToUserSpace._22,
imageSpaceToUserSpace._31,
imageSpaceToUserSpace._32));
target->FillRect(fillRect, pattern);
ToMatrix(aContext->CurrentMatrix()));
target->FillRect(ToRect(aRegion.Rect()), pattern);
}
RefPtr<SourceSurface> newsurf = target->Snapshot();
@ -366,15 +357,9 @@ imgFrame::SurfaceForDrawing(bool aDoPadding,
// Not tiling, and we have a surface, so we can account for
// padding and/or a partial decode just by twiddling parameters.
// First, update our user-space fill rect.
aSourceRect = aSourceRect.Intersect(available);
gfxMatrix imageSpaceToUserSpace = aUserSpaceToImageSpace;
imageSpaceToUserSpace.Invert();
aFill = imageSpaceToUserSpace.Transform(aSourceRect);
aSubimage = aSubimage.Intersect(available) - gfxPoint(aPadding.left, aPadding.top);
aUserSpaceToImageSpace *= gfxMatrix::Translation(-aPadding.left, -aPadding.top);
aSourceRect = aSourceRect - gfxPoint(aPadding.left, aPadding.top);
gfxPoint paddingTopLeft(aPadding.left, aPadding.top);
aRegion = aRegion.Intersect(available) - paddingTopLeft;
aContext->Multiply(gfxMatrix::Translation(paddingTopLeft));
aImageRect = gfxRect(0, 0, mSize.width, mSize.height);
gfxIntSize availableSize(mDecoded.width, mDecoded.height);
@ -382,16 +367,17 @@ imgFrame::SurfaceForDrawing(bool aDoPadding,
mFormat);
}
bool imgFrame::Draw(gfxContext *aContext, GraphicsFilter aFilter,
const gfxMatrix &aUserSpaceToImageSpace, const gfxRect& aFill,
const nsIntMargin &aPadding, const nsIntRect &aSubimage,
bool imgFrame::Draw(gfxContext* aContext, const ImageRegion& aRegion,
const nsIntMargin& aPadding, GraphicsFilter aFilter,
uint32_t aImageFlags)
{
PROFILER_LABEL("imgFrame", "Draw",
js::ProfileEntry::Category::GRAPHICS);
NS_ASSERTION(!aFill.IsEmpty(), "zero dest size --- fix caller");
NS_ASSERTION(!aSubimage.IsEmpty(), "zero source size --- fix caller");
NS_ASSERTION(!aRegion.Rect().IsEmpty(), "Drawing empty region!");
NS_ASSERTION(!aRegion.IsRestricted() ||
!aRegion.Rect().Intersect(aRegion.Restriction()).IsEmpty(),
"We must be allowed to sample *some* source pixels!");
NS_ASSERTION(!mPalettedImageData, "Directly drawing a paletted image!");
bool doPadding = aPadding != nsIntMargin(0,0,0,0);
@ -402,40 +388,32 @@ bool imgFrame::Draw(gfxContext *aContext, GraphicsFilter aFilter,
return true;
}
RefPtr<DrawTarget> dt = aContext->GetDrawTarget();
dt->FillRect(ToRect(aFill),
dt->FillRect(ToRect(aRegion.Rect()),
ColorPattern(mSinglePixelColor),
DrawOptions(1.0f,
CompositionOpForOp(aContext->CurrentOperator())));
return true;
}
gfxMatrix userSpaceToImageSpace = aUserSpaceToImageSpace;
gfxRect sourceRect = userSpaceToImageSpace.TransformBounds(aFill);
gfxRect imageRect(0, 0, mSize.width + aPadding.LeftRight(),
mSize.height + aPadding.TopBottom());
gfxRect subimage(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height);
gfxRect fill = aFill;
NS_ASSERTION(!sourceRect.Intersect(subimage).IsEmpty(),
"We must be allowed to sample *some* source pixels!");
RefPtr<SourceSurface> surf = GetSurface();
if (!surf) {
return false;
}
bool doTile = !imageRect.Contains(sourceRect) &&
bool doTile = !imageRect.Contains(aRegion.Rect()) &&
!(aImageFlags & imgIContainer::FLAG_CLAMP);
ImageRegion region(aRegion);
SurfaceWithFormat surfaceResult =
SurfaceForDrawing(doPadding, doPartialDecode, doTile, aPadding,
userSpaceToImageSpace, fill, subimage, sourceRect,
imageRect, surf);
SurfaceForDrawing(doPadding, doPartialDecode, doTile, aContext,
aPadding, imageRect, region, surf);
if (surfaceResult.IsValid()) {
gfxUtils::DrawPixelSnapped(aContext, surfaceResult.mDrawable,
userSpaceToImageSpace,
subimage, sourceRect, imageRect, fill,
surfaceResult.mFormat, aFilter, aImageFlags);
imageRect.Size(), region, surfaceResult.mFormat,
aFilter, aImageFlags);
}
return true;
}

View File

@ -16,6 +16,8 @@
namespace mozilla {
namespace image {
class ImageRegion;
class imgFrame
{
typedef gfx::Color Color;
@ -31,10 +33,9 @@ public:
nsresult Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, SurfaceFormat aFormat, uint8_t aPaletteDepth = 0);
nsresult Optimize();
bool Draw(gfxContext *aContext, GraphicsFilter aFilter,
const gfxMatrix &aUserSpaceToImageSpace, const gfxRect& aFill,
const nsIntMargin &aPadding, const nsIntRect &aSubimage,
uint32_t aImageFlags = imgIContainer::FLAG_NONE);
bool Draw(gfxContext* aContext, const ImageRegion& aRegion,
const nsIntMargin& aPadding, GraphicsFilter aFilter,
uint32_t aImageFlags);
nsresult ImageUpdated(const nsIntRect &aUpdateRect);
@ -111,12 +112,10 @@ private: // methods
SurfaceWithFormat SurfaceForDrawing(bool aDoPadding,
bool aDoPartialDecode,
bool aDoTile,
gfxContext* aContext,
const nsIntMargin& aPadding,
gfxMatrix& aUserSpaceToImageSpace,
gfxRect& aFill,
gfxRect& aSubimage,
gfxRect& aSourceRect,
gfxRect& aImageRect,
ImageRegion& aRegion,
SourceSurface* aSurface);
private: // data

View File

@ -4,13 +4,13 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ImageLogging.h"
#include "imgRequestProxy.h"
#include "imgIOnloadBlocker.h"
#include "Image.h"
#include "ImageOps.h"
#include "nsError.h"
#include "ImageLogging.h"
#include "nsCRTGlue.h"
#include "imgINotificationObserver.h"
#include "nsNetUtil.h"

View File

@ -4,13 +4,13 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ImageLogging.h"
#include "imgStatusTracker.h"
#include "imgIContainer.h"
#include "imgRequestProxy.h"
#include "imgDecoderObserver.h"
#include "Image.h"
#include "ImageLogging.h"
#include "nsNetUtil.h"
#include "nsIObserverService.h"

View File

@ -6,6 +6,7 @@
EXPORTS += [
'ImageOps.h',
'ImageRegion.h',
'imgLoader.h',
'imgRequest.h',
'imgRequestProxy.h',

View File

@ -20,78 +20,80 @@
#
# Also note that Mac OS X has its own system-level downscaling algorithm, so
# tests here may need Mac-specific "fuzzy-if(cocoaWidget,...)" annotations.
# Similarly, modern versions of Windows have slightly different downscaling
# behavior than other platforms, and may require "fuzzy-if(winWidget,...)".
# RUN TESTS NOT AFFECTED BY HIGH QUALITY DOWNSCALING:
# ===================================================
fails == downscale-svg-1a.html downscale-svg-1-ref.html?80
fails == downscale-svg-1b.html downscale-svg-1-ref.html?72
fails == downscale-svg-1c.html downscale-svg-1-ref.html?64
fails == downscale-svg-1d.html downscale-svg-1-ref.html?53
fails == downscale-svg-1e.html downscale-svg-1-ref.html?40
fails == downscale-svg-1f.html downscale-svg-1-ref.html?24
== downscale-svg-1a.html downscale-svg-1-ref.html?80
fuzzy(80,468) == downscale-svg-1b.html downscale-svg-1-ref.html?72
== downscale-svg-1c.html downscale-svg-1-ref.html?64
fuzzy(17,208) == downscale-svg-1d.html downscale-svg-1-ref.html?53
fuzzy(78,216) == downscale-svg-1e.html downscale-svg-1-ref.html?40
fuzzy(51,90) == downscale-svg-1f.html downscale-svg-1-ref.html?24
# RUN TESTS WITH HIGH QUALITY DOWNSCALING DISABLED:
# =================================================
default-preferences pref(image.high_quality_downscaling.enabled,false)
fuzzy-if(cocoaWidget,106,31) == downscale-1.html downscale-1-ref.html
fuzzy-if(winWidget,16,20) fuzzy-if(cocoaWidget,106,31) == downscale-1.html downscale-1-ref.html
fuzzy(5,999) random != downscale-2a.html?203,52,left about:blank
fuzzy(5,999) random != downscale-2b.html?203,52,left about:blank
fuzzy(5,999) random != downscale-2c.html?203,52,left about:blank
fuzzy(5,999) random != downscale-2d.html?203,52,left about:blank
fuzzy(5,999) random != downscale-2e.html?203,52,left about:blank
fuzzy(5,999) random != downscale-2f.html?203,52,left about:blank
fuzzy(20,999) != downscale-2a.html?203,52,left about:blank
fuzzy(20,999) != downscale-2b.html?203,52,left about:blank
fuzzy(20,999) != downscale-2c.html?203,52,left about:blank
fuzzy(20,999) != downscale-2d.html?203,52,left about:blank
fuzzy(20,999) != downscale-2e.html?203,52,left about:blank
fuzzy(20,999) fails-if(!cocoaWidget) != downscale-2f.html?203,52,left about:blank
fuzzy(5,999) random != downscale-2a.html?205,53,left about:blank
fuzzy(5,999) random != downscale-2b.html?205,53,left about:blank
fuzzy(5,999) random != downscale-2c.html?205,53,left about:blank
fuzzy(5,999) random != downscale-2d.html?205,53,left about:blank
fuzzy(5,999) random != downscale-2e.html?205,53,left about:blank
fuzzy(5,999) random != downscale-2f.html?205,53,left about:blank
fuzzy(20,999) != downscale-2a.html?205,53,left about:blank
fuzzy(20,999) != downscale-2b.html?205,53,left about:blank
fuzzy(20,999) != downscale-2c.html?205,53,left about:blank
fuzzy(20,999) != downscale-2d.html?205,53,left about:blank
fuzzy(20,999) != downscale-2e.html?205,53,left about:blank
fuzzy(20,999) fails-if(!cocoaWidget) != downscale-2f.html?205,53,left about:blank
fuzzy(5,999) random != downscale-2a.html?203,52,right about:blank
fuzzy(5,999) random != downscale-2b.html?203,52,right about:blank
fuzzy(5,999) random != downscale-2c.html?203,52,right about:blank
fuzzy(5,999) random != downscale-2d.html?203,52,right about:blank
fuzzy(5,999) random != downscale-2e.html?203,52,right about:blank
fuzzy(5,999) random != downscale-2f.html?203,52,right about:blank
fuzzy(20,999) != downscale-2a.html?203,52,right about:blank
fuzzy(20,999) != downscale-2b.html?203,52,right about:blank
fuzzy(20,999) != downscale-2c.html?203,52,right about:blank
fuzzy(20,999) != downscale-2d.html?203,52,right about:blank
fuzzy(20,999) != downscale-2e.html?203,52,right about:blank
fuzzy(20,999) random-if(!cocoaWidget) != downscale-2f.html?203,52,right about:blank
fuzzy(5,999) random != downscale-2a.html?205,53,right about:blank
fuzzy(5,999) random != downscale-2b.html?205,53,right about:blank
fuzzy(5,999) random != downscale-2c.html?205,53,right about:blank
fuzzy(5,999) random != downscale-2d.html?205,53,right about:blank
fuzzy(5,999) random != downscale-2e.html?205,53,right about:blank
fuzzy(5,999) random != downscale-2f.html?205,53,right about:blank
fuzzy(20,999) != downscale-2a.html?205,53,right about:blank
fuzzy(20,999) != downscale-2b.html?205,53,right about:blank
fuzzy(20,999) != downscale-2c.html?205,53,right about:blank
fuzzy(20,999) != downscale-2d.html?205,53,right about:blank
fuzzy(20,999) != downscale-2e.html?205,53,right about:blank
fuzzy(20,999) random-if(!cocoaWidget) != downscale-2f.html?205,53,right about:blank
fuzzy(5,999) random != downscale-2a.html?203,52,top about:blank
fuzzy(5,999) random != downscale-2b.html?203,52,top about:blank
fuzzy(5,999) random != downscale-2c.html?203,52,top about:blank
fuzzy(5,999) random != downscale-2d.html?203,52,top about:blank
fuzzy(5,999) random != downscale-2e.html?203,52,top about:blank
fuzzy(5,999) random != downscale-2f.html?203,52,top about:blank
fuzzy(20,999) != downscale-2a.html?203,52,top about:blank
fuzzy(20,999) != downscale-2b.html?203,52,top about:blank
fuzzy(20,999) != downscale-2c.html?203,52,top about:blank
fuzzy(20,999) != downscale-2d.html?203,52,top about:blank
fuzzy(20,999) != downscale-2e.html?203,52,top about:blank
fuzzy(20,999) fails-if(!cocoaWidget) != downscale-2f.html?203,52,top about:blank
fuzzy(5,999) random != downscale-2a.html?205,53,top about:blank
fuzzy(5,999) random != downscale-2b.html?205,53,top about:blank
fuzzy(5,999) random != downscale-2c.html?205,53,top about:blank
fuzzy(5,999) random != downscale-2d.html?205,53,top about:blank
fuzzy(5,999) random != downscale-2e.html?205,53,top about:blank
fuzzy(5,999) random != downscale-2f.html?205,53,top about:blank
fuzzy(20,999) != downscale-2a.html?205,53,top about:blank
fuzzy(20,999) != downscale-2b.html?205,53,top about:blank
fuzzy(20,999) != downscale-2c.html?205,53,top about:blank
fuzzy(20,999) != downscale-2d.html?205,53,top about:blank
fuzzy(20,999) != downscale-2e.html?205,53,top about:blank
fuzzy(20,999) fails-if(!cocoaWidget) != downscale-2f.html?205,53,top about:blank
fuzzy(5,999) random != downscale-2a.html?203,52,bottom about:blank
fuzzy(5,999) random != downscale-2b.html?203,52,bottom about:blank
fuzzy(5,999) random != downscale-2c.html?203,52,bottom about:blank
fuzzy(5,999) random != downscale-2d.html?203,52,bottom about:blank
fuzzy(5,999) random != downscale-2e.html?203,52,bottom about:blank
fuzzy(5,999) random != downscale-2f.html?203,52,bottom about:blank
fuzzy(20,999) != downscale-2a.html?203,52,bottom about:blank
fuzzy(20,999) != downscale-2b.html?203,52,bottom about:blank
fuzzy(20,999) != downscale-2c.html?203,52,bottom about:blank
fuzzy(20,999) != downscale-2d.html?203,52,bottom about:blank
fuzzy(20,999) != downscale-2e.html?203,52,bottom about:blank
fuzzy(20,999) fails-if(!cocoaWidget) != downscale-2f.html?203,52,bottom about:blank
fuzzy(5,999) random != downscale-2a.html?205,53,bottom about:blank
fuzzy(5,999) random != downscale-2b.html?205,53,bottom about:blank
fuzzy(5,999) random != downscale-2c.html?205,53,bottom about:blank
fuzzy(5,999) random != downscale-2d.html?205,53,bottom about:blank
fuzzy(5,999) random != downscale-2e.html?205,53,bottom about:blank
fuzzy(5,999) random != downscale-2f.html?205,53,bottom about:blank
fuzzy(20,999) != downscale-2a.html?205,53,bottom about:blank
fuzzy(20,999) != downscale-2b.html?205,53,bottom about:blank
fuzzy(20,999) != downscale-2c.html?205,53,bottom about:blank
fuzzy(20,999) != downscale-2d.html?205,53,bottom about:blank
fuzzy(20,999) != downscale-2e.html?205,53,bottom about:blank
fuzzy(20,999) fails-if(!cocoaWidget) != downscale-2f.html?205,53,bottom about:blank
# RUN TESTS WITH HIGH QUALITY DOWNSCALING ENABLED:
# ================================================
@ -100,58 +102,58 @@ default-preferences pref(image.high_quality_downscaling.enabled,true)
fuzzy(31,127) == downscale-1.html downscale-1-ref.html
fuzzy(5,999) random != downscale-2a.html?203,52,left about:blank
fuzzy(5,999) random != downscale-2b.html?203,52,left about:blank
fuzzy(5,999) random != downscale-2c.html?203,52,left about:blank
fuzzy(5,999) random != downscale-2d.html?203,52,left about:blank
fuzzy(5,999) random != downscale-2e.html?203,52,left about:blank
fuzzy(5,999) random != downscale-2f.html?203,52,left about:blank
fuzzy(20,999) != downscale-2a.html?203,52,left about:blank
fuzzy(20,999) != downscale-2b.html?203,52,left about:blank
fuzzy(20,999) != downscale-2c.html?203,52,left about:blank
fuzzy(20,999) != downscale-2d.html?203,52,left about:blank
fuzzy(20,999) != downscale-2e.html?203,52,left about:blank
fuzzy(20,999) fails-if(B2G) != downscale-2f.html?203,52,left about:blank
fuzzy(5,999) random != downscale-2a.html?205,53,left about:blank
fuzzy(5,999) random != downscale-2b.html?205,53,left about:blank
fuzzy(5,999) random != downscale-2c.html?205,53,left about:blank
fuzzy(5,999) random != downscale-2d.html?205,53,left about:blank
fuzzy(5,999) random != downscale-2e.html?205,53,left about:blank
fuzzy(5,999) random != downscale-2f.html?205,53,left about:blank
fuzzy(20,999) != downscale-2a.html?205,53,left about:blank
fuzzy(20,999) != downscale-2b.html?205,53,left about:blank
fuzzy(20,999) != downscale-2c.html?205,53,left about:blank
fuzzy(20,999) != downscale-2d.html?205,53,left about:blank
fuzzy(20,999) != downscale-2e.html?205,53,left about:blank
fuzzy(20,999) fails-if(B2G) != downscale-2f.html?205,53,left about:blank
fuzzy(5,999) random != downscale-2a.html?203,52,right about:blank
fuzzy(5,999) random != downscale-2b.html?203,52,right about:blank
fuzzy(5,999) random != downscale-2c.html?203,52,right about:blank
fuzzy(5,999) random != downscale-2d.html?203,52,right about:blank
fuzzy(5,999) random != downscale-2e.html?203,52,right about:blank
fuzzy(5,999) random != downscale-2f.html?203,52,right about:blank
fuzzy(20,999) != downscale-2a.html?203,52,right about:blank
fuzzy(20,999) != downscale-2b.html?203,52,right about:blank
fuzzy(20,999) != downscale-2c.html?203,52,right about:blank
fuzzy(20,999) != downscale-2d.html?203,52,right about:blank
fuzzy(20,999) != downscale-2e.html?203,52,right about:blank
fuzzy(20,999) fails-if(B2G) != downscale-2f.html?203,52,right about:blank
fuzzy(5,999) random != downscale-2a.html?205,53,right about:blank
fuzzy(5,999) random != downscale-2b.html?205,53,right about:blank
fuzzy(5,999) random != downscale-2c.html?205,53,right about:blank
fuzzy(5,999) random != downscale-2d.html?205,53,right about:blank
fuzzy(5,999) random != downscale-2e.html?205,53,right about:blank
fuzzy(5,999) random != downscale-2f.html?205,53,right about:blank
fuzzy(20,999) != downscale-2a.html?205,53,right about:blank
fuzzy(20,999) != downscale-2b.html?205,53,right about:blank
fuzzy(20,999) != downscale-2c.html?205,53,right about:blank
fuzzy(20,999) != downscale-2d.html?205,53,right about:blank
fuzzy(20,999) != downscale-2e.html?205,53,right about:blank
fuzzy(20,999) fails-if(B2G) != downscale-2f.html?205,53,right about:blank
fuzzy(5,999) random != downscale-2a.html?203,52,top about:blank
fuzzy(5,999) random != downscale-2b.html?203,52,top about:blank
fuzzy(5,999) random != downscale-2c.html?203,52,top about:blank
fuzzy(5,999) random != downscale-2d.html?203,52,top about:blank
fuzzy(5,999) random != downscale-2e.html?203,52,top about:blank
fuzzy(5,999) random != downscale-2f.html?203,52,top about:blank
fuzzy(20,999) != downscale-2a.html?203,52,top about:blank
fuzzy(20,999) != downscale-2b.html?203,52,top about:blank
fuzzy(20,999) != downscale-2c.html?203,52,top about:blank
fuzzy(20,999) != downscale-2d.html?203,52,top about:blank
fuzzy(20,999) != downscale-2e.html?203,52,top about:blank
fuzzy(20,999) fails-if(B2G) != downscale-2f.html?203,52,top about:blank
fuzzy(5,999) random != downscale-2a.html?205,53,top about:blank
fuzzy(5,999) random != downscale-2b.html?205,53,top about:blank
fuzzy(5,999) random != downscale-2c.html?205,53,top about:blank
fuzzy(5,999) random != downscale-2d.html?205,53,top about:blank
fuzzy(5,999) random != downscale-2e.html?205,53,top about:blank
fuzzy(5,999) random != downscale-2f.html?205,53,top about:blank
fuzzy(20,999) != downscale-2a.html?205,53,top about:blank
fuzzy(20,999) != downscale-2b.html?205,53,top about:blank
fuzzy(20,999) != downscale-2c.html?205,53,top about:blank
fuzzy(20,999) != downscale-2d.html?205,53,top about:blank
fuzzy(20,999) != downscale-2e.html?205,53,top about:blank
fuzzy(20,999) fails-if(B2G) != downscale-2f.html?205,53,top about:blank
fuzzy(5,999) random != downscale-2a.html?203,52,bottom about:blank
fuzzy(5,999) random != downscale-2b.html?203,52,bottom about:blank
fuzzy(5,999) random != downscale-2c.html?203,52,bottom about:blank
fuzzy(5,999) random != downscale-2d.html?203,52,bottom about:blank
fuzzy(5,999) random != downscale-2e.html?203,52,bottom about:blank
fuzzy(5,999) random != downscale-2f.html?203,52,bottom about:blank
fuzzy(20,999) != downscale-2a.html?203,52,bottom about:blank
fuzzy(20,999) != downscale-2b.html?203,52,bottom about:blank
fuzzy(20,999) != downscale-2c.html?203,52,bottom about:blank
fuzzy(20,999) != downscale-2d.html?203,52,bottom about:blank
fuzzy(20,999) != downscale-2e.html?203,52,bottom about:blank
fuzzy(20,999) fails-if(B2G) != downscale-2f.html?203,52,bottom about:blank
fuzzy(5,999) random != downscale-2a.html?205,53,bottom about:blank
fuzzy(5,999) random != downscale-2b.html?205,53,bottom about:blank
fuzzy(5,999) random != downscale-2c.html?205,53,bottom about:blank
fuzzy(5,999) random != downscale-2d.html?205,53,bottom about:blank
fuzzy(5,999) random != downscale-2e.html?205,53,bottom about:blank
fuzzy(5,999) random != downscale-2f.html?205,53,bottom about:blank
fuzzy(20,999) != downscale-2a.html?205,53,bottom about:blank
fuzzy(20,999) != downscale-2b.html?205,53,bottom about:blank
fuzzy(20,999) != downscale-2c.html?205,53,bottom about:blank
fuzzy(20,999) != downscale-2d.html?205,53,bottom about:blank
fuzzy(20,999) != downscale-2e.html?205,53,bottom about:blank
fuzzy(20,999) fails-if(B2G) != downscale-2f.html?205,53,bottom about:blank

View File

@ -8,6 +8,7 @@
#include "mozilla/ArrayUtils.h"
#include "mozilla/BasicEvents.h"
#include "mozilla/Maybe.h"
#include "mozilla/MemoryReporting.h"
#include "nsPresContext.h"
#include "nsIContent.h"
@ -31,6 +32,7 @@
#include "nsBidiPresUtils.h"
#include "imgIContainer.h"
#include "ImageOps.h"
#include "ImageRegion.h"
#include "gfxRect.h"
#include "gfxContext.h"
#include "nsRenderingContext.h"
@ -66,6 +68,7 @@
#include "nsFontFaceList.h"
#include "nsFontInflationData.h"
#include "nsSVGUtils.h"
#include "SVGImageContext.h"
#include "SVGTextFrame.h"
#include "nsStyleStructInlines.h"
#include "nsStyleTransformMatrix.h"
@ -103,15 +106,11 @@
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::image;
using namespace mozilla::layers;
using namespace mozilla::layout;
using namespace mozilla::gfx;
using mozilla::image::Angle;
using mozilla::image::Flip;
using mozilla::image::ImageOps;
using mozilla::image::Orientation;
#define GRID_ENABLED_PREF_NAME "layout.css.grid.enabled"
#define RUBY_ENABLED_PREF_NAME "layout.css.ruby.enabled"
#define STICKY_ENABLED_PREF_NAME "layout.css.sticky.enabled"
@ -4928,37 +4927,50 @@ nsLayoutUtils::RectToGfxRect(const nsRect& aRect, int32_t aAppUnitsPerDevPixel)
}
struct SnappedImageDrawingParameters {
// A transform from either device space or user space (depending on mResetCTM)
// to image space
gfxMatrix mUserSpaceToImageSpace;
// A device-space, pixel-aligned rectangle to fill
gfxRect mFillRect;
// A pixel rectangle in tiled image space outside of which gfx should not
// sample (using EXTEND_PAD as necessary)
nsIntRect mSubimage;
// Whether there's anything to draw at all
bool mShouldDraw;
// true iff the CTM of the rendering context needs to be reset to the
// identity matrix before drawing
bool mResetCTM;
// A transform from image space to device space.
gfxMatrix imageSpaceToDeviceSpace;
// The size at which the image should be drawn (which may not be its
// intrinsic size due to, for example, HQ scaling).
nsIntSize size;
// The region in tiled image space which will be drawn, with an associated
// region to which sampling should be restricted.
ImageRegion region;
// Whether there's anything to draw at all.
bool shouldDraw;
SnappedImageDrawingParameters()
: mShouldDraw(false)
, mResetCTM(false)
: region(ImageRegion::Empty())
, shouldDraw(false)
{}
SnappedImageDrawingParameters(const gfxMatrix& aUserSpaceToImageSpace,
const gfxRect& aFillRect,
const nsIntRect& aSubimage,
bool aResetCTM)
: mUserSpaceToImageSpace(aUserSpaceToImageSpace)
, mFillRect(aFillRect)
, mSubimage(aSubimage)
, mShouldDraw(true)
, mResetCTM(aResetCTM)
SnappedImageDrawingParameters(const gfxMatrix& aImageSpaceToDeviceSpace,
const nsIntSize& aSize,
const ImageRegion& aRegion)
: imageSpaceToDeviceSpace(aImageSpaceToDeviceSpace)
, size(aSize)
, region(aRegion)
, shouldDraw(true)
{}
};
/**
* Given two axis-aligned rectangles, returns the transformation that maps the
* first onto the second.
*
* @param aFrom The rect to be transformed.
* @param aTo The rect that aFrom should be mapped onto by the transformation.
*/
static gfxMatrix
TransformBetweenRects(const gfxRect& aFrom, const gfxRect& aTo)
{
gfxSize scale(aTo.width / aFrom.width,
aTo.height / aFrom.height);
gfxPoint translation(aTo.x - aFrom.x * scale.width,
aTo.y - aFrom.y * scale.height);
return gfxMatrix(scale.width, 0, 0, scale.height,
translation.x, translation.y);
}
static nsRect
TileNearRect(const nsRect& aAnyTile, const nsRect& aTargetRect)
{
@ -4981,10 +4993,11 @@ ComputeSnappedImageDrawingParameters(gfxContext* aCtx,
const nsRect aFill,
const nsPoint aAnchor,
const nsRect aDirty,
const nsIntSize aImageSize)
imgIContainer* aImage,
GraphicsFilter aGraphicsFilter,
uint32_t aImageFlags)
{
if (aDest.IsEmpty() || aFill.IsEmpty() || !aImageSize.width || !aImageSize.height)
if (aDest.IsEmpty() || aFill.IsEmpty())
return SnappedImageDrawingParameters();
// Avoid unnecessarily large offsets.
@ -5017,68 +5030,97 @@ ComputeSnappedImageDrawingParameters(gfxContext* aCtx,
fill = devPixelFill;
}
gfxSize imageSize(aImageSize.width, aImageSize.height);
gfxSize destScale = didSnap ? gfxSize(currentMatrix._11, currentMatrix._22)
: gfxSize(1.0, 1.0);
gfxSize snappedDest(NSAppUnitsToIntPixels(dest.width * destScale.width,
aAppUnitsPerDevPixel),
NSAppUnitsToIntPixels(dest.height * destScale.height,
aAppUnitsPerDevPixel));
nsIntSize intImageSize =
aImage->OptimalImageSizeForDest(snappedDest,
imgIContainer::FRAME_CURRENT,
aGraphicsFilter, aImageFlags);
gfxSize imageSize(intImageSize.width, intImageSize.height);
// Compute the set of pixels that would be sampled by an ideal rendering
gfxPoint subimageTopLeft =
MapToFloatImagePixels(imageSize, devPixelDest, devPixelFill.TopLeft());
gfxPoint subimageBottomRight =
MapToFloatImagePixels(imageSize, devPixelDest, devPixelFill.BottomRight());
nsIntRect intSubimage;
intSubimage.MoveTo(NSToIntFloor(subimageTopLeft.x),
NSToIntFloor(subimageTopLeft.y));
intSubimage.SizeTo(NSToIntCeil(subimageBottomRight.x) - intSubimage.x,
NSToIntCeil(subimageBottomRight.y) - intSubimage.y);
gfxRect subimage;
subimage.MoveTo(NSToIntFloor(subimageTopLeft.x),
NSToIntFloor(subimageTopLeft.y));
subimage.SizeTo(NSToIntCeil(subimageBottomRight.x) - subimage.x,
NSToIntCeil(subimageBottomRight.y) - subimage.y);
// Compute the anchor point and compute final fill rect.
// This code assumes that pixel-based devices have one pixel per
// device unit!
gfxPoint anchorPoint(gfxFloat(anchor.x)/aAppUnitsPerDevPixel,
gfxFloat(anchor.y)/aAppUnitsPerDevPixel);
gfxPoint imageSpaceAnchorPoint =
MapToFloatImagePixels(imageSize, devPixelDest, anchorPoint);
gfxMatrix transform;
gfxMatrix invTransform;
if (didSnap) {
imageSpaceAnchorPoint.Round();
anchorPoint = imageSpaceAnchorPoint;
anchorPoint = MapToFloatUserPixels(imageSize, devPixelDest, anchorPoint);
anchorPoint = currentMatrix.Transform(anchorPoint);
anchorPoint.Round();
bool anchorAtUpperLeft = anchor.x == dest.x && anchor.y == dest.y;
bool exactlyOneImageCopy = aFill.IsEqualEdges(dest);
if (anchorAtUpperLeft && exactlyOneImageCopy) {
// The simple case: we can ignore the anchor point and compute the
// transformation from the sampled region (the subimage) to the fill rect.
// This approach is preferable when it works since it tends to produce
// less numerical error.
transform = TransformBetweenRects(subimage, fill);
invTransform = TransformBetweenRects(fill, subimage);
} else {
// The more complicated case: we compute the transformation from the
// image rect positioned at the image space anchor point to the dest rect
// positioned at the device space anchor point.
// Compute the anchor point in both device space and image space. This
// code assumes that pixel-based devices have one pixel per device unit!
gfxPoint anchorPoint(gfxFloat(anchor.x)/aAppUnitsPerDevPixel,
gfxFloat(anchor.y)/aAppUnitsPerDevPixel);
gfxPoint imageSpaceAnchorPoint =
MapToFloatImagePixels(imageSize, devPixelDest, anchorPoint);
if (didSnap) {
imageSpaceAnchorPoint.Round();
anchorPoint = imageSpaceAnchorPoint;
anchorPoint = MapToFloatUserPixels(imageSize, devPixelDest, anchorPoint);
anchorPoint = currentMatrix.Transform(anchorPoint);
anchorPoint.Round();
}
gfxRect anchoredDestRect(anchorPoint, snappedDest);
gfxRect anchoredImageRect(imageSpaceAnchorPoint, imageSize);
transform = TransformBetweenRects(anchoredImageRect, anchoredDestRect);
invTransform = TransformBetweenRects(anchoredDestRect, anchoredImageRect);
}
// If the transform is not a straight translation by integers, then
// filtering will occur, and restricting the fill rect to the dirty rect
// would change the values computed for edge pixels, which we can't allow.
// Also, if 'didSnap' is false then rounding out 'devPixelDirty' might not
// produce pixel-aligned coordinates, which would also break the values
// computed for edge pixels.
if (didSnap && !invTransform.HasNonIntegerTranslation()) {
// This form of Transform is safe to call since non-axis-aligned
// transforms wouldn't be snapped.
devPixelDirty = currentMatrix.Transform(devPixelDirty);
}
gfxFloat scaleX = imageSize.width*aAppUnitsPerDevPixel/aDest.width;
gfxFloat scaleY = imageSize.height*aAppUnitsPerDevPixel/aDest.height;
if (didSnap) {
// We'll reset aCTX to the identity matrix before drawing, so we need to
// adjust our scales to match.
scaleX /= currentMatrix._11;
scaleY /= currentMatrix._22;
}
gfxFloat translateX = imageSpaceAnchorPoint.x - anchorPoint.x*scaleX;
gfxFloat translateY = imageSpaceAnchorPoint.y - anchorPoint.y*scaleY;
gfxMatrix transform(scaleX, 0, 0, scaleY, translateX, translateY);
gfxRect finalFillRect = fill;
// If the user-space-to-image-space transform is not a straight
// translation by integers, then filtering will occur, and
// restricting the fill rect to the dirty rect would change the values
// computed for edge pixels, which we can't allow.
// Also, if didSnap is false then rounding out 'devPixelDirty' might not
// produce pixel-aligned coordinates, which would also break the values
// computed for edge pixels.
if (didSnap && !transform.HasNonIntegerTranslation()) {
devPixelDirty.RoundOut();
finalFillRect = fill.Intersect(devPixelDirty);
fill = fill.Intersect(devPixelDirty);
}
if (finalFillRect.IsEmpty())
if (fill.IsEmpty())
return SnappedImageDrawingParameters();
return SnappedImageDrawingParameters(transform, finalFillRect, intSubimage,
didSnap);
gfxRect imageSpaceFill(didSnap ? invTransform.Transform(fill)
: invTransform.TransformBounds(fill));
// If we didn't snap, we need to post-multiply the matrix on the context to
// get the final matrix we'll draw with, because we didn't take it into
// account when computing the matrices above.
if (!didSnap) {
transform = transform * currentMatrix;
}
ImageRegion region =
ImageRegion::CreateWithSamplingRestriction(imageSpaceFill, subimage);
return SnappedImageDrawingParameters(transform, intImageSize, region);
}
@ -5091,7 +5133,6 @@ DrawImageInternal(nsRenderingContext* aRenderingContext,
const nsRect& aFill,
const nsPoint& aAnchor,
const nsRect& aDirty,
const nsIntSize& aImageSize,
const SVGImageContext* aSVGContext,
uint32_t aImageFlags)
{
@ -5107,21 +5148,20 @@ DrawImageInternal(nsRenderingContext* aRenderingContext,
aPresContext->AppUnitsPerDevPixel();
gfxContext* ctx = aRenderingContext->ThebesContext();
SnappedImageDrawingParameters drawingParams =
ComputeSnappedImageDrawingParameters(ctx, appUnitsPerDevPixel, aDest, aFill,
aAnchor, aDirty, aImageSize);
SnappedImageDrawingParameters params =
ComputeSnappedImageDrawingParameters(ctx, appUnitsPerDevPixel, aDest,
aFill, aAnchor, aDirty, aImage,
aGraphicsFilter, aImageFlags);
if (!drawingParams.mShouldDraw)
if (!params.shouldDraw)
return NS_OK;
gfxContextMatrixAutoSaveRestore saveMatrix(ctx);
if (drawingParams.mResetCTM) {
ctx->IdentityMatrix();
}
gfxContextMatrixAutoSaveRestore contextMatrixRestorer(ctx);
ctx->SetMatrix(params.imageSpaceToDeviceSpace);
aImage->Draw(ctx, params.size, params.region, imgIContainer::FRAME_CURRENT,
aGraphicsFilter, ToMaybe(aSVGContext), aImageFlags);
aImage->Draw(ctx, aGraphicsFilter, drawingParams.mUserSpaceToImageSpace,
drawingParams.mFillRect, drawingParams.mSubimage, aImageSize,
aSVGContext, imgIContainer::FRAME_CURRENT, aImageFlags);
return NS_OK;
}
@ -5160,7 +5200,7 @@ nsLayoutUtils::DrawSingleUnscaledImage(nsRenderingContext* aRenderingContext,
return DrawImageInternal(aRenderingContext, aPresContext,
aImage, aGraphicsFilter,
dest, fill, aDest, aDirty ? *aDirty : dest,
imageSize, nullptr, aImageFlags);
nullptr, aImageFlags);
}
/* static */ nsresult
@ -5174,29 +5214,21 @@ nsLayoutUtils::DrawSingleImage(nsRenderingContext* aRenderingContext,
uint32_t aImageFlags,
const nsRect* aSourceArea)
{
nsIntSize imageSize;
if (aImage->GetType() == imgIContainer::TYPE_VECTOR) {
// We choose a size for vector images that emulates a raster image which
// is perfectly sized for the destination rect: each pixel in the image
// maps exactly to a single pixel on-screen.
nscoord appUnitsPerDevPx =
aPresContext->AppUnitsPerDevPixel();
imageSize.width = NSAppUnitsToIntPixels(aDest.width, appUnitsPerDevPx);
imageSize.height = NSAppUnitsToIntPixels(aDest.height, appUnitsPerDevPx);
} else {
// Raster images have an intrinsic size, so we just use that.
aImage->GetWidth(&imageSize.width);
aImage->GetHeight(&imageSize.height);
}
nsIntSize imageSize(ComputeSizeForDrawingWithFallback(aImage, aDest.Size()));
NS_ENSURE_TRUE(imageSize.width > 0 && imageSize.height > 0, NS_ERROR_FAILURE);
nsRect source;
nsCOMPtr<imgIContainer> image;
if (aSourceArea) {
source = *aSourceArea;
nsIntRect subRect(source.x, source.y, source.width, source.height);
subRect.ScaleInverseRoundOut(nsDeviceContext::AppUnitsPerCSSPixel());
image = ImageOps::Clip(aImage, subRect);
} else {
nscoord appUnitsPerCSSPixel = nsDeviceContext::AppUnitsPerCSSPixel();
source.SizeTo(imageSize.width*appUnitsPerCSSPixel,
imageSize.height*appUnitsPerCSSPixel);
image = aImage;
}
nsRect dest = nsLayoutUtils::GetWholeImageDestination(imageSize, source,
@ -5207,8 +5239,8 @@ nsLayoutUtils::DrawSingleImage(nsRenderingContext* aRenderingContext,
nsRect fill;
fill.IntersectRect(aDest, dest);
return DrawImageInternal(aRenderingContext, aPresContext, aImage,
aGraphicsFilter, dest, fill,
fill.TopLeft(), aDirty, imageSize, aSVGContext, aImageFlags);
aGraphicsFilter, dest, fill, fill.TopLeft(),
aDirty, aSVGContext, aImageFlags);
}
/* static */ void
@ -5231,50 +5263,17 @@ nsLayoutUtils::ComputeSizeForDrawing(imgIContainer *aImage,
}
}
/* static */ nsresult
nsLayoutUtils::DrawBackgroundImage(nsRenderingContext* aRenderingContext,
nsPresContext* aPresContext,
imgIContainer* aImage,
const nsIntSize& aImageSize,
GraphicsFilter aGraphicsFilter,
const nsRect& aDest,
const nsRect& aFill,
const nsPoint& aAnchor,
const nsRect& aDirty,
uint32_t aImageFlags)
{
PROFILER_LABEL("nsLayoutUtils", "DrawBackgroundImage",
js::ProfileEntry::Category::GRAPHICS);
if (UseBackgroundNearestFiltering()) {
aGraphicsFilter = GraphicsFilter::FILTER_NEAREST;
}
return DrawImageInternal(aRenderingContext, aPresContext, aImage,
aGraphicsFilter,
aDest, aFill, aAnchor, aDirty,
aImageSize, nullptr, aImageFlags);
}
/* static */ nsresult
nsLayoutUtils::DrawImage(nsRenderingContext* aRenderingContext,
nsPresContext* aPresContext,
imgIContainer* aImage,
GraphicsFilter aGraphicsFilter,
const nsRect& aDest,
const nsRect& aFill,
const nsPoint& aAnchor,
const nsRect& aDirty,
uint32_t aImageFlags)
/* static */ nsIntSize
nsLayoutUtils::ComputeSizeForDrawingWithFallback(imgIContainer* aImage,
const nsSize& aFallbackSize)
{
nsIntSize imageSize;
nsSize imageRatio;
bool gotHeight, gotWidth;
ComputeSizeForDrawing(aImage, imageSize, imageRatio, gotWidth, gotHeight);
// XXX Dimensionless images shouldn't fall back to filled-area size -- the
// caller should provide the image size, a la DrawBackgroundImage.
// If we didn't get both width and height, try to compute them using the
// intrinsic ratio of the image.
if (gotWidth != gotHeight) {
if (!gotWidth) {
if (imageRatio.height != 0) {
@ -5295,17 +5294,58 @@ nsLayoutUtils::DrawImage(nsRenderingContext* aRenderingContext,
}
}
// If we still don't have a width or height, just use the fallback size the
// caller provided.
if (!gotWidth) {
imageSize.width = nsPresContext::AppUnitsToIntCSSPixels(aFill.width);
imageSize.width = nsPresContext::AppUnitsToIntCSSPixels(aFallbackSize.width);
}
if (!gotHeight) {
imageSize.height = nsPresContext::AppUnitsToIntCSSPixels(aFill.height);
imageSize.height = nsPresContext::AppUnitsToIntCSSPixels(aFallbackSize.height);
}
return imageSize;
}
/* static */ nsresult
nsLayoutUtils::DrawBackgroundImage(nsRenderingContext* aRenderingContext,
nsPresContext* aPresContext,
imgIContainer* aImage,
const nsIntSize& aImageSize,
GraphicsFilter aGraphicsFilter,
const nsRect& aDest,
const nsRect& aFill,
const nsPoint& aAnchor,
const nsRect& aDirty,
uint32_t aImageFlags)
{
PROFILER_LABEL("layout", "nsLayoutUtils::DrawBackgroundImage",
js::ProfileEntry::Category::GRAPHICS);
if (UseBackgroundNearestFiltering()) {
aGraphicsFilter = GraphicsFilter::FILTER_NEAREST;
}
SVGImageContext svgContext(aImageSize, Nothing());
return DrawImageInternal(aRenderingContext, aPresContext, aImage,
aGraphicsFilter,
aDest, aFill, aAnchor, aDirty,
imageSize, nullptr, aImageFlags);
aGraphicsFilter, aDest, aFill, aAnchor,
aDirty, &svgContext, aImageFlags);
}
/* static */ nsresult
nsLayoutUtils::DrawImage(nsRenderingContext* aRenderingContext,
nsPresContext* aPresContext,
imgIContainer* aImage,
GraphicsFilter aGraphicsFilter,
const nsRect& aDest,
const nsRect& aFill,
const nsPoint& aAnchor,
const nsRect& aDirty,
uint32_t aImageFlags)
{
return DrawImageInternal(aRenderingContext, aPresContext, aImage,
aGraphicsFilter, aDest, aFill, aAnchor,
aDirty, nullptr, aImageFlags);
}
/* static */ nsRect

View File

@ -1455,14 +1455,14 @@ public:
* @param aImageFlags Image flags of the imgIContainer::FLAG_* variety
*/
static nsresult DrawImage(nsRenderingContext* aRenderingContext,
nsPresContext* aPresContext,
imgIContainer* aImage,
GraphicsFilter aGraphicsFilter,
const nsRect& aDest,
const nsRect& aFill,
const nsPoint& aAnchor,
const nsRect& aDirty,
uint32_t aImageFlags);
nsPresContext* aPresContext,
imgIContainer* aImage,
GraphicsFilter aGraphicsFilter,
const nsRect& aDest,
const nsRect& aFill,
const nsPoint& aAnchor,
const nsRect& aDirty,
uint32_t aImageFlags);
/**
* Convert an nsRect to a gfxRect.
@ -1515,15 +1515,15 @@ public:
* in appunits. For best results it should
* be aligned with image pixels.
*/
static nsresult DrawSingleImage(nsRenderingContext* aRenderingContext,
nsPresContext* aPresContext,
imgIContainer* aImage,
GraphicsFilter aGraphicsFilter,
const nsRect& aDest,
const nsRect& aDirty,
static nsresult DrawSingleImage(nsRenderingContext* aRenderingContext,
nsPresContext* aPresContext,
imgIContainer* aImage,
GraphicsFilter aGraphicsFilter,
const nsRect& aDest,
const nsRect& aDirty,
const mozilla::SVGImageContext* aSVGContext,
uint32_t aImageFlags,
const nsRect* aSourceArea = nullptr);
uint32_t aImageFlags,
const nsRect* aSourceArea = nullptr);
/**
* Given an imgIContainer, this method attempts to obtain an intrinsic
@ -1547,6 +1547,17 @@ public:
bool& aGotWidth,
bool& aGotHeight);
/**
* Given an imgIContainer, this method attempts to obtain an intrinsic
* px-valued height & width for it. If the imgIContainer has a non-pixel
* value for either height or width, this method tries to generate a pixel
* value for that dimension using the intrinsic ratio (if available). If,
* after trying all these methods, no value is available for one or both
* dimensions, the corresponding dimension of aFallbackSize is used instead.
*/
static nsIntSize ComputeSizeForDrawingWithFallback(imgIContainer* aImage,
const nsSize& aFallbackSize);
/**
* Given a source area of an image (in appunits) and a destination area
* that we want to map that source area too, computes the area that

View File

@ -3,7 +3,9 @@
== solid-image-2.html solid-image-2-ref.html
== solid-image-2a.html solid-image-2-ref.html
== multicolor-image-1.html multicolor-image-1-ref.html
== multicolor-image-2.html multicolor-image-2-ref.html
# This is fuzzy temporarily until bug 1044702 makes it possible to use source
# clipping on Windows. (Any other fix would have a significant perf cost.)
fuzzy-if(winWidget,1,1) == multicolor-image-2.html multicolor-image-2-ref.html
== multicolor-image-3.html multicolor-image-3-ref.html
== multicolor-image-4.html multicolor-image-4-ref.html
== multicolor-image-5.html multicolor-image-5-ref.html

View File

@ -1720,7 +1720,7 @@ skip-if(B2G) == 751012-1b.html 751012-1-ref.html
random-if(Android) == 753329-1.html about:blank
== 758561-1.html 758561-1-ref.html
fuzzy-if(true,1,90) random-if(Android&&AndroidVersion<15) == 759036-1.html 759036-1-ref.html
fuzzy-if(true,17,5885) random-if(Android&&AndroidVersion<15) == 759036-2.html 759036-2-ref.html
fuzzy-if(true,17,5886) random-if(Android&&AndroidVersion<15) == 759036-2.html 759036-2-ref.html
random-if(Android&&AndroidVersion<15) == 776265-1a.html 776265-1-ref.html
== 776265-1b.html 776265-1-ref.html
== 776265-1c.html 776265-1-ref.html

View File

@ -2,7 +2,7 @@ fuzzy-if(Android,8,30) == background-image-zoom-1.html background-image-zoom-1-r
== background-image-zoom-2.html about:blank
== image-seam-1a.html image-seam-1-ref.html
== image-seam-1b.html image-seam-1-ref.html
fails-if(cocoaWidget) == image-seam-2.html image-seam-2-ref.html # Quartz doesn't support EXTEND_PAD (bug 567370)
== image-seam-2.html image-seam-2-ref.html
skip-if(B2G&&browserIsRemote) == image-zoom-1.html image-zoom-1-ref.html
skip-if(B2G&&browserIsRemote) == image-zoom-2.html image-zoom-1-ref.html
== invalid-url-image-1.html invalid-url-image-1-ref.html

View File

@ -41,9 +41,10 @@ skip-if(B2G) == canvas-drawImage-scale-1a.html lime100x100-ref.html
skip-if(B2G) == canvas-drawImage-scale-1b.html lime100x100-ref.html
skip-if(B2G) == canvas-drawImage-scale-1c.html lime100x100-ref.html
# Fuzzyness and fails for scaling should be fixed by bug 941467
# Fails on Android versions where we apply a zoom by default, because the
# resolution of a canvas element is fixed regardless of zoom level.
fuzzy(1,2) fails-if(Android&&AndroidVersion<15&&AndroidVersion!=10) == canvas-drawImage-scale-2a.html canvas-drawImage-scale-2-ref.html
fails == canvas-drawImage-scale-2b.html canvas-drawImage-scale-2-ref.html
fuzzy(1,2) fails-if(Android&&AndroidVersion<15&&AndroidVersion!=10) == canvas-drawImage-scale-2b.html canvas-drawImage-scale-2-ref.html
skip-if(B2G) == canvas-drawImage-slice-1a.html lime100x100-ref.html
== canvas-drawImage-slice-1b.html lime100x100-ref.html

View File

@ -6,28 +6,38 @@
#ifndef MOZILLA_SVGCONTEXT_H_
#define MOZILLA_SVGCONTEXT_H_
#include "mozilla/Maybe.h"
#include "SVGPreserveAspectRatio.h"
namespace mozilla {
// SVG image-specific rendering context. For imgIContainer::Draw.
// Used to pass information about overridden attributes from an SVG <image>
// element to the image's internal SVG document when it's drawn.
// Used to pass information such as
// - viewport information from CSS, and
// - overridden attributes from an SVG <image> element
// to the image's internal SVG document when it's drawn.
class SVGImageContext
{
public:
SVGImageContext() { }
SVGImageContext(SVGPreserveAspectRatio aPreserveAspectRatio)
: mPreserveAspectRatio(aPreserveAspectRatio)
SVGImageContext(nsIntSize aViewportSize,
Maybe<SVGPreserveAspectRatio> aPreserveAspectRatio)
: mViewportSize(aViewportSize)
, mPreserveAspectRatio(aPreserveAspectRatio)
{ }
const SVGPreserveAspectRatio& GetPreserveAspectRatio() const {
const nsIntSize& GetViewportSize() const {
return mViewportSize;
}
const Maybe<SVGPreserveAspectRatio>& GetPreserveAspectRatio() const {
return mPreserveAspectRatio;
}
bool operator==(const SVGImageContext& aOther) const {
return mPreserveAspectRatio == aOther.mPreserveAspectRatio;
return mViewportSize == aOther.mViewportSize &&
mPreserveAspectRatio == aOther.mPreserveAspectRatio;
}
bool operator!=(const SVGImageContext& aOther) const {
@ -35,11 +45,18 @@ public:
}
uint32_t Hash() const {
return mPreserveAspectRatio.Hash();
return HashGeneric(mViewportSize.width,
mViewportSize.height,
mPreserveAspectRatio.map(HashPAR).valueOr(0));
}
private:
SVGPreserveAspectRatio mPreserveAspectRatio;
static uint32_t HashPAR(const SVGPreserveAspectRatio& aPAR) {
return aPAR.Hash();
}
nsIntSize mViewportSize;
Maybe<SVGPreserveAspectRatio> mPreserveAspectRatio;
};
} // namespace mozilla

View File

@ -377,7 +377,8 @@ nsSVGImageFrame::PaintSVG(nsRenderingContext *aContext,
if (mImageContainer->GetType() == imgIContainer::TYPE_VECTOR) {
// Package up the attributes of this image element which can override the
// attributes of mImageContainer's internal SVG document.
SVGImageContext context(imgElem->mPreserveAspectRatio.GetAnimValue());
SVGImageContext context(nsIntSize(width, height),
Some(imgElem->mPreserveAspectRatio.GetAnimValue()));
nsRect destRect(0, 0,
appUnitsPerDevPx * width,

View File

@ -3,8 +3,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <cmath>
#include "gfx2DGlue.h"
#include "gfxPlatform.h"
#include "gfxUtils.h"
#include "ImageRegion.h"
#include "nsCocoaUtils.h"
#include "nsChildView.h"
#include "nsMenuBarX.h"
@ -18,6 +22,7 @@
#include "nsMenuUtilsX.h"
#include "nsToolkit.h"
#include "nsCRT.h"
#include "SVGImageContext.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/MiscEvents.h"
#include "mozilla/Preferences.h"
@ -35,6 +40,8 @@ using mozilla::gfx::IntRect;
using mozilla::gfx::IntSize;
using mozilla::gfx::SurfaceFormat;
using mozilla::gfx::SourceSurface;
using mozilla::image::ImageRegion;
using std::ceil;
static float
MenuBarScreenHeight()
@ -473,11 +480,11 @@ nsresult nsCocoaUtils::CreateNSImageFromImageContainer(imgIContainer *aImage, ui
// Render a vector image at the correct resolution on a retina display
if (aImage->GetType() == imgIContainer::TYPE_VECTOR && scaleFactor != 1.0f) {
int scaledWidth = (int)ceilf(width * scaleFactor);
int scaledHeight = (int)ceilf(height * scaleFactor);
gfxIntSize scaledSize(ceil(width * scaleFactor),
ceil(height * scaleFactor));
RefPtr<DrawTarget> drawTarget = gfxPlatform::GetPlatform()->
CreateOffscreenContentDrawTarget(IntSize(scaledWidth, scaledHeight),
CreateOffscreenContentDrawTarget(ToIntSize(scaledSize),
SurfaceFormat::B8G8R8A8);
if (!drawTarget) {
NS_ERROR("Failed to create DrawTarget");
@ -490,11 +497,9 @@ nsresult nsCocoaUtils::CreateNSImageFromImageContainer(imgIContainer *aImage, ui
return NS_ERROR_FAILURE;
}
aImage->Draw(context, GraphicsFilter::FILTER_NEAREST, gfxMatrix(),
gfxRect(0.0f, 0.0f, scaledWidth, scaledHeight),
nsIntRect(0, 0, width, height),
nsIntSize(scaledWidth, scaledHeight),
nullptr, aWhichFrame, imgIContainer::FLAG_SYNC_DECODE);
aImage->Draw(context, scaledSize, ImageRegion::Create(scaledSize),
aWhichFrame, GraphicsFilter::FILTER_NEAREST, Nothing(),
imgIContainer::FLAG_SYNC_DECODE);
surface = drawTarget->Snapshot();
} else {

View File

@ -28,9 +28,11 @@
#include "nsIImageLoadingContent.h"
#include "imgIContainer.h"
#include "imgIRequest.h"
#include "ImageRegion.h"
#include "nsRegion.h"
#include "nsXULPopupManager.h"
#include "nsMenuPopupFrame.h"
#include "SVGImageContext.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/Preferences.h"
#include "mozilla/gfx/2D.h"
@ -40,8 +42,9 @@
#include <algorithm>
using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::dom;
using namespace mozilla::gfx;
using namespace mozilla::image;
#define DRAGIMAGES_PREF "nglayout.enable_drag_images"
@ -629,12 +632,9 @@ nsBaseDragService::DrawDragForImage(nsPresContext* aPresContext,
if (!ctx)
return NS_ERROR_FAILURE;
gfxRect outRect(0, 0, destSize.width, destSize.height);
gfxMatrix scale =
gfxMatrix().Scale(srcSize.width/outRect.Width(), srcSize.height/outRect.Height());
nsIntRect imgSize(0, 0, srcSize.width, srcSize.height);
imgContainer->Draw(ctx, GraphicsFilter::FILTER_GOOD, scale, outRect, imgSize,
destSize, nullptr, imgIContainer::FRAME_CURRENT,
imgContainer->Draw(ctx, destSize, ImageRegion::Create(destSize),
imgIContainer::FRAME_CURRENT,
GraphicsFilter::FILTER_GOOD, Nothing(),
imgIContainer::FLAG_SYNC_DECODE);
*aSurface = dt->Snapshot();
} else {