2013-08-25 00:19:43 -07:00
|
|
|
/* -*- 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"
|
|
|
|
|
2014-04-15 11:02:23 -07:00
|
|
|
using namespace mozilla::gfx;
|
|
|
|
|
2013-08-25 00:19:43 -07:00
|
|
|
using std::swap;
|
|
|
|
using mozilla::layers::LayerManager;
|
|
|
|
using mozilla::layers::ImageContainer;
|
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
namespace image {
|
|
|
|
|
2014-04-27 00:06:00 -07:00
|
|
|
NS_IMPL_ISUPPORTS(OrientedImage, imgIContainer)
|
2013-08-25 00:19:43 -07:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-04-15 11:02:23 -07:00
|
|
|
NS_IMETHODIMP_(TemporaryRef<SourceSurface>)
|
2013-08-25 00:19:43 -07:00
|
|
|
OrientedImage::GetFrame(uint32_t aWhichFrame,
|
2013-12-13 00:34:24 -08:00
|
|
|
uint32_t aFlags)
|
2013-08-25 00:19:43 -07:00
|
|
|
{
|
|
|
|
nsresult rv;
|
|
|
|
|
|
|
|
if (mOrientation.IsIdentity()) {
|
2013-12-13 00:34:24 -08:00
|
|
|
return InnerImage()->GetFrame(aWhichFrame, aFlags);
|
2013-08-25 00:19:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Get the underlying dimensions.
|
|
|
|
int32_t width, height;
|
2014-04-15 11:02:23 -07:00
|
|
|
rv = InnerImage()->GetWidth(&width);
|
|
|
|
NS_ENSURE_SUCCESS(rv, nullptr);
|
|
|
|
rv = InnerImage()->GetHeight(&height);
|
|
|
|
NS_ENSURE_SUCCESS(rv, nullptr);
|
2013-08-25 00:19:43 -07:00
|
|
|
if (mOrientation.SwapsWidthAndHeight()) {
|
2014-04-15 11:02:23 -07:00
|
|
|
swap(width, height);
|
2013-08-25 00:19:43 -07:00
|
|
|
}
|
2013-12-13 00:34:24 -08:00
|
|
|
NS_ENSURE_SUCCESS(rv, nullptr);
|
2013-08-25 00:19:43 -07:00
|
|
|
|
|
|
|
// Determine an appropriate format for the surface.
|
|
|
|
gfx::SurfaceFormat surfaceFormat;
|
|
|
|
if (InnerImage()->FrameIsOpaque(aWhichFrame)) {
|
2014-01-10 11:06:16 -08:00
|
|
|
surfaceFormat = gfx::SurfaceFormat::B8G8R8X8;
|
2013-08-25 00:19:43 -07:00
|
|
|
} else {
|
2014-01-10 11:06:16 -08:00
|
|
|
surfaceFormat = gfx::SurfaceFormat::B8G8R8A8;
|
2013-08-25 00:19:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create a surface to draw into.
|
2014-04-15 11:02:23 -07:00
|
|
|
mozilla::RefPtr<DrawTarget> target =
|
|
|
|
gfxPlatform::GetPlatform()->
|
|
|
|
CreateOffscreenContentDrawTarget(IntSize(width, height), surfaceFormat);
|
2014-05-31 03:26:04 -07:00
|
|
|
if (!target) {
|
|
|
|
NS_ERROR("Could not create a DrawTarget");
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2013-08-25 00:19:43 -07:00
|
|
|
|
|
|
|
// Create our drawable.
|
2014-04-15 11:02:23 -07:00
|
|
|
RefPtr<SourceSurface> innerSurface =
|
2013-12-13 00:34:24 -08:00
|
|
|
InnerImage()->GetFrame(aWhichFrame, aFlags);
|
|
|
|
NS_ENSURE_TRUE(innerSurface, nullptr);
|
2013-08-25 00:19:43 -07:00
|
|
|
nsRefPtr<gfxDrawable> drawable =
|
|
|
|
new gfxSurfaceDrawable(innerSurface, gfxIntSize(width, height));
|
|
|
|
|
|
|
|
// Draw.
|
2014-01-19 17:17:38 -08:00
|
|
|
nsRefPtr<gfxContext> ctx = new gfxContext(target);
|
2013-08-25 00:19:43 -07:00
|
|
|
gfxRect imageRect(0, 0, width, height);
|
|
|
|
gfxUtils::DrawPixelSnapped(ctx, drawable, OrientationMatrix(nsIntSize(width, height)),
|
|
|
|
imageRect, imageRect, imageRect, imageRect,
|
2014-04-19 18:28:38 -07:00
|
|
|
surfaceFormat, GraphicsFilter::FILTER_FAST);
|
2014-01-19 17:17:38 -08:00
|
|
|
|
2014-04-15 11:02:23 -07:00
|
|
|
return target->Snapshot();
|
2013-08-25 00:19:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
2013-10-01 14:01:19 -07:00
|
|
|
GraphicsFilter aFilter,
|
2013-08-25 00:19:43 -07:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2014-06-26 16:02:04 -07:00
|
|
|
NS_IMETHODIMP_(nsIntRect)
|
|
|
|
OrientedImage::GetImageSpaceInvalidationRect(const nsIntRect& aRect)
|
|
|
|
{
|
|
|
|
nsIntRect rect(InnerImage()->GetImageSpaceInvalidationRect(aRect));
|
|
|
|
|
|
|
|
if (mOrientation.IsIdentity()) {
|
|
|
|
return rect;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t width, height;
|
|
|
|
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 rect;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Transform the invalidation rect into the correct orientation.
|
|
|
|
gfxMatrix matrix(OrientationMatrix(nsIntSize(width, height)).Invert());
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-08-25 00:19:43 -07:00
|
|
|
} // namespace image
|
|
|
|
} // namespace mozilla
|