gfx/layers/RotatedBuffer.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 20; 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/. */
     6 #include "RotatedBuffer.h"
     7 #include <sys/types.h>                  // for int32_t
     8 #include <algorithm>                    // for max
     9 #include "BasicImplData.h"              // for BasicImplData
    10 #include "BasicLayersImpl.h"            // for ToData
    11 #include "BufferUnrotate.h"             // for BufferUnrotate
    12 #include "GeckoProfiler.h"              // for PROFILER_LABEL
    13 #include "Layers.h"                     // for ThebesLayer, Layer, etc
    14 #include "gfxPlatform.h"                // for gfxPlatform
    15 #include "gfxPrefs.h"                   // for gfxPrefs
    16 #include "gfxUtils.h"                   // for gfxUtils
    17 #include "mozilla/ArrayUtils.h"         // for ArrayLength
    18 #include "mozilla/gfx/BasePoint.h"      // for BasePoint
    19 #include "mozilla/gfx/BaseRect.h"       // for BaseRect
    20 #include "mozilla/gfx/BaseSize.h"       // for BaseSize
    21 #include "mozilla/gfx/Matrix.h"         // for Matrix
    22 #include "mozilla/gfx/Point.h"          // for Point, IntPoint
    23 #include "mozilla/gfx/Rect.h"           // for Rect, IntRect
    24 #include "mozilla/gfx/Types.h"          // for ExtendMode::ExtendMode::CLAMP, etc
    25 #include "mozilla/layers/ShadowLayers.h"  // for ShadowableLayer
    26 #include "mozilla/layers/TextureClient.h"  // for TextureClient
    27 #include "nsSize.h"                     // for nsIntSize
    28 #include "gfx2DGlue.h"
    30 namespace mozilla {
    32 using namespace gfx;
    34 namespace layers {
    36 nsIntRect
    37 RotatedBuffer::GetQuadrantRectangle(XSide aXSide, YSide aYSide) const
    38 {
    39   // quadrantTranslation is the amount we translate the top-left
    40   // of the quadrant by to get coordinates relative to the layer
    41   nsIntPoint quadrantTranslation = -mBufferRotation;
    42   quadrantTranslation.x += aXSide == LEFT ? mBufferRect.width : 0;
    43   quadrantTranslation.y += aYSide == TOP ? mBufferRect.height : 0;
    44   return mBufferRect + quadrantTranslation;
    45 }
    47 Rect
    48 RotatedBuffer::GetSourceRectangle(XSide aXSide, YSide aYSide) const
    49 {
    50   Rect result;
    51   if (aXSide == LEFT) {
    52     result.x = 0;
    53     result.width = mBufferRotation.x;
    54   } else {
    55     result.x = mBufferRotation.x;
    56     result.width = mBufferRect.width - mBufferRotation.x;
    57   }
    58   if (aYSide == TOP) {
    59     result.y = 0;
    60     result.height = mBufferRotation.y;
    61   } else {
    62     result.y = mBufferRotation.y;
    63     result.height = mBufferRect.height - mBufferRotation.y;
    64   }
    65   return result;
    66 }
    68 /**
    69  * @param aXSide LEFT means we draw from the left side of the buffer (which
    70  * is drawn on the right side of mBufferRect). RIGHT means we draw from
    71  * the right side of the buffer (which is drawn on the left side of
    72  * mBufferRect).
    73  * @param aYSide TOP means we draw from the top side of the buffer (which
    74  * is drawn on the bottom side of mBufferRect). BOTTOM means we draw from
    75  * the bottom side of the buffer (which is drawn on the top side of
    76  * mBufferRect).
    77  */
    78 void
    79 RotatedBuffer::DrawBufferQuadrant(gfx::DrawTarget* aTarget,
    80                                   XSide aXSide, YSide aYSide,
    81                                   ContextSource aSource,
    82                                   float aOpacity,
    83                                   gfx::CompositionOp aOperator,
    84                                   gfx::SourceSurface* aMask,
    85                                   const gfx::Matrix* aMaskTransform) const
    86 {
    87   // The rectangle that we're going to fill. Basically we're going to
    88   // render the buffer at mBufferRect + quadrantTranslation to get the
    89   // pixels in the right place, but we're only going to paint within
    90   // mBufferRect
    91   nsIntRect quadrantRect = GetQuadrantRectangle(aXSide, aYSide);
    92   nsIntRect fillRect;
    93   if (!fillRect.IntersectRect(mBufferRect, quadrantRect))
    94     return;
    96   gfx::Point quadrantTranslation(quadrantRect.x, quadrantRect.y);
    98   MOZ_ASSERT(aOperator == CompositionOp::OP_OVER || aOperator == CompositionOp::OP_SOURCE);
    99   // direct2d is much slower when using OP_SOURCE so use OP_OVER and
   100   // (maybe) a clear instead. Normally we need to draw in a single operation
   101   // (to avoid flickering) but direct2d is ok since it defers rendering.
   102   // We should try abstract this logic in a helper when we have other use
   103   // cases.
   104   if (aTarget->GetType() == BackendType::DIRECT2D && aOperator == CompositionOp::OP_SOURCE) {
   105     aOperator = CompositionOp::OP_OVER;
   106     if (mDTBuffer->GetFormat() == SurfaceFormat::B8G8R8A8) {
   107       aTarget->ClearRect(ToRect(fillRect));
   108     }
   109   }
   111   RefPtr<gfx::SourceSurface> snapshot;
   112   if (aSource == BUFFER_BLACK) {
   113     snapshot = mDTBuffer->Snapshot();
   114   } else {
   115     MOZ_ASSERT(aSource == BUFFER_WHITE);
   116     snapshot = mDTBufferOnWhite->Snapshot();
   117   }
   119   if (aOperator == CompositionOp::OP_SOURCE) {
   120     // OP_SOURCE is unbounded in Azure, and we really don't want that behaviour here.
   121     // We also can't do a ClearRect+FillRect since we need the drawing to happen
   122     // as an atomic operation (to prevent flickering).
   123     aTarget->PushClipRect(gfx::Rect(fillRect.x, fillRect.y,
   124                                     fillRect.width, fillRect.height));
   125   }
   127   if (aMask) {
   128     Matrix oldTransform = aTarget->GetTransform();
   130     // Transform from user -> buffer space.
   131     Matrix transform;
   132     transform.Translate(quadrantTranslation.x, quadrantTranslation.y);
   134     Matrix inverseMask = *aMaskTransform;
   135     inverseMask.Invert();
   137     transform *= oldTransform;
   138     transform *= inverseMask;
   140 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
   141     SurfacePattern source(snapshot, ExtendMode::CLAMP, transform, Filter::POINT);
   142 #else
   143     SurfacePattern source(snapshot, ExtendMode::CLAMP, transform);
   144 #endif
   146     aTarget->SetTransform(*aMaskTransform);
   147     aTarget->MaskSurface(source, aMask, Point(0, 0), DrawOptions(aOpacity, aOperator));
   148     aTarget->SetTransform(oldTransform);
   149   } else {
   150 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
   151     DrawSurfaceOptions options(Filter::POINT);
   152 #else
   153     DrawSurfaceOptions options;
   154 #endif
   155     aTarget->DrawSurface(snapshot, ToRect(fillRect),
   156                          GetSourceRectangle(aXSide, aYSide),
   157                          options,
   158                          DrawOptions(aOpacity, aOperator));
   159   }
   161   if (aOperator == CompositionOp::OP_SOURCE) {
   162     aTarget->PopClip();
   163   }
   164 }
   166 void
   167 RotatedBuffer::DrawBufferWithRotation(gfx::DrawTarget *aTarget, ContextSource aSource,
   168                                       float aOpacity,
   169                                       gfx::CompositionOp aOperator,
   170                                       gfx::SourceSurface* aMask,
   171                                       const gfx::Matrix* aMaskTransform) const
   172 {
   173   PROFILER_LABEL("RotatedBuffer", "DrawBufferWithRotation");
   174   // See above, in Azure Repeat should always be a safe, even faster choice
   175   // though! Particularly on D2D Repeat should be a lot faster, need to look
   176   // into that. TODO[Bas]
   177   DrawBufferQuadrant(aTarget, LEFT, TOP, aSource, aOpacity, aOperator, aMask, aMaskTransform);
   178   DrawBufferQuadrant(aTarget, RIGHT, TOP, aSource, aOpacity, aOperator, aMask, aMaskTransform);
   179   DrawBufferQuadrant(aTarget, LEFT, BOTTOM, aSource, aOpacity, aOperator, aMask, aMaskTransform);
   180   DrawBufferQuadrant(aTarget, RIGHT, BOTTOM, aSource, aOpacity, aOperator,aMask, aMaskTransform);
   181 }
   183 /* static */ bool
   184 RotatedContentBuffer::IsClippingCheap(DrawTarget* aTarget, const nsIntRegion& aRegion)
   185 {
   186   // Assume clipping is cheap if the draw target just has an integer
   187   // translation, and the visible region is simple.
   188   return !aTarget->GetTransform().HasNonIntegerTranslation() &&
   189          aRegion.GetNumRects() <= 1;
   190 }
   192 void
   193 RotatedContentBuffer::DrawTo(ThebesLayer* aLayer,
   194                              DrawTarget* aTarget,
   195                              float aOpacity,
   196                              CompositionOp aOp,
   197                              SourceSurface* aMask,
   198                              const Matrix* aMaskTransform)
   199 {
   200   if (!EnsureBuffer()) {
   201     return;
   202   }
   204   bool clipped = false;
   206   // If the entire buffer is valid, we can just draw the whole thing,
   207   // no need to clip. But we'll still clip if clipping is cheap ---
   208   // that might let us copy a smaller region of the buffer.
   209   // Also clip to the visible region if we're told to.
   210   if (!aLayer->GetValidRegion().Contains(BufferRect()) ||
   211       (ToData(aLayer)->GetClipToVisibleRegion() &&
   212        !aLayer->GetVisibleRegion().Contains(BufferRect())) ||
   213       IsClippingCheap(aTarget, aLayer->GetEffectiveVisibleRegion())) {
   214     // We don't want to draw invalid stuff, so we need to clip. Might as
   215     // well clip to the smallest area possible --- the visible region.
   216     // Bug 599189 if there is a non-integer-translation transform in aTarget,
   217     // we might sample pixels outside GetEffectiveVisibleRegion(), which is wrong
   218     // and may cause gray lines.
   219     gfxUtils::ClipToRegionSnapped(aTarget, aLayer->GetEffectiveVisibleRegion());
   220     clipped = true;
   221   }
   223   DrawBufferWithRotation(aTarget, BUFFER_BLACK, aOpacity, aOp, aMask, aMaskTransform);
   224   if (clipped) {
   225     aTarget->PopClip();
   226   }
   227 }
   229 DrawTarget*
   230 RotatedContentBuffer::BorrowDrawTargetForQuadrantUpdate(const nsIntRect& aBounds,
   231                                                         ContextSource aSource,
   232                                                         DrawIterator* aIter)
   233 {
   234   nsIntRect bounds = aBounds;
   235   if (aIter) {
   236     // If an iterator was provided, then BeginPaint must have been run with
   237     // PAINT_CAN_DRAW_ROTATED, and the draw region might cover multiple quadrants.
   238     // Iterate over each of them, and return an appropriate buffer each time we find
   239     // one that intersects the draw region. The iterator mCount value tracks which
   240     // quadrants we have considered across multiple calls to this function.
   241     aIter->mDrawRegion.SetEmpty();
   242     while (aIter->mCount < 4) {
   243       nsIntRect quadrant = GetQuadrantRectangle((aIter->mCount & 1) ? LEFT : RIGHT,
   244         (aIter->mCount & 2) ? TOP : BOTTOM);
   245       aIter->mDrawRegion.And(aBounds, quadrant);
   246       aIter->mCount++;
   247       if (!aIter->mDrawRegion.IsEmpty()) {
   248         break;
   249       }
   250     }
   251     if (aIter->mDrawRegion.IsEmpty()) {
   252       return nullptr;
   253     }
   254     bounds = aIter->mDrawRegion.GetBounds();
   255   }
   257   if (!EnsureBuffer()) {
   258     return nullptr;
   259   }
   261   MOZ_ASSERT(!mLoanedDrawTarget, "draw target has been borrowed and not returned");
   262   if (aSource == BUFFER_BOTH && HaveBufferOnWhite()) {
   263     if (!EnsureBufferOnWhite()) {
   264       return nullptr;
   265     }
   266     MOZ_ASSERT(mDTBuffer && mDTBufferOnWhite);
   267     mLoanedDrawTarget = Factory::CreateDualDrawTarget(mDTBuffer, mDTBufferOnWhite);
   268   } else if (aSource == BUFFER_WHITE) {
   269     if (!EnsureBufferOnWhite()) {
   270       return nullptr;
   271     }
   272     mLoanedDrawTarget = mDTBufferOnWhite;
   273   } else {
   274     // BUFFER_BLACK, or BUFFER_BOTH with a single buffer.
   275     mLoanedDrawTarget = mDTBuffer;
   276   }
   278   // Figure out which quadrant to draw in
   279   int32_t xBoundary = mBufferRect.XMost() - mBufferRotation.x;
   280   int32_t yBoundary = mBufferRect.YMost() - mBufferRotation.y;
   281   XSide sideX = bounds.XMost() <= xBoundary ? RIGHT : LEFT;
   282   YSide sideY = bounds.YMost() <= yBoundary ? BOTTOM : TOP;
   283   nsIntRect quadrantRect = GetQuadrantRectangle(sideX, sideY);
   284   NS_ASSERTION(quadrantRect.Contains(bounds), "Messed up quadrants");
   286   mLoanedTransform = mLoanedDrawTarget->GetTransform();
   287   mLoanedTransform.Translate(-quadrantRect.x, -quadrantRect.y);
   288   mLoanedDrawTarget->SetTransform(mLoanedTransform);
   289   mLoanedTransform.Translate(quadrantRect.x, quadrantRect.y);
   291   return mLoanedDrawTarget;
   292 }
   294 void
   295 BorrowDrawTarget::ReturnDrawTarget(gfx::DrawTarget*& aReturned)
   296 {
   297   MOZ_ASSERT(aReturned == mLoanedDrawTarget);
   298   mLoanedDrawTarget->SetTransform(mLoanedTransform);
   299   mLoanedDrawTarget = nullptr;
   300   aReturned = nullptr;
   301 }
   303 gfxContentType
   304 RotatedContentBuffer::BufferContentType()
   305 {
   306   if (mBufferProvider || mDTBuffer) {
   307     SurfaceFormat format;
   309     if (mBufferProvider) {
   310       format = mBufferProvider->GetFormat();
   311     } else if (mDTBuffer) {
   312       format = mDTBuffer->GetFormat();
   313     }
   315     return ContentForFormat(format);
   316   }
   317   return gfxContentType::SENTINEL;
   318 }
   320 bool
   321 RotatedContentBuffer::BufferSizeOkFor(const nsIntSize& aSize)
   322 {
   323   return (aSize == mBufferRect.Size() ||
   324           (SizedToVisibleBounds != mBufferSizePolicy &&
   325            aSize < mBufferRect.Size()));
   326 }
   328 bool
   329 RotatedContentBuffer::EnsureBuffer()
   330 {
   331   NS_ASSERTION(!mLoanedDrawTarget, "Loaned draw target must be returned");
   332   if (!mDTBuffer) {
   333     if (mBufferProvider) {
   334       mDTBuffer = mBufferProvider->GetAsDrawTarget();
   335     }
   336   }
   338   NS_WARN_IF_FALSE(mDTBuffer, "no buffer");
   339   return !!mDTBuffer;
   340 }
   342 bool
   343 RotatedContentBuffer::EnsureBufferOnWhite()
   344 {
   345   NS_ASSERTION(!mLoanedDrawTarget, "Loaned draw target must be returned");
   346   if (!mDTBufferOnWhite) {
   347     if (mBufferProviderOnWhite) {
   348       mDTBufferOnWhite =
   349         mBufferProviderOnWhite->GetAsDrawTarget();
   350     }
   351   }
   353   NS_WARN_IF_FALSE(mDTBufferOnWhite, "no buffer");
   354   return mDTBufferOnWhite;
   355 }
   357 bool
   358 RotatedContentBuffer::HaveBuffer() const
   359 {
   360   return mDTBuffer || mBufferProvider;
   361 }
   363 bool
   364 RotatedContentBuffer::HaveBufferOnWhite() const
   365 {
   366   return mDTBufferOnWhite || mBufferProviderOnWhite;
   367 }
   369 static void
   370 WrapRotationAxis(int32_t* aRotationPoint, int32_t aSize)
   371 {
   372   if (*aRotationPoint < 0) {
   373     *aRotationPoint += aSize;
   374   } else if (*aRotationPoint >= aSize) {
   375     *aRotationPoint -= aSize;
   376   }
   377 }
   379 static nsIntRect
   380 ComputeBufferRect(const nsIntRect& aRequestedRect)
   381 {
   382   nsIntRect rect(aRequestedRect);
   383   // Set a minimum width to guarantee a minimum size of buffers we
   384   // allocate (and work around problems on some platforms with smaller
   385   // dimensions).  64 is the magic number needed to work around the
   386   // rendering glitch, and guarantees image rows can be SIMD'd for
   387   // even r5g6b5 surfaces pretty much everywhere.
   388   rect.width = std::max(aRequestedRect.width, 64);
   389 #ifdef MOZ_WIDGET_GONK
   390   // Set a minumum height to guarantee a minumum height of buffers we
   391   // allocate. Some GL implementations fail to render gralloc textures
   392   // with a height 9px-16px. It happens on Adreno 200. Adreno 320 does not
   393   // have this problem. 32 is choosed as alignment of gralloc buffers.
   394   // See Bug 873937.
   395   // Increase the height only when the requested height is more than 0.
   396   // See Bug 895976.
   397   // XXX it might be better to disable it on the gpu that does not have
   398   // the height problem.
   399   if (rect.height > 0) {
   400     rect.height = std::max(aRequestedRect.height, 32);
   401   }
   402 #endif
   403   return rect;
   404 }
   406 void
   407 RotatedContentBuffer::FlushBuffers()
   408 {
   409   if (mDTBuffer) {
   410     mDTBuffer->Flush();
   411   }
   412   if (mDTBufferOnWhite) {
   413     mDTBufferOnWhite->Flush();
   414   }
   415 }
   417 RotatedContentBuffer::PaintState
   418 RotatedContentBuffer::BeginPaint(ThebesLayer* aLayer,
   419                                  uint32_t aFlags)
   420 {
   421   PaintState result;
   422   // We need to disable rotation if we're going to be resampled when
   423   // drawing, because we might sample across the rotation boundary.
   424   bool canHaveRotation = gfxPlatform::BufferRotationEnabled() &&
   425                          !(aFlags & (PAINT_WILL_RESAMPLE | PAINT_NO_ROTATION));
   427   nsIntRegion validRegion = aLayer->GetValidRegion();
   429   bool canUseOpaqueSurface = aLayer->CanUseOpaqueSurface();
   430   ContentType layerContentType =
   431     canUseOpaqueSurface ? gfxContentType::COLOR :
   432                           gfxContentType::COLOR_ALPHA;
   434   SurfaceMode mode;
   435   nsIntRegion neededRegion;
   436   bool canReuseBuffer;
   437   nsIntRect destBufferRect;
   439   while (true) {
   440     mode = aLayer->GetSurfaceMode();
   441     neededRegion = aLayer->GetVisibleRegion();
   442     canReuseBuffer = HaveBuffer() && BufferSizeOkFor(neededRegion.GetBounds().Size());
   443     result.mContentType = layerContentType;
   445     if (canReuseBuffer) {
   446       if (mBufferRect.Contains(neededRegion.GetBounds())) {
   447         // We don't need to adjust mBufferRect.
   448         destBufferRect = mBufferRect;
   449       } else if (neededRegion.GetBounds().Size() <= mBufferRect.Size()) {
   450         // The buffer's big enough but doesn't contain everything that's
   451         // going to be visible. We'll move it.
   452         destBufferRect = nsIntRect(neededRegion.GetBounds().TopLeft(), mBufferRect.Size());
   453       } else {
   454         destBufferRect = neededRegion.GetBounds();
   455       }
   456     } else {
   457       // We won't be reusing the buffer.  Compute a new rect.
   458       destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
   459     }
   461     if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
   462 #if defined(MOZ_GFX_OPTIMIZE_MOBILE) || defined(MOZ_WIDGET_GONK)
   463       mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
   464 #else
   465       if (!aLayer->GetParent() ||
   466           !aLayer->GetParent()->SupportsComponentAlphaChildren() ||
   467           !aLayer->Manager()->IsCompositingCheap() ||
   468           !aLayer->AsShadowableLayer() ||
   469           !aLayer->AsShadowableLayer()->HasShadow() ||
   470           !gfxPrefs::ComponentAlphaEnabled()) {
   471         mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
   472       } else {
   473         result.mContentType = gfxContentType::COLOR;
   474       }
   475 #endif
   476     }
   478     if ((aFlags & PAINT_WILL_RESAMPLE) &&
   479         (!neededRegion.GetBounds().IsEqualInterior(destBufferRect) ||
   480          neededRegion.GetNumRects() > 1)) {
   481       // The area we add to neededRegion might not be painted opaquely
   482       if (mode == SurfaceMode::SURFACE_OPAQUE) {
   483         result.mContentType = gfxContentType::COLOR_ALPHA;
   484         mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
   485       }
   487       // We need to validate the entire buffer, to make sure that only valid
   488       // pixels are sampled
   489       neededRegion = destBufferRect;
   490     }
   492     // If we have an existing buffer, but the content type has changed or we
   493     // have transitioned into/out of component alpha, then we need to recreate it.
   494     if (HaveBuffer() &&
   495         (result.mContentType != BufferContentType() ||
   496         (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != HaveBufferOnWhite())) {
   498       // We're effectively clearing the valid region, so we need to draw
   499       // the entire needed region now.
   500       result.mRegionToInvalidate = aLayer->GetValidRegion();
   501       validRegion.SetEmpty();
   502       Clear();
   503       // Restart decision process with the cleared buffer. We can only go
   504       // around the loop one more iteration, since mDTBuffer is null now.
   505       continue;
   506     }
   508     break;
   509   }
   511   NS_ASSERTION(destBufferRect.Contains(neededRegion.GetBounds()),
   512                "Destination rect doesn't contain what we need to paint");
   514   result.mRegionToDraw.Sub(neededRegion, validRegion);
   516   if (result.mRegionToDraw.IsEmpty())
   517     return result;
   519   // Do not modify result.mRegionToDraw or result.mContentType after this call.
   520   // Do not modify mBufferRect, mBufferRotation, or mDidSelfCopy,
   521   // or call CreateBuffer before this call.
   522   FinalizeFrame(result.mRegionToDraw);
   524   nsIntRect drawBounds = result.mRegionToDraw.GetBounds();
   525   RefPtr<DrawTarget> destDTBuffer;
   526   RefPtr<DrawTarget> destDTBufferOnWhite;
   527   uint32_t bufferFlags = canHaveRotation ? ALLOW_REPEAT : 0;
   528   if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
   529     bufferFlags |= BUFFER_COMPONENT_ALPHA;
   530   }
   531   if (canReuseBuffer) {
   532     if (!EnsureBuffer()) {
   533       return result;
   534     }
   535     nsIntRect keepArea;
   536     if (keepArea.IntersectRect(destBufferRect, mBufferRect)) {
   537       // Set mBufferRotation so that the pixels currently in mDTBuffer
   538       // will still be rendered in the right place when mBufferRect
   539       // changes to destBufferRect.
   540       nsIntPoint newRotation = mBufferRotation +
   541         (destBufferRect.TopLeft() - mBufferRect.TopLeft());
   542       WrapRotationAxis(&newRotation.x, mBufferRect.width);
   543       WrapRotationAxis(&newRotation.y, mBufferRect.height);
   544       NS_ASSERTION(nsIntRect(nsIntPoint(0,0), mBufferRect.Size()).Contains(newRotation),
   545                    "newRotation out of bounds");
   546       int32_t xBoundary = destBufferRect.XMost() - newRotation.x;
   547       int32_t yBoundary = destBufferRect.YMost() - newRotation.y;
   548       bool drawWrapsBuffer = (drawBounds.x < xBoundary && xBoundary < drawBounds.XMost()) ||
   549                              (drawBounds.y < yBoundary && yBoundary < drawBounds.YMost());
   550       if ((drawWrapsBuffer && !(aFlags & PAINT_CAN_DRAW_ROTATED)) ||
   551           (newRotation != nsIntPoint(0,0) && !canHaveRotation)) {
   552         // The stuff we need to redraw will wrap around an edge of the
   553         // buffer (and the caller doesn't know how to support that), so
   554         // move the pixels we can keep into a position that lets us
   555         // redraw in just one quadrant.
   556         if (mBufferRotation == nsIntPoint(0,0)) {
   557           nsIntRect srcRect(nsIntPoint(0, 0), mBufferRect.Size());
   558           nsIntPoint dest = mBufferRect.TopLeft() - destBufferRect.TopLeft();
   559           MOZ_ASSERT(mDTBuffer);
   560           mDTBuffer->CopyRect(IntRect(srcRect.x, srcRect.y, srcRect.width, srcRect.height),
   561                               IntPoint(dest.x, dest.y));
   562           if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
   563             if (!EnsureBufferOnWhite()) {
   564               return result;
   565             }
   566             MOZ_ASSERT(mDTBufferOnWhite);
   567             mDTBufferOnWhite->CopyRect(IntRect(srcRect.x, srcRect.y, srcRect.width, srcRect.height),
   568                                        IntPoint(dest.x, dest.y));
   569           }
   570           result.mDidSelfCopy = true;
   571           mDidSelfCopy = true;
   572           // Don't set destBuffer; we special-case self-copies, and
   573           // just did the necessary work above.
   574           mBufferRect = destBufferRect;
   575         } else {
   576           // With azure and a data surface perform an buffer unrotate
   577           // (SelfCopy).
   578           unsigned char* data;
   579           IntSize size;
   580           int32_t stride;
   581           SurfaceFormat format;
   583           if (mDTBuffer->LockBits(&data, &size, &stride, &format)) {
   584             uint8_t bytesPerPixel = BytesPerPixel(format);
   585             BufferUnrotate(data,
   586                            size.width * bytesPerPixel,
   587                            size.height, stride,
   588                            newRotation.x * bytesPerPixel, newRotation.y);
   589             mDTBuffer->ReleaseBits(data);
   591             if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
   592               if (!EnsureBufferOnWhite()) {
   593                 return result;
   594               }
   595               MOZ_ASSERT(mDTBufferOnWhite);
   596               mDTBufferOnWhite->LockBits(&data, &size, &stride, &format);
   597               uint8_t bytesPerPixel = BytesPerPixel(format);
   598               BufferUnrotate(data,
   599                              size.width * bytesPerPixel,
   600                              size.height, stride,
   601                              newRotation.x * bytesPerPixel, newRotation.y);
   602               mDTBufferOnWhite->ReleaseBits(data);
   603             }
   605             // Buffer unrotate moves all the pixels, note that
   606             // we self copied for SyncBackToFrontBuffer
   607             result.mDidSelfCopy = true;
   608             mDidSelfCopy = true;
   609             mBufferRect = destBufferRect;
   610             mBufferRotation = nsIntPoint(0, 0);
   611           }
   613           if (!result.mDidSelfCopy) {
   614             destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
   615             CreateBuffer(result.mContentType, destBufferRect, bufferFlags,
   616                          &destDTBuffer, &destDTBufferOnWhite);
   617             if (!destDTBuffer) {
   618               return result;
   619             }
   620           }
   621         }
   622       } else {
   623         mBufferRect = destBufferRect;
   624         mBufferRotation = newRotation;
   625       }
   626     } else {
   627       // No pixels are going to be kept. The whole visible region
   628       // will be redrawn, so we don't need to copy anything, so we don't
   629       // set destBuffer.
   630       mBufferRect = destBufferRect;
   631       mBufferRotation = nsIntPoint(0,0);
   632     }
   633   } else {
   634     // The buffer's not big enough, so allocate a new one
   635     CreateBuffer(result.mContentType, destBufferRect, bufferFlags,
   636                  &destDTBuffer, &destDTBufferOnWhite);
   637     if (!destDTBuffer) {
   638       return result;
   639     }
   640   }
   642   NS_ASSERTION(!(aFlags & PAINT_WILL_RESAMPLE) || destBufferRect == neededRegion.GetBounds(),
   643                "If we're resampling, we need to validate the entire buffer");
   645   // If we have no buffered data already, then destBuffer will be a fresh buffer
   646   // and we do not need to clear it below.
   647   bool isClear = !HaveBuffer();
   649   if (destDTBuffer) {
   650     if (!isClear && (mode != SurfaceMode::SURFACE_COMPONENT_ALPHA || HaveBufferOnWhite())) {
   651       // Copy the bits
   652       nsIntPoint offset = -destBufferRect.TopLeft();
   653       Matrix mat;
   654       mat.Translate(offset.x, offset.y);
   655       destDTBuffer->SetTransform(mat);
   656       if (!EnsureBuffer()) {
   657         return result;
   658       }
   659        MOZ_ASSERT(mDTBuffer, "Have we got a Thebes buffer for some reason?");
   660       DrawBufferWithRotation(destDTBuffer, BUFFER_BLACK, 1.0, CompositionOp::OP_SOURCE);
   661       destDTBuffer->SetTransform(Matrix());
   663       if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
   664         NS_ASSERTION(destDTBufferOnWhite, "Must have a white buffer!");
   665         destDTBufferOnWhite->SetTransform(mat);
   666         if (!EnsureBufferOnWhite()) {
   667           return result;
   668         }
   669         MOZ_ASSERT(mDTBufferOnWhite, "Have we got a Thebes buffer for some reason?");
   670         DrawBufferWithRotation(destDTBufferOnWhite, BUFFER_WHITE, 1.0, CompositionOp::OP_SOURCE);
   671         destDTBufferOnWhite->SetTransform(Matrix());
   672       }
   673     }
   675     mDTBuffer = destDTBuffer.forget();
   676     mDTBufferOnWhite = destDTBufferOnWhite.forget();
   677     mBufferRect = destBufferRect;
   678     mBufferRotation = nsIntPoint(0,0);
   679   }
   680   NS_ASSERTION(canHaveRotation || mBufferRotation == nsIntPoint(0,0),
   681                "Rotation disabled, but we have nonzero rotation?");
   683   nsIntRegion invalidate;
   684   invalidate.Sub(aLayer->GetValidRegion(), destBufferRect);
   685   result.mRegionToInvalidate.Or(result.mRegionToInvalidate, invalidate);
   686   result.mClip = DrawRegionClip::DRAW_SNAPPED;
   687   result.mMode = mode;
   689   return result;
   690 }
   692 DrawTarget*
   693 RotatedContentBuffer::BorrowDrawTargetForPainting(const PaintState& aPaintState,
   694                                                   DrawIterator* aIter /* = nullptr */)
   695 {
   696   if (aPaintState.mMode == SurfaceMode::SURFACE_NONE) {
   697     return nullptr;
   698   }
   700   DrawTarget* result = BorrowDrawTargetForQuadrantUpdate(aPaintState.mRegionToDraw.GetBounds(),
   701                                                          BUFFER_BOTH, aIter);
   702   if (!result) {
   703     return nullptr;
   704   }
   705   const nsIntRegion* drawPtr = &aPaintState.mRegionToDraw;
   706   if (aIter) {
   707     // The iterators draw region currently only contains the bounds of the region,
   708     // this makes it the precise region.
   709     aIter->mDrawRegion.And(aIter->mDrawRegion, aPaintState.mRegionToDraw);
   710     drawPtr = &aIter->mDrawRegion;
   711   }
   713   if (aPaintState.mMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
   714     MOZ_ASSERT(mDTBuffer && mDTBufferOnWhite);
   715     nsIntRegionRectIterator iter(*drawPtr);
   716     const nsIntRect *iterRect;
   717     while ((iterRect = iter.Next())) {
   718       mDTBuffer->FillRect(Rect(iterRect->x, iterRect->y, iterRect->width, iterRect->height),
   719                           ColorPattern(Color(0.0, 0.0, 0.0, 1.0)));
   720       mDTBufferOnWhite->FillRect(Rect(iterRect->x, iterRect->y, iterRect->width, iterRect->height),
   721                                  ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
   722     }
   723   } else if (aPaintState.mContentType == gfxContentType::COLOR_ALPHA && HaveBuffer()) {
   724     // HaveBuffer() => we have an existing buffer that we must clear
   725     nsIntRegionRectIterator iter(*drawPtr);
   726     const nsIntRect *iterRect;
   727     while ((iterRect = iter.Next())) {
   728       result->ClearRect(Rect(iterRect->x, iterRect->y, iterRect->width, iterRect->height));
   729     }
   730   }
   732   return result;
   733 }
   735 }
   736 }

mercurial