mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
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:
parent
eae5445a6a
commit
71d94ea626
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
||||
/*
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
158
image/src/ImageRegion.h
Normal 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
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
EXPORTS += [
|
||||
'ImageOps.h',
|
||||
'ImageRegion.h',
|
||||
'imgLoader.h',
|
||||
'imgRequest.h',
|
||||
'imgRequestProxy.h',
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user