gecko-dev/image/OrientedImage.cpp
Birunthan Mohanathas db2ca75447 Bug 1038536 - Flatten image/src/ directory. r=seth
--HG--
rename : image/src/BMPFileHeaders.h => image/BMPFileHeaders.h
rename : image/src/ClippedImage.cpp => image/ClippedImage.cpp
rename : image/src/ClippedImage.h => image/ClippedImage.h
rename : image/src/DecodePool.cpp => image/DecodePool.cpp
rename : image/src/DecodePool.h => image/DecodePool.h
rename : image/src/Decoder.cpp => image/Decoder.cpp
rename : image/src/Decoder.h => image/Decoder.h
rename : image/src/Downscaler.cpp => image/Downscaler.cpp
rename : image/src/Downscaler.h => image/Downscaler.h
rename : image/src/DynamicImage.cpp => image/DynamicImage.cpp
rename : image/src/DynamicImage.h => image/DynamicImage.h
rename : image/src/FrameAnimator.cpp => image/FrameAnimator.cpp
rename : image/src/FrameAnimator.h => image/FrameAnimator.h
rename : image/src/FrozenImage.cpp => image/FrozenImage.cpp
rename : image/src/FrozenImage.h => image/FrozenImage.h
rename : image/src/ICOFileHeaders.h => image/ICOFileHeaders.h
rename : image/src/IProgressObserver.h => image/IProgressObserver.h
rename : image/src/Image.cpp => image/Image.cpp
rename : image/src/Image.h => image/Image.h
rename : image/src/ImageFactory.cpp => image/ImageFactory.cpp
rename : image/src/ImageFactory.h => image/ImageFactory.h
rename : image/src/ImageMetadata.cpp => image/ImageMetadata.cpp
rename : image/src/ImageMetadata.h => image/ImageMetadata.h
rename : image/src/ImageOps.cpp => image/ImageOps.cpp
rename : image/src/ImageOps.h => image/ImageOps.h
rename : image/src/ImageRegion.h => image/ImageRegion.h
rename : image/src/ImageURL.h => image/ImageURL.h
rename : image/src/ImageWrapper.cpp => image/ImageWrapper.cpp
rename : image/src/ImageWrapper.h => image/ImageWrapper.h
rename : image/src/MultipartImage.cpp => image/MultipartImage.cpp
rename : image/src/MultipartImage.h => image/MultipartImage.h
rename : image/src/Orientation.h => image/Orientation.h
rename : image/src/OrientedImage.cpp => image/OrientedImage.cpp
rename : image/src/OrientedImage.h => image/OrientedImage.h
rename : image/src/ProgressTracker.cpp => image/ProgressTracker.cpp
rename : image/src/ProgressTracker.h => image/ProgressTracker.h
rename : image/src/RasterImage.cpp => image/RasterImage.cpp
rename : image/src/RasterImage.h => image/RasterImage.h
rename : image/src/SVGDocumentWrapper.cpp => image/SVGDocumentWrapper.cpp
rename : image/src/SVGDocumentWrapper.h => image/SVGDocumentWrapper.h
rename : image/src/ScriptedNotificationObserver.cpp => image/ScriptedNotificationObserver.cpp
rename : image/src/ScriptedNotificationObserver.h => image/ScriptedNotificationObserver.h
rename : image/src/ShutdownTracker.cpp => image/ShutdownTracker.cpp
rename : image/src/ShutdownTracker.h => image/ShutdownTracker.h
rename : image/src/SourceBuffer.cpp => image/SourceBuffer.cpp
rename : image/src/SourceBuffer.h => image/SourceBuffer.h
rename : image/src/SurfaceCache.cpp => image/SurfaceCache.cpp
rename : image/src/SurfaceCache.h => image/SurfaceCache.h
rename : image/src/VectorImage.cpp => image/VectorImage.cpp
rename : image/src/VectorImage.h => image/VectorImage.h
rename : image/src/imgFrame.cpp => image/imgFrame.cpp
rename : image/src/imgFrame.h => image/imgFrame.h
rename : image/src/imgLoader.cpp => image/imgLoader.cpp
rename : image/src/imgLoader.h => image/imgLoader.h
rename : image/src/imgRequest.cpp => image/imgRequest.cpp
rename : image/src/imgRequest.h => image/imgRequest.h
rename : image/src/imgRequestProxy.cpp => image/imgRequestProxy.cpp
rename : image/src/imgRequestProxy.h => image/imgRequestProxy.h
rename : image/src/imgTools.cpp => image/imgTools.cpp
rename : image/src/imgTools.h => image/imgTools.h
2015-05-14 20:52:05 -07:00

