tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

OrientedImage.cpp (11554B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "OrientedImage.h"
      7 
      8 #include <algorithm>
      9 
     10 #include "gfx2DGlue.h"
     11 #include "gfxContext.h"
     12 #include "gfxDrawable.h"
     13 #include "gfxPlatform.h"
     14 #include "gfxUtils.h"
     15 #include "ImageRegion.h"
     16 #include "mozilla/SVGImageContext.h"
     17 
     18 using std::swap;
     19 
     20 namespace mozilla {
     21 
     22 using namespace gfx;
     23 
     24 namespace image {
     25 
     26 NS_IMETHODIMP
     27 OrientedImage::GetWidth(int32_t* aWidth) {
     28  if (mOrientation.SwapsWidthAndHeight()) {
     29    return InnerImage()->GetHeight(aWidth);
     30  } else {
     31    return InnerImage()->GetWidth(aWidth);
     32  }
     33 }
     34 
     35 NS_IMETHODIMP
     36 OrientedImage::GetHeight(int32_t* aHeight) {
     37  if (mOrientation.SwapsWidthAndHeight()) {
     38    return InnerImage()->GetWidth(aHeight);
     39  } else {
     40    return InnerImage()->GetHeight(aHeight);
     41  }
     42 }
     43 
     44 nsresult OrientedImage::GetNativeSizes(nsTArray<IntSize>& aNativeSizes) {
     45  nsresult rv = InnerImage()->GetNativeSizes(aNativeSizes);
     46 
     47  if (mOrientation.SwapsWidthAndHeight()) {
     48    auto i = aNativeSizes.Length();
     49    while (i > 0) {
     50      --i;
     51      swap(aNativeSizes[i].width, aNativeSizes[i].height);
     52    }
     53  }
     54 
     55  return rv;
     56 }
     57 
     58 NS_IMETHODIMP
     59 OrientedImage::GetIntrinsicSizeInAppUnits(nsSize* aSize) {
     60  nsresult rv = InnerImage()->GetIntrinsicSizeInAppUnits(aSize);
     61 
     62  if (mOrientation.SwapsWidthAndHeight()) {
     63    swap(aSize->width, aSize->height);
     64  }
     65 
     66  return rv;
     67 }
     68 
     69 AspectRatio OrientedImage::GetIntrinsicRatio() {
     70  AspectRatio ratio = InnerImage()->GetIntrinsicRatio();
     71  if (ratio && mOrientation.SwapsWidthAndHeight()) {
     72    ratio = ratio.Inverted();
     73  }
     74  return ratio;
     75 }
     76 
     77 already_AddRefed<SourceSurface> OrientedImage::OrientSurface(
     78    Orientation aOrientation, SourceSurface* aSurface) {
     79  MOZ_ASSERT(aSurface);
     80 
     81  // If the image does not require any re-orientation, return aSurface itself.
     82  if (aOrientation.IsIdentity()) {
     83    return do_AddRef(aSurface);
     84  }
     85 
     86  // Determine the size of the new surface.
     87  nsIntSize originalSize = aSurface->GetSize();
     88  nsIntSize targetSize = originalSize;
     89  if (aOrientation.SwapsWidthAndHeight()) {
     90    swap(targetSize.width, targetSize.height);
     91  }
     92 
     93  // Create our drawable.
     94  RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(aSurface, originalSize);
     95 
     96  // Determine an appropriate format for the surface.
     97  gfx::SurfaceFormat surfaceFormat = IsOpaque(aSurface->GetFormat())
     98                                         ? gfx::SurfaceFormat::OS_RGBX
     99                                         : gfx::SurfaceFormat::OS_RGBA;
    100 
    101  // Create the new surface to draw into.
    102  RefPtr<DrawTarget> target =
    103      gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
    104          targetSize, surfaceFormat);
    105  if (!target || !target->IsValid()) {
    106    NS_ERROR("Could not create a DrawTarget");
    107    return nullptr;
    108  }
    109 
    110  // Draw.
    111  gfxContext ctx(target);
    112 
    113  ctx.Multiply(OrientationMatrix(aOrientation, originalSize));
    114  gfxUtils::DrawPixelSnapped(&ctx, drawable, SizeDouble(originalSize),
    115                             ImageRegion::Create(originalSize), surfaceFormat,
    116                             SamplingFilter::LINEAR);
    117 
    118  return target->Snapshot();
    119 }
    120 
    121 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
    122 OrientedImage::GetFrame(uint32_t aWhichFrame, uint32_t aFlags) {
    123  // Get a SourceSurface for the inner image then orient it according to
    124  // mOrientation.
    125  RefPtr<SourceSurface> innerSurface =
    126      InnerImage()->GetFrame(aWhichFrame, aFlags);
    127  NS_ENSURE_TRUE(innerSurface, nullptr);
    128 
    129  return OrientSurface(mOrientation, innerSurface);
    130 }
    131 
    132 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
    133 OrientedImage::GetFrameAtSize(const IntSize& aSize, uint32_t aWhichFrame,
    134                              uint32_t aFlags) {
    135  // Get a SourceSurface for the inner image then orient it according to
    136  // mOrientation.
    137  IntSize innerSize = aSize;
    138  if (mOrientation.SwapsWidthAndHeight()) {
    139    swap(innerSize.width, innerSize.height);
    140  }
    141  RefPtr<SourceSurface> innerSurface =
    142      InnerImage()->GetFrameAtSize(innerSize, aWhichFrame, aFlags);
    143  NS_ENSURE_TRUE(innerSurface, nullptr);
    144 
    145  return OrientSurface(mOrientation, innerSurface);
    146 }
    147 
    148 NS_IMETHODIMP_(bool)
    149 OrientedImage::IsImageContainerAvailable(WindowRenderer* aRenderer,
    150                                         uint32_t aFlags) {
    151  if (mOrientation.IsIdentity()) {
    152    return InnerImage()->IsImageContainerAvailable(aRenderer, aFlags);
    153  }
    154  return false;
    155 }
    156 
    157 NS_IMETHODIMP_(ImgDrawResult)
    158 OrientedImage::GetImageProvider(WindowRenderer* aRenderer,
    159                                const gfx::IntSize& aSize,
    160                                const SVGImageContext& aSVGContext,
    161                                const Maybe<ImageIntRegion>& aRegion,
    162                                uint32_t aFlags,
    163                                WebRenderImageProvider** aProvider) {
    164  // XXX(seth): We currently don't have a way of orienting the result of
    165  // GetImageContainer. We work around this by always returning null, but if it
    166  // ever turns out that OrientedImage is widely used on codepaths that can
    167  // actually benefit from GetImageContainer, it would be a good idea to fix
    168  // that method for performance reasons.
    169 
    170  if (mOrientation.IsIdentity()) {
    171    return InnerImage()->GetImageProvider(aRenderer, aSize, aSVGContext,
    172                                          aRegion, aFlags, aProvider);
    173  }
    174 
    175  return ImgDrawResult::NOT_SUPPORTED;
    176 }
    177 
    178 struct MatrixBuilder {
    179  explicit MatrixBuilder(bool aInvert) : mInvert(aInvert) {}
    180 
    181  gfxMatrix Build() { return mMatrix; }
    182 
    183  void Scale(gfxFloat aX, gfxFloat aY) {
    184    if (mInvert) {
    185      mMatrix *= gfxMatrix::Scaling(1.0 / aX, 1.0 / aY);
    186    } else {
    187      mMatrix.PreScale(aX, aY);
    188    }
    189  }
    190 
    191  void Rotate(gfxFloat aPhi) {
    192    if (mInvert) {
    193      mMatrix *= gfxMatrix::Rotation(-aPhi);
    194    } else {
    195      mMatrix.PreRotate(aPhi);
    196    }
    197  }
    198 
    199  void Translate(gfxPoint aDelta) {
    200    if (mInvert) {
    201      mMatrix *= gfxMatrix::Translation(-aDelta);
    202    } else {
    203      mMatrix.PreTranslate(aDelta);
    204    }
    205  }
    206 
    207 private:
    208  gfxMatrix mMatrix;
    209  bool mInvert;
    210 };
    211 
    212 gfxMatrix OrientedImage::OrientationMatrix(Orientation aOrientation,
    213                                           const nsIntSize& aSize,
    214                                           bool aInvert /* = false */) {
    215  MatrixBuilder builder(aInvert);
    216 
    217  // Apply reflection, if present. (For a regular, non-flipFirst reflection,
    218  // this logically happens second, but we apply it first because these
    219  // transformations are all premultiplied.) A translation is necessary to place
    220  // the image back in the first quadrant.
    221  if (aOrientation.flip == Flip::Horizontal && !aOrientation.flipFirst) {
    222    if (aOrientation.SwapsWidthAndHeight()) {
    223      builder.Translate(gfxPoint(aSize.height, 0));
    224    } else {
    225      builder.Translate(gfxPoint(aSize.width, 0));
    226    }
    227    builder.Scale(-1.0, 1.0);
    228  }
    229 
    230  // Apply rotation, if present. Again, a translation is used to place the
    231  // image back in the first quadrant.
    232  switch (aOrientation.rotation) {
    233    case Angle::D0:
    234      break;
    235    case Angle::D90:
    236      builder.Translate(gfxPoint(aSize.height, 0));
    237      builder.Rotate(-1.5 * M_PI);
    238      break;
    239    case Angle::D180:
    240      builder.Translate(gfxPoint(aSize.width, aSize.height));
    241      builder.Rotate(-1.0 * M_PI);
    242      break;
    243    case Angle::D270:
    244      builder.Translate(gfxPoint(0, aSize.width));
    245      builder.Rotate(-0.5 * M_PI);
    246      break;
    247    default:
    248      MOZ_ASSERT(false, "Invalid rotation value");
    249  }
    250 
    251  // Apply a flipFirst reflection.
    252  if (aOrientation.flip == Flip::Horizontal && aOrientation.flipFirst) {
    253    builder.Translate(gfxPoint(aSize.width, 0.0));
    254    builder.Scale(-1.0, 1.0);
    255  }
    256 
    257  return builder.Build();
    258 }
    259 
    260 NS_IMETHODIMP_(ImgDrawResult)
    261 OrientedImage::Draw(gfxContext* aContext, const nsIntSize& aSize,
    262                    const ImageRegion& aRegion, uint32_t aWhichFrame,
    263                    SamplingFilter aSamplingFilter,
    264                    const SVGImageContext& aSVGContext, uint32_t aFlags,
    265                    float aOpacity) {
    266  if (mOrientation.IsIdentity()) {
    267    return InnerImage()->Draw(aContext, aSize, aRegion, aWhichFrame,
    268                              aSamplingFilter, aSVGContext, aFlags, aOpacity);
    269  }
    270 
    271  // Update the image size to match the image's coordinate system. (This could
    272  // be done using TransformBounds but since it's only a size a swap is enough.)
    273  nsIntSize size(aSize);
    274  if (mOrientation.SwapsWidthAndHeight()) {
    275    swap(size.width, size.height);
    276  }
    277 
    278  // Update the matrix so that we transform the image into the orientation
    279  // expected by the caller before drawing.
    280  gfxMatrix matrix(OrientationMatrix(size));
    281  gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
    282  aContext->Multiply(matrix);
    283 
    284  // The region is already in the orientation expected by the caller, but we
    285  // need it to be in the image's coordinate system, so we transform it using
    286  // the inverse of the orientation matrix.
    287  gfxMatrix inverseMatrix(OrientationMatrix(size, /* aInvert = */ true));
    288  ImageRegion region(aRegion);
    289  region.TransformBoundsBy(inverseMatrix);
    290 
    291  auto orientViewport = [&](const SVGImageContext& aOldContext) {
    292    SVGImageContext context(aOldContext);
    293    auto oldViewport = aOldContext.GetViewportSize();
    294    if (oldViewport && mOrientation.SwapsWidthAndHeight()) {
    295      // Swap width and height:
    296      CSSIntSize newViewport(oldViewport->height, oldViewport->width);
    297      context.SetViewportSize(Some(newViewport));
    298    }
    299    return context;
    300  };
    301 
    302  return InnerImage()->Draw(aContext, size, region, aWhichFrame,
    303                            aSamplingFilter, orientViewport(aSVGContext),
    304                            aFlags, aOpacity);
    305 }
    306 
    307 nsIntSize OrientedImage::OptimalImageSizeForDest(const gfxSize& aDest,
    308                                                 uint32_t aWhichFrame,
    309                                                 SamplingFilter aSamplingFilter,
    310                                                 uint32_t aFlags) {
    311  if (!mOrientation.SwapsWidthAndHeight()) {
    312    return InnerImage()->OptimalImageSizeForDest(aDest, aWhichFrame,
    313                                                 aSamplingFilter, aFlags);
    314  }
    315 
    316  // Swap the size for the calculation, then swap it back for the caller.
    317  gfxSize destSize(aDest.height, aDest.width);
    318  nsIntSize innerImageSize(InnerImage()->OptimalImageSizeForDest(
    319      destSize, aWhichFrame, aSamplingFilter, aFlags));
    320  return nsIntSize(innerImageSize.height, innerImageSize.width);
    321 }
    322 
    323 NS_IMETHODIMP_(nsIntRect)
    324 OrientedImage::GetImageSpaceInvalidationRect(const nsIntRect& aRect) {
    325  nsIntRect rect(InnerImage()->GetImageSpaceInvalidationRect(aRect));
    326 
    327  if (mOrientation.IsIdentity()) {
    328    return rect;
    329  }
    330 
    331  nsIntSize innerSize;
    332  nsresult rv = InnerImage()->GetWidth(&innerSize.width);
    333  rv = NS_FAILED(rv) ? rv : InnerImage()->GetHeight(&innerSize.height);
    334  if (NS_FAILED(rv)) {
    335    // Fall back to identity if the width and height aren't available.
    336    return rect;
    337  }
    338 
    339  // Transform the invalidation rect into the correct orientation.
    340  gfxMatrix matrix(OrientationMatrix(innerSize));
    341  gfxRect invalidRect(matrix.TransformBounds(
    342      gfxRect(rect.X(), rect.Y(), rect.Width(), rect.Height())));
    343 
    344  return IntRect::RoundOut(invalidRect.X(), invalidRect.Y(),
    345                           invalidRect.Width(), invalidRect.Height());
    346 }
    347 
    348 }  // namespace image
    349 }  // namespace mozilla