Bug 869723 (Part 4) - Add OrientedImage. r=joe

This commit is contained in:
Seth Fowler 2013-08-24 17:31:15 -07:00
parent c217638415
commit 9d09a6075a
6 changed files with 351 additions and 1 deletions

View File

@ -7,6 +7,7 @@
#include "imgIContainer.h"
#include "ClippedImage.h"
#include "FrozenImage.h"
#include "OrientedImage.h"
#include "Image.h"
#include "ImageOps.h"
@ -44,5 +45,20 @@ ImageOps::Clip(imgIContainer* aImage, nsIntRect aClip)
return clippedImage.forget();
}
/* static */ already_AddRefed<Image>
ImageOps::Orient(Image* aImage, Orientation aOrientation)
{
nsRefPtr<Image> orientedImage = new OrientedImage(aImage, aOrientation);
return orientedImage.forget();
}
/* static */ already_AddRefed<imgIContainer>
ImageOps::Orient(imgIContainer* aImage, Orientation aOrientation)
{
nsCOMPtr<imgIContainer> orientedImage =
new OrientedImage(static_cast<Image*>(aImage), aOrientation);
return orientedImage.forget();
}
} // namespace image
} // namespace mozilla

View File

@ -9,6 +9,7 @@
#include "nsCOMPtr.h"
#include "nsRect.h"
#include "Orientation.h"
class imgIContainer;
@ -38,6 +39,16 @@ public:
static already_AddRefed<Image> Clip(Image* aImage, nsIntRect aClip);
static already_AddRefed<imgIContainer> Clip(imgIContainer* aImage, nsIntRect aClip);
/**
* Creates a version of an existing image which is rotated and/or flipped to
* the specified orientation.
*
* @param aImage The existing image.
* @param aOrientation The desired orientation.
*/
static already_AddRefed<Image> Orient(Image* aImage, Orientation aOrientation);
static already_AddRefed<imgIContainer> Orient(imgIContainer* aImage, Orientation aOrientation);
private:
// This is a static utility class, so disallow instantiation.
virtual ~ImageOps() = 0;

254
image/src/OrientedImage.cpp Normal file
View File