348 lines
10 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 <algorithm>
#include "gfx2DGlue.h"
#include "gfxDrawable.h"
#include "gfxPlatform.h"
#include "gfxUtils.h"
#include "ImageRegion.h"
#include "SVGImageContext.h"
#include "OrientedImage.h"
using std::swap;
namespace mozilla {
using namespace gfx;
using layers::LayerManager;
using layers::ImageContainer;
namespace image {
NS_IMPL_ISUPPORTS_INHERITED0(OrientedImage, ImageWrapper)
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);
}
}
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_(TemporaryRef<SourceSurface>)
OrientedImage::GetFrame(uint32_t aWhichFrame,
uint32_t aFlags)
{
nsresult rv;
if (mOrientation.IsIdentity()) {
return InnerImage()->GetFrame(aWhichFrame, aFlags);
}
// Get the underlying dimensions.
gfxIntSize 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()->IsOpaque()) {
surfaceFormat = gfx::SurfaceFormat::B8G8R8X8;
} else {
surfaceFormat = gfx::SurfaceFormat::B8G8R8A8;
}
// Create a surface to draw into.
RefPtr<DrawTarget> target =
gfxPlatform::GetPlatform()->
CreateOffscreenContentDrawTarget(size, surfaceFormat);
if (!target) {
NS_ERROR("Could not create a DrawTarget");
return nullptr;
}
// Create our drawable.
RefPtr<SourceSurface> innerSurface =
InnerImage()->GetFrame(aWhichFrame, aFlags);
NS_ENSURE_TRUE(innerSurface, nullptr);
nsRefPtr<gfxDrawable> drawable =
new gfxSurfaceDrawable(innerSurface, size);
// Draw.
nsRefPtr<gfxContext> ctx = new gfxContext(target);
ctx->Multiply(OrientationMatrix(size));
gfxUtils::DrawPixelSnapped(ctx, drawable, size,
ImageRegion::Create(size),
surfaceFormat, GraphicsFilter::FILTER_FAST);
return target->Snapshot();
}
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;
}
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.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 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();
}
static SVGImageContext
OrientViewport(const SVGImageContext& aOldContext,
const Orientation& aOrientation)
{
CSSIntSize viewportSize(aOldContext.GetViewportSize());
if (aOrientation.SwapsWidthAndHeight()) {
swap(viewportSize.width, viewportSize.height);
}
return SVGImageContext(viewportSize,
aOldContext.GetPreserveAspectRatio());
}
NS_IMETHODIMP_(DrawResult)
OrientedImage::Draw(gfxContext* aContext,
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, aSize, aRegion,
aWhichFrame, aFilter, aSVGContext, aFlags);
}
// 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);
return InnerImage()->Draw(aContext, size, region, aWhichFrame, aFilter,
aSVGContext.map(OrientViewport, mOrientation),
aFlags);
}
nsIntSize
OrientedImage::OptimalImageSizeForDest(const gfxSize& aDest,
uint32_t aWhichFrame,
GraphicsFilter aFilter, uint32_t aFlags)
{
if (!mOrientation.SwapsWidthAndHeight()) {
return InnerImage()->OptimalImageSizeForDest(aDest, aWhichFrame, aFilter,
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,
aFilter,
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, /* aInvert = */ true));
gfxRect invalidRect(matrix.TransformBounds(gfxRect(rect.x, rect.y,
rect.width, rect.height)));
invalidRect.RoundOut();
return nsIntRect(invalidRect.x, invalidRect.y,
invalidRect.width, invalidRect.height);
}
} // namespace image
} // namespace mozilla