gecko-dev/image/OrientedImage.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

375 строки
12 KiB
C++
Исходник Обычный вид История

/* -*- 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/. */
#include "OrientedImage.h"
#include <algorithm>
#include "gfx2DGlue.h"
#include "gfxDrawable.h"
#include "gfxPlatform.h"
#include "gfxUtils.h"
#include "ImageRegion.h"
#include "SVGImageContext.h"
using std::swap;
namespace mozilla {
using namespace gfx;
using layers::ImageContainer;
using layers::LayerManager;
namespace image {
NS_IMETHODIMP
OrientedImage::GetWidth(int32_t* aWidth) {
if (mOrientation.SwapsWidthAndHeight()) {
return InnerImage()->GetHeight(aWidth);
} else {
return InnerImage()->GetWidth(aWidth);
}
}
NS_IMETHODIMP
OrientedImage::GetHeight(int32_t* aHeight) {
if (mOrientation.SwapsWidthAndHeight()) {
return InnerImage()->GetWidth(aHeight);
} else {
return InnerImage()->GetHeight(aHeight);
}
}
nsresult OrientedImage::GetNativeSizes(nsTArray<IntSize>& aNativeSizes) const {
nsresult rv = InnerImage()->GetNativeSizes(aNativeSizes);
if (mOrientation.SwapsWidthAndHeight()) {
auto i = aNativeSizes.Length();
while (i > 0) {
--i;
swap(aNativeSizes[i].width, aNativeSizes[i].height);
}
}
return rv;
}
NS_IMETHODIMP
OrientedImage::GetIntrinsicSize(nsSize* aSize) {
nsresult rv = InnerImage()->GetIntrinsicSize(aSize);
if (mOrientation.SwapsWidthAndHeight()) {
swap(aSize->width, aSize->height);
}
return rv;
}
NS_IMETHODIMP
OrientedImage::GetIntrinsicRatio(nsSize* aRatio) {
nsresult rv = InnerImage()->GetIntrinsicRatio(aRatio);
if (mOrientation.SwapsWidthAndHeight()) {
swap(aRatio->width, aRatio->height);
}
return rv;
}
NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
OrientedImage::GetFrame(uint32_t aWhichFrame, uint32_t aFlags) {
nsresult rv;
if (mOrientation.IsIdentity()) {
return InnerImage()->GetFrame(aWhichFrame, aFlags);
}
// Get the underlying dimensions.
IntSize size;
rv = InnerImage()->GetWidth(&size.width);
NS_ENSURE_SUCCESS(rv, nullptr);
rv = InnerImage()->GetHeight(&size.height);
NS_ENSURE_SUCCESS(rv, nullptr);
// Determine an appropriate format for the surface.
gfx::SurfaceFormat surfaceFormat;
if (InnerImage()->WillDrawOpaqueNow()) {
surfaceFormat = gfx::SurfaceFormat::B8G8R8X8;
} else {
surfaceFormat = gfx::SurfaceFormat::B8G8R8A8;
}
// Create a surface to draw into.
Bug 1207245 - part 6 - rename nsRefPtr<T> to RefPtr<T>; r=ehsan; a=Tomcat The bulk of this commit was generated with a script, executed at the top level of a typical source code checkout. The only non-machine-generated part was modifying MFBT's moz.build to reflect the new naming. CLOSED TREE makes big refactorings like this a piece of cake. # The main substitution. find . -name '*.cpp' -o -name '*.cc' -o -name '*.h' -o -name '*.mm' -o -name '*.idl'| \ xargs perl -p -i -e ' s/nsRefPtr\.h/RefPtr\.h/g; # handle includes s/nsRefPtr ?</RefPtr</g; # handle declarations and variables ' # Handle a special friend declaration in gfx/layers/AtomicRefCountedWithFinalize.h. perl -p -i -e 's/::nsRefPtr;/::RefPtr;/' gfx/layers/AtomicRefCountedWithFinalize.h # Handle nsRefPtr.h itself, a couple places that define constructors # from nsRefPtr, and code generators specially. We do this here, rather # than indiscriminantly s/nsRefPtr/RefPtr/, because that would rename # things like nsRefPtrHashtable. perl -p -i -e 's/nsRefPtr/RefPtr/g' \ mfbt/nsRefPtr.h \ xpcom/glue/nsCOMPtr.h \ xpcom/base/OwningNonNull.h \ ipc/ipdl/ipdl/lower.py \ ipc/ipdl/ipdl/builtin.py \ dom/bindings/Codegen.py \ python/lldbutils/lldbutils/utils.py # In our indiscriminate substitution above, we renamed # nsRefPtrGetterAddRefs, the class behind getter_AddRefs. Fix that up. find . -name '*.cpp' -o -name '*.h' -o -name '*.idl' | \ xargs perl -p -i -e 's/nsRefPtrGetterAddRefs/RefPtrGetterAddRefs/g' if [ -d .git ]; then git mv mfbt/nsRefPtr.h mfbt/RefPtr.h else hg mv mfbt/nsRefPtr.h mfbt/RefPtr.h fi --HG-- rename : mfbt/nsRefPtr.h => mfbt/RefPtr.h
2015-10-18 08:24:48 +03:00
RefPtr<DrawTarget> target =
gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
size, surfaceFormat);
if (!target || !target->IsValid()) {
NS_ERROR("Could not create a DrawTarget");
return nullptr;
}
// Create our drawable.
Bug 1207245 - part 6 - rename nsRefPtr<T> to RefPtr<T>; r=ehsan; a=Tomcat The bulk of this commit was generated with a script, executed at the top level of a typical source code checkout. The only non-machine-generated part was modifying MFBT's moz.build to reflect the new naming. CLOSED TREE makes big refactorings like this a piece of cake. # The main substitution. find . -name '*.cpp' -o -name '*.cc' -o -name '*.h' -o -name '*.mm' -o -name '*.idl'| \ xargs perl -p -i -e ' s/nsRefPtr\.h/RefPtr\.h/g; # handle includes s/nsRefPtr ?</RefPtr</g; # handle declarations and variables ' # Handle a special friend declaration in gfx/layers/AtomicRefCountedWithFinalize.h. perl -p -i -e 's/::nsRefPtr;/::RefPtr;/' gfx/layers/AtomicRefCountedWithFinalize.h # Handle nsRefPtr.h itself, a couple places that define constructors # from nsRefPtr, and code generators specially. We do this here, rather # than indiscriminantly s/nsRefPtr/RefPtr/, because that would rename # things like nsRefPtrHashtable. perl -p -i -e 's/nsRefPtr/RefPtr/g' \ mfbt/nsRefPtr.h \ xpcom/glue/nsCOMPtr.h \ xpcom/base/OwningNonNull.h \ ipc/ipdl/ipdl/lower.py \ ipc/ipdl/ipdl/builtin.py \ dom/bindings/Codegen.py \ python/lldbutils/lldbutils/utils.py # In our indiscriminate substitution above, we renamed # nsRefPtrGetterAddRefs, the class behind getter_AddRefs. Fix that up. find . -name '*.cpp' -o -name '*.h' -o -name '*.idl' | \ xargs perl -p -i -e 's/nsRefPtrGetterAddRefs/RefPtrGetterAddRefs/g' if [ -d .git ]; then git mv mfbt/nsRefPtr.h mfbt/RefPtr.h else hg mv mfbt/nsRefPtr.h mfbt/RefPtr.h fi --HG-- rename : mfbt/nsRefPtr.h => mfbt/RefPtr.h
2015-10-18 08:24:48 +03:00
RefPtr<SourceSurface> innerSurface =
InnerImage()->GetFrame(aWhichFrame, aFlags);
NS_ENSURE_TRUE(innerSurface, nullptr);
RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(innerSurface, size);
// Draw.
RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(target);
MOZ_ASSERT(ctx); // already checked the draw target above
ctx->Multiply(OrientationMatrix(size));
gfxUtils::DrawPixelSnapped(ctx, drawable, SizeDouble(size),
ImageRegion::Create(size), surfaceFormat,
SamplingFilter::LINEAR);
return target->Snapshot();
}
NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
OrientedImage::GetFrameAtSize(const IntSize& aSize, uint32_t aWhichFrame,
uint32_t aFlags) {
// XXX(seth): It'd be nice to support downscale-during-decode for this case,
// but right now we just fall back to the intrinsic size.
return GetFrame(aWhichFrame, aFlags);
}
NS_IMETHODIMP_(bool)
OrientedImage::IsImageContainerAvailable(LayerManager* aManager,
uint32_t aFlags) {
if (mOrientation.IsIdentity()) {
return InnerImage()->IsImageContainerAvailable(aManager, aFlags);
}
return false;
}
NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
OrientedImage::GetImageContainer(LayerManager* aManager, uint32_t aFlags) {
// XXX(seth): We currently don't have a way of orienting the result of
// GetImageContainer. We work around this by always returning null, but if it
// ever turns out that OrientedImage is widely used on codepaths that can
// actually benefit from GetImageContainer, it would be a good idea to fix
// that method for performance reasons.
if (mOrientation.IsIdentity()) {
return InnerImage()->GetImageContainer(aManager, aFlags);
}
return nullptr;
}
NS_IMETHODIMP_(bool)
OrientedImage::IsImageContainerAvailableAtSize(LayerManager* aManager,
const IntSize& aSize,
uint32_t aFlags) {
if (mOrientation.IsIdentity()) {
return InnerImage()->IsImageContainerAvailableAtSize(aManager, aSize,
aFlags);
}
return false;
}
NS_IMETHODIMP_(ImgDrawResult)
OrientedImage::GetImageContainerAtSize(
layers::LayerManager* aManager, const gfx::IntSize& aSize,
const Maybe<SVGImageContext>& aSVGContext, uint32_t aFlags,
layers::ImageContainer** aOutContainer) {
// XXX(seth): We currently don't have a way of orienting the result of
// GetImageContainer. We work around this by always returning null, but if it
// ever turns out that OrientedImage is widely used on codepaths that can
// actually benefit from GetImageContainer, it would be a good idea to fix
// that method for performance reasons.
if (mOrientation.IsIdentity()) {
return InnerImage()->GetImageContainerAtSize(aManager, aSize, aSVGContext,
aFlags, aOutContainer);
}
return ImgDrawResult::NOT_SUPPORTED;
}
struct MatrixBuilder {
explicit MatrixBuilder(bool aInvert) : mInvert(aInvert) {}
gfxMatrix Build() { return mMatrix; }
void Scale(gfxFloat aX, gfxFloat aY) {
if (mInvert) {
mMatrix *= gfxMatrix::Scaling(1.0 / aX, 1.0 / aY);
} else {
mMatrix.PreScale(aX, aY);
}
}
void Rotate(gfxFloat aPhi) {
if (mInvert) {
mMatrix *= gfxMatrix::Rotation(-aPhi);
} else {
mMatrix.PreRotate(aPhi);
}
}
void Translate(gfxPoint aDelta) {
if (mInvert) {
mMatrix *= gfxMatrix::Translation(-aDelta);
} else {
mMatrix.PreTranslate(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 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");
}
return builder.Build();
}
NS_IMETHODIMP_(ImgDrawResult)
OrientedImage::Draw(gfxContext* aContext, const nsIntSize& aSize,
const ImageRegion& aRegion, uint32_t aWhichFrame,
SamplingFilter aSamplingFilter,
const Maybe<SVGImageContext>& aSVGContext, uint32_t aFlags,
float aOpacity) {
if (mOrientation.IsIdentity()) {
return InnerImage()->Draw(aContext, aSize, aRegion, aWhichFrame,
aSamplingFilter, aSVGContext, aFlags, aOpacity);
}
// 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(size.width, size.height);
}
// 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);
auto orientViewport = [&](const SVGImageContext& aOldContext) {
SVGImageContext context(aOldContext);
auto oldViewport = aOldContext.GetViewportSize();
if (oldViewport && mOrientation.SwapsWidthAndHeight()) {
// Swap width and height:
CSSIntSize newViewport(oldViewport->height, oldViewport->width);
context.SetViewportSize(Some(newViewport));
}
return context;
};
return InnerImage()->Draw(aContext, size, region, aWhichFrame,
aSamplingFilter, aSVGContext.map(orientViewport),
aFlags, aOpacity);
}
nsIntSize OrientedImage::OptimalImageSizeForDest(const gfxSize& aDest,
uint32_t aWhichFrame,
SamplingFilter aSamplingFilter,
uint32_t aFlags) {
if (!mOrientation.SwapsWidthAndHeight()) {
return InnerImage()->OptimalImageSizeForDest(aDest, aWhichFrame,
aSamplingFilter, aFlags);
}
// Swap the size for the calculation, then swap it back for the caller.
gfxSize destSize(aDest.height, aDest.width);
nsIntSize innerImageSize(InnerImage()->OptimalImageSizeForDest(
destSize, aWhichFrame, aSamplingFilter, aFlags));
return nsIntSize(innerImageSize.height, innerImageSize.width);
}
NS_IMETHODIMP_(nsIntRect)
OrientedImage::GetImageSpaceInvalidationRect(const nsIntRect& aRect) {
nsIntRect rect(InnerImage()->GetImageSpaceInvalidationRect(aRect));
if (mOrientation.IsIdentity()) {
return rect;
}
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(innerSize));
gfxRect invalidRect(matrix.TransformBounds(
gfxRect(rect.X(), rect.Y(), rect.Width(), rect.Height())));
return IntRect::RoundOut(invalidRect.X(), invalidRect.Y(),
invalidRect.Width(), invalidRect.Height());
}
} // namespace image
} // namespace mozilla