@ -0,0 +1,254 @@
/* -*- 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 "gfxDrawable.h"
#include "gfxPlatform.h"
#include "gfxUtils.h"
#include "OrientedImage.h"
using std::swap;
using mozilla::layers::LayerManager;
using mozilla::layers::ImageContainer;
namespace mozilla {
namespace image {
NS_IMPL_ISUPPORTS1(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
OrientedImage::GetFrame(uint32_t aWhichFrame,
uint32_t aFlags,
gfxASurface** _retval)
{
nsresult rv;
if (mOrientation.IsIdentity()) {
return InnerImage()->GetFrame(aWhichFrame, aFlags, _retval);
}
// Get the underlying dimensions.
int32_t width, height;
if (mOrientation.SwapsWidthAndHeight()) {
rv = InnerImage()->GetWidth(&height);
rv = NS_FAILED(rv) ? rv : InnerImage()->GetHeight(&width);
} else {
rv = InnerImage()->GetWidth(&width);
rv = NS_FAILED(rv) ? rv : InnerImage()->GetHeight(&height);
}
NS_ENSURE_SUCCESS(rv, rv);
// Determine an appropriate format for the surface.
gfx::SurfaceFormat surfaceFormat;
gfxImageFormat imageFormat;
if (InnerImage()->FrameIsOpaque(aWhichFrame)) {
surfaceFormat = gfx::FORMAT_B8G8R8X8;
imageFormat = gfxASurface::ImageFormatARGB32;
} else {
surfaceFormat = gfx::FORMAT_B8G8R8A8;
imageFormat = gfxASurface::ImageFormatARGB32;
}
// Create a surface to draw into.
mozilla::RefPtr<mozilla::gfx::DrawTarget> target;
target = gfxPlatform::GetPlatform()->
CreateOffscreenDrawTarget(gfx::IntSize(width, height), surfaceFormat);
nsRefPtr<gfxASurface> surface = gfxPlatform::GetPlatform()->
GetThebesSurfaceForDrawTarget(target);
// Create our drawable.
nsRefPtr<gfxASurface> innerSurface;
rv = InnerImage()->GetFrame(aWhichFrame, aFlags, getter_AddRefs(innerSurface));
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<gfxDrawable> drawable =
new gfxSurfaceDrawable(innerSurface, gfxIntSize(width, height));
// Draw.
nsRefPtr<gfxContext> ctx = new gfxContext(surface);
gfxRect imageRect(0, 0, width, height);
gfxUtils::DrawPixelSnapped(ctx, drawable, OrientationMatrix(nsIntSize(width, height)),
imageRect, imageRect, imageRect, imageRect,
imageFormat, gfxPattern::FILTER_FAST);
surface.forget(_retval);
return NS_OK;
}
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,
gfxPattern::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

67
image/src/OrientedImage.h Normal file
View File

@ -0,0 +1,67 @@
/* -*- 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/. */
#ifndef MOZILLA_IMAGELIB_ORIENTEDIMAGE_H_
#define MOZILLA_IMAGELIB_ORIENTEDIMAGE_H_
#include "ImageWrapper.h"
#include "Orientation.h"
namespace mozilla {
namespace image {
/**
* An Image wrapper that rotates and/or flips an image according to a specified
* Orientation.
*
* XXX(seth): There a known (performance, not correctness) issue with
* GetImageContainer. See the comments for that method for more information.
*/
class OrientedImage : public ImageWrapper
{
public:
NS_DECL_ISUPPORTS
virtual ~OrientedImage() { }
virtual nsIntRect FrameRect(uint32_t aWhichFrame) MOZ_OVERRIDE;
NS_IMETHOD GetWidth(int32_t* aWidth) MOZ_OVERRIDE;
NS_IMETHOD GetHeight(int32_t* aHeight) MOZ_OVERRIDE;
NS_IMETHOD GetIntrinsicSize(nsSize* aSize) MOZ_OVERRIDE;
NS_IMETHOD GetIntrinsicRatio(nsSize* aRatio) MOZ_OVERRIDE;
NS_IMETHOD GetFrame(uint32_t aWhichFrame,
uint32_t aFlags,
gfxASurface** _retval) MOZ_OVERRIDE;
NS_IMETHOD GetImageContainer(mozilla::layers::LayerManager* aManager,
mozilla::layers::ImageContainer** _retval) MOZ_OVERRIDE;
NS_IMETHOD Draw(gfxContext* aContext,
gfxPattern::GraphicsFilter aFilter,
const gfxMatrix& aUserSpaceToImageSpace,
const gfxRect& aFill,
const nsIntRect& aSubimage,
const nsIntSize& aViewportSize,
const SVGImageContext* aSVGContext,
uint32_t aWhichFrame,
uint32_t aFlags) MOZ_OVERRIDE;
protected:
OrientedImage(Image* aImage, Orientation aOrientation)
: ImageWrapper(aImage)
, mOrientation(aOrientation)
{ }
gfxMatrix OrientationMatrix(const nsIntSize& aViewportSize);
private:
Orientation mOrientation;
friend class ImageOps;
};
} // namespace image
} // namespace mozilla
#endif // MOZILLA_IMAGELIB_ORIENTEDIMAGE_H_

View File

@ -468,7 +468,7 @@ void imgFrame::Draw(gfxContext *aContext, gfxPattern::GraphicsFilter aFilter,
}
gfxMatrix userSpaceToImageSpace = aUserSpaceToImageSpace;
gfxRect sourceRect = userSpaceToImageSpace.Transform(aFill);
gfxRect sourceRect = userSpaceToImageSpace.TransformBounds(aFill);
gfxRect imageRect(0, 0, mSize.width + aPadding.LeftRight(),
mSize.height + aPadding.TopBottom());
gfxRect subimage(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height);

View File

@ -8,6 +8,7 @@ MODULE = 'imglib2'
EXPORTS += [
'ImageOps.h',
'Orientation.h',
'imgLoader.h',
'imgRequest.h',
'imgRequestProxy.h',
@ -26,6 +27,7 @@ CPP_SOURCES += [
'ImageMetadata.cpp',
'ImageOps.cpp',
'ImageWrapper.cpp',
'OrientedImage.cpp',
'RasterImage.cpp',
'SVGDocumentWrapper.cpp',
'ScriptedNotificationObserver.cpp',