1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/image/src/OrientedImage.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,252 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include <algorithm> 1.10 + 1.11 +#include "gfxDrawable.h" 1.12 +#include "gfxPlatform.h" 1.13 +#include "gfxUtils.h" 1.14 + 1.15 +#include "OrientedImage.h" 1.16 + 1.17 +using namespace mozilla::gfx; 1.18 + 1.19 +using std::swap; 1.20 +using mozilla::layers::LayerManager; 1.21 +using mozilla::layers::ImageContainer; 1.22 + 1.23 +namespace mozilla { 1.24 +namespace image { 1.25 + 1.26 +NS_IMPL_ISUPPORTS(OrientedImage, imgIContainer) 1.27 + 1.28 +nsIntRect 1.29 +OrientedImage::FrameRect(uint32_t aWhichFrame) 1.30 +{ 1.31 + if (mOrientation.SwapsWidthAndHeight()) { 1.32 + nsIntRect innerRect = InnerImage()->FrameRect(aWhichFrame); 1.33 + return nsIntRect(innerRect.x, innerRect.y, innerRect.height, innerRect.width); 1.34 + } else { 1.35 + return InnerImage()->FrameRect(aWhichFrame); 1.36 + } 1.37 +} 1.38 + 1.39 +NS_IMETHODIMP 1.40 +OrientedImage::GetWidth(int32_t* aWidth) 1.41 +{ 1.42 + if (mOrientation.SwapsWidthAndHeight()) { 1.43 + return InnerImage()->GetHeight(aWidth); 1.44 + } else { 1.45 + return InnerImage()->GetWidth(aWidth); 1.46 + } 1.47 +} 1.48 + 1.49 +NS_IMETHODIMP 1.50 +OrientedImage::GetHeight(int32_t* aHeight) 1.51 +{ 1.52 + if (mOrientation.SwapsWidthAndHeight()) { 1.53 + return InnerImage()->GetWidth(aHeight); 1.54 + } else { 1.55 + return InnerImage()->GetHeight(aHeight); 1.56 + } 1.57 +} 1.58 + 1.59 +NS_IMETHODIMP 1.60 +OrientedImage::GetIntrinsicSize(nsSize* aSize) 1.61 +{ 1.62 + nsresult rv = InnerImage()->GetIntrinsicSize(aSize); 1.63 + 1.64 + if (mOrientation.SwapsWidthAndHeight()) { 1.65 + swap(aSize->width, aSize->height); 1.66 + } 1.67 + 1.68 + return rv; 1.69 +} 1.70 + 1.71 +NS_IMETHODIMP 1.72 +OrientedImage::GetIntrinsicRatio(nsSize* aRatio) 1.73 +{ 1.74 + nsresult rv = InnerImage()->GetIntrinsicRatio(aRatio); 1.75 + 1.76 + if (mOrientation.SwapsWidthAndHeight()) { 1.77 + swap(aRatio->width, aRatio->height); 1.78 + } 1.79 + 1.80 + return rv; 1.81 +} 1.82 + 1.83 +NS_IMETHODIMP_(TemporaryRef<SourceSurface>) 1.84 +OrientedImage::GetFrame(uint32_t aWhichFrame, 1.85 + uint32_t aFlags) 1.86 +{ 1.87 + nsresult rv; 1.88 + 1.89 + if (mOrientation.IsIdentity()) { 1.90 + return InnerImage()->GetFrame(aWhichFrame, aFlags); 1.91 + } 1.92 + 1.93 + // Get the underlying dimensions. 1.94 + int32_t width, height; 1.95 + rv = InnerImage()->GetWidth(&width); 1.96 + NS_ENSURE_SUCCESS(rv, nullptr); 1.97 + rv = InnerImage()->GetHeight(&height); 1.98 + NS_ENSURE_SUCCESS(rv, nullptr); 1.99 + if (mOrientation.SwapsWidthAndHeight()) { 1.100 + swap(width, height); 1.101 + } 1.102 + NS_ENSURE_SUCCESS(rv, nullptr); 1.103 + 1.104 + // Determine an appropriate format for the surface. 1.105 + gfx::SurfaceFormat surfaceFormat; 1.106 + gfxImageFormat imageFormat; 1.107 + if (InnerImage()->FrameIsOpaque(aWhichFrame)) { 1.108 + surfaceFormat = gfx::SurfaceFormat::B8G8R8X8; 1.109 + imageFormat = gfxImageFormat::ARGB32; 1.110 + } else { 1.111 + surfaceFormat = gfx::SurfaceFormat::B8G8R8A8; 1.112 + imageFormat = gfxImageFormat::ARGB32; 1.113 + } 1.114 + 1.115 + // Create a surface to draw into. 1.116 + mozilla::RefPtr<DrawTarget> target = 1.117 + gfxPlatform::GetPlatform()-> 1.118 + CreateOffscreenContentDrawTarget(IntSize(width, height), surfaceFormat); 1.119 + 1.120 + // Create our drawable. 1.121 + RefPtr<SourceSurface> innerSurface = 1.122 + InnerImage()->GetFrame(aWhichFrame, aFlags); 1.123 + NS_ENSURE_TRUE(innerSurface, nullptr); 1.124 + nsRefPtr<gfxDrawable> drawable = 1.125 + new gfxSurfaceDrawable(innerSurface, gfxIntSize(width, height)); 1.126 + 1.127 + // Draw. 1.128 + nsRefPtr<gfxContext> ctx = new gfxContext(target); 1.129 + gfxRect imageRect(0, 0, width, height); 1.130 + gfxUtils::DrawPixelSnapped(ctx, drawable, OrientationMatrix(nsIntSize(width, height)), 1.131 + imageRect, imageRect, imageRect, imageRect, 1.132 + imageFormat, GraphicsFilter::FILTER_FAST); 1.133 + 1.134 + return target->Snapshot(); 1.135 +} 1.136 + 1.137 +NS_IMETHODIMP 1.138 +OrientedImage::GetImageContainer(LayerManager* aManager, ImageContainer** _retval) 1.139 +{ 1.140 + // XXX(seth): We currently don't have a way of orienting the result of 1.141 + // GetImageContainer. We work around this by always returning null, but if it 1.142 + // ever turns out that OrientedImage is widely used on codepaths that can 1.143 + // actually benefit from GetImageContainer, it would be a good idea to fix 1.144 + // that method for performance reasons. 1.145 + 1.146 + if (mOrientation.IsIdentity()) { 1.147 + return InnerImage()->GetImageContainer(aManager, _retval); 1.148 + } 1.149 + 1.150 + *_retval = nullptr; 1.151 + return NS_OK; 1.152 +} 1.153 + 1.154 +gfxMatrix 1.155 +OrientedImage::OrientationMatrix(const nsIntSize& aViewportSize) 1.156 +{ 1.157 + gfxMatrix matrix; 1.158 + 1.159 + int32_t width, height; 1.160 + if (InnerImage()->GetType() == imgIContainer::TYPE_VECTOR) { 1.161 + width = mOrientation.SwapsWidthAndHeight() ? aViewportSize.height : aViewportSize.width; 1.162 + height = mOrientation.SwapsWidthAndHeight() ? aViewportSize.width : aViewportSize.height; 1.163 + } else { 1.164 + nsresult rv = InnerImage()->GetWidth(&width); 1.165 + rv = NS_FAILED(rv) ? rv : InnerImage()->GetHeight(&height); 1.166 + if (NS_FAILED(rv)) { 1.167 + // Fall back to identity if the width and height aren't available. 1.168 + return matrix; 1.169 + } 1.170 + } 1.171 + 1.172 + // Apply reflection, if present. (This logically happens second, but we 1.173 + // apply it first because these transformations are all premultiplied.) A 1.174 + // translation is necessary to place the image back in the first quadrant. 1.175 + switch (mOrientation.flip) { 1.176 + case Flip::Unflipped: 1.177 + break; 1.178 + case Flip::Horizontal: 1.179 + if (mOrientation.SwapsWidthAndHeight()) { 1.180 + // In the coordinate system after the rotation the reflection we want 1.181 + // is across the x-axis rather than the y-axis. 1.182 + matrix.Translate(gfxPoint(0, height)); 1.183 + matrix.Scale(1.0, -1.0); 1.184 + } else { 1.185 + matrix.Translate(gfxPoint(width, 0)); 1.186 + matrix.Scale(-1.0, 1.0); 1.187 + } 1.188 + break; 1.189 + default: 1.190 + MOZ_ASSERT(false, "Invalid flip value"); 1.191 + } 1.192 + 1.193 + // Apply rotation, if present. Again, a translation is used to place the 1.194 + // image back in the first quadrant. 1.195 + switch (mOrientation.rotation) { 1.196 + case Angle::D0: 1.197 + break; 1.198 + case Angle::D90: 1.199 + matrix.Translate(gfxPoint(0, height)); 1.200 + matrix.Rotate(-0.5 * M_PI); 1.201 + break; 1.202 + case Angle::D180: 1.203 + matrix.Translate(gfxPoint(width, height)); 1.204 + matrix.Rotate(-1.0 * M_PI); 1.205 + break; 1.206 + case Angle::D270: 1.207 + matrix.Translate(gfxPoint(width, 0)); 1.208 + matrix.Rotate(-1.5 * M_PI); 1.209 + break; 1.210 + default: 1.211 + MOZ_ASSERT(false, "Invalid rotation value"); 1.212 + } 1.213 + 1.214 + return matrix; 1.215 +} 1.216 + 1.217 +NS_IMETHODIMP 1.218 +OrientedImage::Draw(gfxContext* aContext, 1.219 + GraphicsFilter aFilter, 1.220 + const gfxMatrix& aUserSpaceToImageSpace, 1.221 + const gfxRect& aFill, 1.222 + const nsIntRect& aSubimage, 1.223 + const nsIntSize& aViewportSize, 1.224 + const SVGImageContext* aSVGContext, 1.225 + uint32_t aWhichFrame, 1.226 + uint32_t aFlags) 1.227 +{ 1.228 + if (mOrientation.IsIdentity()) { 1.229 + return InnerImage()->Draw(aContext, aFilter, aUserSpaceToImageSpace, 1.230 + aFill, aSubimage, aViewportSize, aSVGContext, 1.231 + aWhichFrame, aFlags); 1.232 + } 1.233 + 1.234 + // Update the matrix to reorient the image. 1.235 + gfxMatrix matrix(OrientationMatrix(aViewportSize)); 1.236 + gfxMatrix userSpaceToImageSpace(aUserSpaceToImageSpace * matrix); 1.237 + 1.238 + // Update the subimage. 1.239 + gfxRect gfxSubimage(matrix.TransformBounds(gfxRect(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height))); 1.240 + nsIntRect subimage(gfxSubimage.x, gfxSubimage.y, gfxSubimage.width, gfxSubimage.height); 1.241 + 1.242 + // Update the viewport size. (This could be done using TransformBounds but 1.243 + // since it's only a size a swap is enough.) 1.244 + nsIntSize viewportSize(aViewportSize); 1.245 + if (mOrientation.SwapsWidthAndHeight()) { 1.246 + swap(viewportSize.width, viewportSize.height); 1.247 + } 1.248 + 1.249 + return InnerImage()->Draw(aContext, aFilter, userSpaceToImageSpace, 1.250 + aFill, subimage, viewportSize, aSVGContext, 1.251 + aWhichFrame, aFlags); 1.252 +} 1.253 + 1.254 +} // namespace image 1.255 +} // namespace mozilla