diff -r 000000000000 -r 6474c204b198 image/src/OrientedImage.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/image/src/OrientedImage.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,252 @@ +/* -*- 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 + +#include "gfxDrawable.h" +#include "gfxPlatform.h" +#include "gfxUtils.h" + +#include "OrientedImage.h" + +using namespace mozilla::gfx; + +using std::swap; +using mozilla::layers::LayerManager; +using mozilla::layers::ImageContainer; + +namespace mozilla { +namespace image { + +NS_IMPL_ISUPPORTS(OrientedImage, imgIContainer) + +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); + } +} + +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) +OrientedImage::GetFrame(uint32_t aWhichFrame, + uint32_t aFlags) +{ + nsresult rv; + + if (mOrientation.IsIdentity()) { + return InnerImage()->GetFrame(aWhichFrame, aFlags); + } + + // Get the underlying dimensions. + int32_t width, height; + rv = InnerImage()->GetWidth(&width); + NS_ENSURE_SUCCESS(rv, nullptr); + rv = InnerImage()->GetHeight(&height); + NS_ENSURE_SUCCESS(rv, nullptr); + if (mOrientation.SwapsWidthAndHeight()) { + swap(width, height); + } + NS_ENSURE_SUCCESS(rv, nullptr); + + // Determine an appropriate format for the surface. + gfx::SurfaceFormat surfaceFormat; + gfxImageFormat imageFormat; + if (InnerImage()->FrameIsOpaque(aWhichFrame)) { + surfaceFormat = gfx::SurfaceFormat::B8G8R8X8; + imageFormat = gfxImageFormat::ARGB32; + } else { + surfaceFormat = gfx::SurfaceFormat::B8G8R8A8; + imageFormat = gfxImageFormat::ARGB32; + } + + // Create a surface to draw into. + mozilla::RefPtr target = + gfxPlatform::GetPlatform()-> + CreateOffscreenContentDrawTarget(IntSize(width, height), surfaceFormat); + + // Create our drawable. + RefPtr innerSurface = + InnerImage()->GetFrame(aWhichFrame, aFlags); + NS_ENSURE_TRUE(innerSurface, nullptr); + nsRefPtr drawable = + new gfxSurfaceDrawable(innerSurface, gfxIntSize(width, height)); + + // Draw. + nsRefPtr ctx = new gfxContext(target); + gfxRect imageRect(0, 0, width, height); + gfxUtils::DrawPixelSnapped(ctx, drawable, OrientationMatrix(nsIntSize(width, height)), + imageRect, imageRect, imageRect, imageRect, + imageFormat, GraphicsFilter::FILTER_FAST); + + return target->Snapshot(); +} + +NS_IMETHODIMP +OrientedImage::GetImageContainer(LayerManager* aManager, ImageContainer** _retval) +{ + // 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, _retval); + } + + *_retval = nullptr; + return NS_OK; +} + +gfxMatrix +OrientedImage::OrientationMatrix(const nsIntSize& aViewportSize) +{ + gfxMatrix matrix; + + int32_t width, height; + if (InnerImage()->GetType() == imgIContainer::TYPE_VECTOR) { + width = mOrientation.SwapsWidthAndHeight() ? aViewportSize.height : aViewportSize.width; + height = mOrientation.SwapsWidthAndHeight() ? aViewportSize.width : aViewportSize.height; + } 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; + } + } + + // 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: + 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 matrix; +} + +NS_IMETHODIMP +OrientedImage::Draw(gfxContext* aContext, + GraphicsFilter aFilter, + const gfxMatrix& aUserSpaceToImageSpace, + const gfxRect& aFill, + const nsIntRect& aSubimage, + const nsIntSize& aViewportSize, + const SVGImageContext* aSVGContext, + uint32_t aWhichFrame, + uint32_t aFlags) +{ + if (mOrientation.IsIdentity()) { + return InnerImage()->Draw(aContext, aFilter, aUserSpaceToImageSpace, + aFill, aSubimage, aViewportSize, aSVGContext, + aWhichFrame, 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); + if (mOrientation.SwapsWidthAndHeight()) { + swap(viewportSize.width, viewportSize.height); + } + + return InnerImage()->Draw(aContext, aFilter, userSpaceToImageSpace, + aFill, subimage, viewportSize, aSVGContext, + aWhichFrame, aFlags); +} + +} // namespace image +} // namespace mozilla