michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: michael@0: #include "gfxDrawable.h" michael@0: #include "gfxPlatform.h" michael@0: #include "gfxUtils.h" michael@0: michael@0: #include "OrientedImage.h" michael@0: michael@0: using namespace mozilla::gfx; michael@0: michael@0: using std::swap; michael@0: using mozilla::layers::LayerManager; michael@0: using mozilla::layers::ImageContainer; michael@0: michael@0: namespace mozilla { michael@0: namespace image { michael@0: michael@0: NS_IMPL_ISUPPORTS(OrientedImage, imgIContainer) michael@0: michael@0: nsIntRect michael@0: OrientedImage::FrameRect(uint32_t aWhichFrame) michael@0: { michael@0: if (mOrientation.SwapsWidthAndHeight()) { michael@0: nsIntRect innerRect = InnerImage()->FrameRect(aWhichFrame); michael@0: return nsIntRect(innerRect.x, innerRect.y, innerRect.height, innerRect.width); michael@0: } else { michael@0: return InnerImage()->FrameRect(aWhichFrame); michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: OrientedImage::GetWidth(int32_t* aWidth) michael@0: { michael@0: if (mOrientation.SwapsWidthAndHeight()) { michael@0: return InnerImage()->GetHeight(aWidth); michael@0: } else { michael@0: return InnerImage()->GetWidth(aWidth); michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: OrientedImage::GetHeight(int32_t* aHeight) michael@0: { michael@0: if (mOrientation.SwapsWidthAndHeight()) { michael@0: return InnerImage()->GetWidth(aHeight); michael@0: } else { michael@0: return InnerImage()->GetHeight(aHeight); michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: OrientedImage::GetIntrinsicSize(nsSize* aSize) michael@0: { michael@0: nsresult rv = InnerImage()->GetIntrinsicSize(aSize); michael@0: michael@0: if (mOrientation.SwapsWidthAndHeight()) { michael@0: swap(aSize->width, aSize->height); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: OrientedImage::GetIntrinsicRatio(nsSize* aRatio) michael@0: { michael@0: nsresult rv = InnerImage()->GetIntrinsicRatio(aRatio); michael@0: michael@0: if (mOrientation.SwapsWidthAndHeight()) { michael@0: swap(aRatio->width, aRatio->height); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(TemporaryRef) michael@0: OrientedImage::GetFrame(uint32_t aWhichFrame, michael@0: uint32_t aFlags) michael@0: { michael@0: nsresult rv; michael@0: michael@0: if (mOrientation.IsIdentity()) { michael@0: return InnerImage()->GetFrame(aWhichFrame, aFlags); michael@0: } michael@0: michael@0: // Get the underlying dimensions. michael@0: int32_t width, height; michael@0: rv = InnerImage()->GetWidth(&width); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: rv = InnerImage()->GetHeight(&height); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: if (mOrientation.SwapsWidthAndHeight()) { michael@0: swap(width, height); michael@0: } michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: michael@0: // Determine an appropriate format for the surface. michael@0: gfx::SurfaceFormat surfaceFormat; michael@0: gfxImageFormat imageFormat; michael@0: if (InnerImage()->FrameIsOpaque(aWhichFrame)) { michael@0: surfaceFormat = gfx::SurfaceFormat::B8G8R8X8; michael@0: imageFormat = gfxImageFormat::ARGB32; michael@0: } else { michael@0: surfaceFormat = gfx::SurfaceFormat::B8G8R8A8; michael@0: imageFormat = gfxImageFormat::ARGB32; michael@0: } michael@0: michael@0: // Create a surface to draw into. michael@0: mozilla::RefPtr target = michael@0: gfxPlatform::GetPlatform()-> michael@0: CreateOffscreenContentDrawTarget(IntSize(width, height), surfaceFormat); michael@0: michael@0: // Create our drawable. michael@0: RefPtr innerSurface = michael@0: InnerImage()->GetFrame(aWhichFrame, aFlags); michael@0: NS_ENSURE_TRUE(innerSurface, nullptr); michael@0: nsRefPtr drawable = michael@0: new gfxSurfaceDrawable(innerSurface, gfxIntSize(width, height)); michael@0: michael@0: // Draw. michael@0: nsRefPtr ctx = new gfxContext(target); michael@0: gfxRect imageRect(0, 0, width, height); michael@0: gfxUtils::DrawPixelSnapped(ctx, drawable, OrientationMatrix(nsIntSize(width, height)), michael@0: imageRect, imageRect, imageRect, imageRect, michael@0: imageFormat, GraphicsFilter::FILTER_FAST); michael@0: michael@0: return target->Snapshot(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: OrientedImage::GetImageContainer(LayerManager* aManager, ImageContainer** _retval) michael@0: { michael@0: // XXX(seth): We currently don't have a way of orienting the result of michael@0: // GetImageContainer. We work around this by always returning null, but if it michael@0: // ever turns out that OrientedImage is widely used on codepaths that can michael@0: // actually benefit from GetImageContainer, it would be a good idea to fix michael@0: // that method for performance reasons. michael@0: michael@0: if (mOrientation.IsIdentity()) { michael@0: return InnerImage()->GetImageContainer(aManager, _retval); michael@0: } michael@0: michael@0: *_retval = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: gfxMatrix michael@0: OrientedImage::OrientationMatrix(const nsIntSize& aViewportSize) michael@0: { michael@0: gfxMatrix matrix; michael@0: michael@0: int32_t width, height; michael@0: if (InnerImage()->GetType() == imgIContainer::TYPE_VECTOR) { michael@0: width = mOrientation.SwapsWidthAndHeight() ? aViewportSize.height : aViewportSize.width; michael@0: height = mOrientation.SwapsWidthAndHeight() ? aViewportSize.width : aViewportSize.height; michael@0: } else { michael@0: nsresult rv = InnerImage()->GetWidth(&width); michael@0: rv = NS_FAILED(rv) ? rv : InnerImage()->GetHeight(&height); michael@0: if (NS_FAILED(rv)) { michael@0: // Fall back to identity if the width and height aren't available. michael@0: return matrix; michael@0: } michael@0: } michael@0: michael@0: // Apply reflection, if present. (This logically happens second, but we michael@0: // apply it first because these transformations are all premultiplied.) A michael@0: // translation is necessary to place the image back in the first quadrant. michael@0: switch (mOrientation.flip) { michael@0: case Flip::Unflipped: michael@0: break; michael@0: case Flip::Horizontal: michael@0: if (mOrientation.SwapsWidthAndHeight()) { michael@0: // In the coordinate system after the rotation the reflection we want michael@0: // is across the x-axis rather than the y-axis. michael@0: matrix.Translate(gfxPoint(0, height)); michael@0: matrix.Scale(1.0, -1.0); michael@0: } else { michael@0: matrix.Translate(gfxPoint(width, 0)); michael@0: matrix.Scale(-1.0, 1.0); michael@0: } michael@0: break; michael@0: default: michael@0: MOZ_ASSERT(false, "Invalid flip value"); michael@0: } michael@0: michael@0: // Apply rotation, if present. Again, a translation is used to place the michael@0: // image back in the first quadrant. michael@0: switch (mOrientation.rotation) { michael@0: case Angle::D0: michael@0: break; michael@0: case Angle::D90: michael@0: matrix.Translate(gfxPoint(0, height)); michael@0: matrix.Rotate(-0.5 * M_PI); michael@0: break; michael@0: case Angle::D180: michael@0: matrix.Translate(gfxPoint(width, height)); michael@0: matrix.Rotate(-1.0 * M_PI); michael@0: break; michael@0: case Angle::D270: michael@0: matrix.Translate(gfxPoint(width, 0)); michael@0: matrix.Rotate(-1.5 * M_PI); michael@0: break; michael@0: default: michael@0: MOZ_ASSERT(false, "Invalid rotation value"); michael@0: } michael@0: michael@0: return matrix; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: OrientedImage::Draw(gfxContext* aContext, michael@0: GraphicsFilter aFilter, michael@0: const gfxMatrix& aUserSpaceToImageSpace, michael@0: const gfxRect& aFill, michael@0: const nsIntRect& aSubimage, michael@0: const nsIntSize& aViewportSize, michael@0: const SVGImageContext* aSVGContext, michael@0: uint32_t aWhichFrame, michael@0: uint32_t aFlags) michael@0: { michael@0: if (mOrientation.IsIdentity()) { michael@0: return InnerImage()->Draw(aContext, aFilter, aUserSpaceToImageSpace, michael@0: aFill, aSubimage, aViewportSize, aSVGContext, michael@0: aWhichFrame, aFlags); michael@0: } michael@0: michael@0: // Update the matrix to reorient the image. michael@0: gfxMatrix matrix(OrientationMatrix(aViewportSize)); michael@0: gfxMatrix userSpaceToImageSpace(aUserSpaceToImageSpace * matrix); michael@0: michael@0: // Update the subimage. michael@0: gfxRect gfxSubimage(matrix.TransformBounds(gfxRect(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height))); michael@0: nsIntRect subimage(gfxSubimage.x, gfxSubimage.y, gfxSubimage.width, gfxSubimage.height); michael@0: michael@0: // Update the viewport size. (This could be done using TransformBounds but michael@0: // since it's only a size a swap is enough.) michael@0: nsIntSize viewportSize(aViewportSize); michael@0: if (mOrientation.SwapsWidthAndHeight()) { michael@0: swap(viewportSize.width, viewportSize.height); michael@0: } michael@0: michael@0: return InnerImage()->Draw(aContext, aFilter, userSpaceToImageSpace, michael@0: aFill, subimage, viewportSize, aSVGContext, michael@0: aWhichFrame, aFlags); michael@0: } michael@0: michael@0: } // namespace image michael@0: } // namespace mozilla