LCOV - code coverage report
Current view: top level - gfx/layers - RotatedBuffer.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 223 385 57.9 %
Date: 2017-07-14 16:53:18 Functions: 18 21 85.7 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       5             : 
       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 AUTO_PROFILER_LABEL
      13             : #include "Layers.h"                     // for PaintedLayer, 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 "mozilla/gfx/Point.h"          // for IntSize
      28             : #include "gfx2DGlue.h"
      29             : #include "nsLayoutUtils.h"              // for invalidation debugging
      30             : 
      31             : namespace mozilla {
      32             : 
      33             : using namespace gfx;
      34             : 
      35             : namespace layers {
      36             : 
      37             : IntRect
      38         286 : RotatedBuffer::GetQuadrantRectangle(XSide aXSide, YSide aYSide) const
      39             : {
      40             :   // quadrantTranslation is the amount we translate the top-left
      41             :   // of the quadrant by to get coordinates relative to the layer
      42         286 :   IntPoint quadrantTranslation = -mBufferRotation;
      43         286 :   quadrantTranslation.x += aXSide == LEFT ? mBufferRect.width : 0;
      44         286 :   quadrantTranslation.y += aYSide == TOP ? mBufferRect.height : 0;
      45         286 :   return mBufferRect + quadrantTranslation;
      46             : }
      47             : 
      48             : Rect
      49          14 : RotatedBuffer::GetSourceRectangle(XSide aXSide, YSide aYSide) const
      50             : {
      51          14 :   Rect result;
      52          14 :   if (aXSide == LEFT) {
      53           0 :     result.x = 0;
      54           0 :     result.width = mBufferRotation.x;
      55             :   } else {
      56          14 :     result.x = mBufferRotation.x;
      57          14 :     result.width = mBufferRect.width - mBufferRotation.x;
      58             :   }
      59          14 :   if (aYSide == TOP) {
      60           0 :     result.y = 0;
      61           0 :     result.height = mBufferRotation.y;
      62             :   } else {
      63          14 :     result.y = mBufferRotation.y;
      64          14 :     result.height = mBufferRect.height - mBufferRotation.y;
      65             :   }
      66          14 :   return result;
      67             : }
      68             : 
      69             : /**
      70             :  * @param aXSide LEFT means we draw from the left side of the buffer (which
      71             :  * is drawn on the right side of mBufferRect). RIGHT means we draw from
      72             :  * the right side of the buffer (which is drawn on the left side of
      73             :  * mBufferRect).
      74             :  * @param aYSide TOP means we draw from the top side of the buffer (which
      75             :  * is drawn on the bottom side of mBufferRect). BOTTOM means we draw from
      76             :  * the bottom side of the buffer (which is drawn on the top side of
      77             :  * mBufferRect).
      78             :  */
      79             : void
      80          56 : RotatedBuffer::DrawBufferQuadrant(gfx::DrawTarget* aTarget,
      81             :                                   XSide aXSide, YSide aYSide,
      82             :                                   ContextSource aSource,
      83             :                                   float aOpacity,
      84             :                                   gfx::CompositionOp aOperator,
      85             :                                   gfx::SourceSurface* aMask,
      86             :                                   const gfx::Matrix* aMaskTransform) const
      87             : {
      88             :   // The rectangle that we're going to fill. Basically we're going to
      89             :   // render the buffer at mBufferRect + quadrantTranslation to get the
      90             :   // pixels in the right place, but we're only going to paint within
      91             :   // mBufferRect
      92          56 :   IntRect quadrantRect = GetQuadrantRectangle(aXSide, aYSide);
      93          56 :   IntRect fillRect;
      94          56 :   if (!fillRect.IntersectRect(mBufferRect, quadrantRect))
      95          84 :     return;
      96             : 
      97          14 :   gfx::Point quadrantTranslation(quadrantRect.x, quadrantRect.y);
      98             : 
      99          14 :   MOZ_ASSERT(aSource != BUFFER_BOTH);
     100          28 :   RefPtr<SourceSurface> snapshot = GetSourceSurface(aSource);
     101             : 
     102          14 :   if (!snapshot) {
     103           0 :     gfxCriticalError() << "Invalid snapshot in RotatedBuffer::DrawBufferQuadrant";
     104           0 :     return;
     105             :   }
     106             : 
     107             :   // direct2d is much slower when using OP_SOURCE so use OP_OVER and
     108             :   // (maybe) a clear instead. Normally we need to draw in a single operation
     109             :   // (to avoid flickering) but direct2d is ok since it defers rendering.
     110             :   // We should try abstract this logic in a helper when we have other use
     111             :   // cases.
     112          42 :   if ((aTarget->GetBackendType() == BackendType::DIRECT2D ||
     113          14 :        aTarget->GetBackendType() == BackendType::DIRECT2D1_1) &&
     114             :       aOperator == CompositionOp::OP_SOURCE) {
     115           0 :     aOperator = CompositionOp::OP_OVER;
     116           0 :     if (snapshot->GetFormat() == SurfaceFormat::B8G8R8A8) {
     117           0 :       aTarget->ClearRect(IntRectToRect(fillRect));
     118             :     }
     119             :   }
     120             : 
     121             :   // OP_SOURCE is unbounded in Azure, and we really don't want that behaviour here.
     122             :   // We also can't do a ClearRect+FillRect since we need the drawing to happen
     123             :   // as an atomic operation (to prevent flickering).
     124             :   // We also need this clip in the case where we have a mask, since the mask surface
     125             :   // might cover more than fillRect, but we only want to touch the pixels inside
     126             :   // fillRect.
     127          14 :   aTarget->PushClipRect(IntRectToRect(fillRect));
     128             : 
     129          14 :   if (aMask) {
     130           0 :     Matrix oldTransform = aTarget->GetTransform();
     131             : 
     132             :     // Transform from user -> buffer space.
     133             :     Matrix transform =
     134           0 :       Matrix::Translation(quadrantTranslation.x, quadrantTranslation.y);
     135             : 
     136           0 :     Matrix inverseMask = *aMaskTransform;
     137           0 :     inverseMask.Invert();
     138             : 
     139           0 :     transform *= oldTransform;
     140           0 :     transform *= inverseMask;
     141             : 
     142             : #ifdef MOZ_GFX_OPTIMIZE_MOBILE
     143             :     SurfacePattern source(snapshot, ExtendMode::CLAMP, transform, SamplingFilter::POINT);
     144             : #else
     145           0 :     SurfacePattern source(snapshot, ExtendMode::CLAMP, transform);
     146             : #endif
     147             : 
     148           0 :     aTarget->SetTransform(*aMaskTransform);
     149           0 :     aTarget->MaskSurface(source, aMask, Point(0, 0), DrawOptions(aOpacity, aOperator));
     150           0 :     aTarget->SetTransform(oldTransform);
     151             :   } else {
     152             : #ifdef MOZ_GFX_OPTIMIZE_MOBILE
     153             :     DrawSurfaceOptions options(SamplingFilter::POINT);
     154             : #else
     155          14 :     DrawSurfaceOptions options;
     156             : #endif
     157          28 :     aTarget->DrawSurface(snapshot, IntRectToRect(fillRect),
     158          28 :                          GetSourceRectangle(aXSide, aYSide),
     159             :                          options,
     160          42 :                          DrawOptions(aOpacity, aOperator));
     161             :   }
     162             : 
     163          14 :   aTarget->PopClip();
     164             : }
     165             : 
     166             : void
     167          14 : 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          28 :   AUTO_PROFILER_LABEL("RotatedBuffer::DrawBufferWithRotation", GRAPHICS);
     174             : 
     175             :   // See above, in Azure Repeat should always be a safe, even faster choice
     176             :   // though! Particularly on D2D Repeat should be a lot faster, need to look
     177             :   // into that. TODO[Bas]
     178          14 :   DrawBufferQuadrant(aTarget, LEFT, TOP, aSource, aOpacity, aOperator, aMask, aMaskTransform);
     179          14 :   DrawBufferQuadrant(aTarget, RIGHT, TOP, aSource, aOpacity, aOperator, aMask, aMaskTransform);
     180          14 :   DrawBufferQuadrant(aTarget, LEFT, BOTTOM, aSource, aOpacity, aOperator, aMask, aMaskTransform);
     181          14 :   DrawBufferQuadrant(aTarget, RIGHT, BOTTOM, aSource, aOpacity, aOperator,aMask, aMaskTransform);
     182          14 : }
     183             : 
     184             : already_AddRefed<SourceSurface>
     185          13 : SourceRotatedBuffer::GetSourceSurface(ContextSource aSource) const
     186             : {
     187          26 :   RefPtr<SourceSurface> surf;
     188          13 :   if (aSource == BUFFER_BLACK) {
     189          13 :     surf = mSource;
     190             :   } else {
     191           0 :     MOZ_ASSERT(aSource == BUFFER_WHITE);
     192           0 :     surf = mSourceOnWhite;
     193             :   }
     194             : 
     195          13 :   MOZ_ASSERT(surf);
     196          26 :   return surf.forget();
     197             : }
     198             : 
     199             : /* static */ bool
     200          13 : RotatedContentBuffer::IsClippingCheap(DrawTarget* aTarget, const nsIntRegion& aRegion)
     201             : {
     202             :   // Assume clipping is cheap if the draw target just has an integer
     203             :   // translation, and the visible region is simple.
     204          52 :   return !aTarget->GetTransform().HasNonIntegerTranslation() &&
     205          39 :          aRegion.GetNumRects() <= 1;
     206             : }
     207             : 
     208             : void
     209           0 : RotatedContentBuffer::DrawTo(PaintedLayer* aLayer,
     210             :                              DrawTarget* aTarget,
     211             :                              float aOpacity,
     212             :                              CompositionOp aOp,
     213             :                              SourceSurface* aMask,
     214             :                              const Matrix* aMaskTransform)
     215             : {
     216           0 :   if (!EnsureBuffer()) {
     217           0 :     return;
     218             :   }
     219             : 
     220           0 :   bool clipped = false;
     221             : 
     222             :   // If the entire buffer is valid, we can just draw the whole thing,
     223             :   // no need to clip. But we'll still clip if clipping is cheap ---
     224             :   // that might let us copy a smaller region of the buffer.
     225             :   // Also clip to the visible region if we're told to.
     226           0 :   if (!aLayer->GetValidRegion().Contains(BufferRect()) ||
     227           0 :       (ToData(aLayer)->GetClipToVisibleRegion() &&
     228           0 :        !aLayer->GetVisibleRegion().ToUnknownRegion().Contains(BufferRect())) ||
     229           0 :       IsClippingCheap(aTarget, aLayer->GetLocalVisibleRegion().ToUnknownRegion())) {
     230             :     // We don't want to draw invalid stuff, so we need to clip. Might as
     231             :     // well clip to the smallest area possible --- the visible region.
     232             :     // Bug 599189 if there is a non-integer-translation transform in aTarget,
     233             :     // we might sample pixels outside GetLocalVisibleRegion(), which is wrong
     234             :     // and may cause gray lines.
     235           0 :     gfxUtils::ClipToRegion(aTarget, aLayer->GetLocalVisibleRegion().ToUnknownRegion());
     236           0 :     clipped = true;
     237             :   }
     238             : 
     239           0 :   DrawBufferWithRotation(aTarget, BUFFER_BLACK, aOpacity, aOp, aMask, aMaskTransform);
     240           0 :   if (clipped) {
     241           0 :     aTarget->PopClip();
     242             :   }
     243             : }
     244             : 
     245             : DrawTarget*
     246          92 : RotatedContentBuffer::BorrowDrawTargetForQuadrantUpdate(const IntRect& aBounds,
     247             :                                                         ContextSource aSource,
     248             :                                                         DrawIterator* aIter)
     249             : {
     250          92 :   IntRect bounds = aBounds;
     251          92 :   if (aIter) {
     252             :     // If an iterator was provided, then BeginPaint must have been run with
     253             :     // PAINT_CAN_DRAW_ROTATED, and the draw region might cover multiple quadrants.
     254             :     // Iterate over each of them, and return an appropriate buffer each time we find
     255             :     // one that intersects the draw region. The iterator mCount value tracks which
     256             :     // quadrants we have considered across multiple calls to this function.
     257          92 :     aIter->mDrawRegion.SetEmpty();
     258         368 :     while (aIter->mCount < 4) {
     259         184 :       IntRect quadrant = GetQuadrantRectangle((aIter->mCount & 1) ? LEFT : RIGHT,
     260         368 :         (aIter->mCount & 2) ? TOP : BOTTOM);
     261         184 :       aIter->mDrawRegion.And(aBounds, quadrant);
     262         184 :       aIter->mCount++;
     263         184 :       if (!aIter->mDrawRegion.IsEmpty()) {
     264          46 :         break;
     265             :       }
     266             :     }
     267          92 :     if (aIter->mDrawRegion.IsEmpty()) {
     268          46 :       return nullptr;
     269             :     }
     270          46 :     bounds = aIter->mDrawRegion.GetBounds();
     271             :   }
     272             : 
     273          46 :   if (!EnsureBuffer()) {
     274           0 :     return nullptr;
     275             :   }
     276             : 
     277          46 :   MOZ_ASSERT(!mLoanedDrawTarget, "draw target has been borrowed and not returned");
     278          46 :   if (aSource == BUFFER_BOTH && HaveBufferOnWhite()) {
     279           0 :     if (!EnsureBufferOnWhite()) {
     280           0 :       return nullptr;
     281             :     }
     282           0 :     MOZ_ASSERT(mDTBuffer && mDTBuffer->IsValid() && mDTBufferOnWhite && mDTBufferOnWhite->IsValid());
     283           0 :     mLoanedDrawTarget = Factory::CreateDualDrawTarget(mDTBuffer, mDTBufferOnWhite);
     284          46 :   } else if (aSource == BUFFER_WHITE) {
     285           0 :     if (!EnsureBufferOnWhite()) {
     286           0 :       return nullptr;
     287             :     }
     288           0 :     mLoanedDrawTarget = mDTBufferOnWhite;
     289             :   } else {
     290             :     // BUFFER_BLACK, or BUFFER_BOTH with a single buffer.
     291          46 :     mLoanedDrawTarget = mDTBuffer;
     292             :   }
     293             : 
     294             :   // Figure out which quadrant to draw in
     295          46 :   int32_t xBoundary = mBufferRect.XMost() - mBufferRotation.x;
     296          46 :   int32_t yBoundary = mBufferRect.YMost() - mBufferRotation.y;
     297          46 :   XSide sideX = bounds.XMost() <= xBoundary ? RIGHT : LEFT;
     298          46 :   YSide sideY = bounds.YMost() <= yBoundary ? BOTTOM : TOP;
     299          46 :   IntRect quadrantRect = GetQuadrantRectangle(sideX, sideY);
     300          46 :   NS_ASSERTION(quadrantRect.Contains(bounds), "Messed up quadrants");
     301             : 
     302          46 :   mLoanedTransform = mLoanedDrawTarget->GetTransform();
     303         138 :   mLoanedDrawTarget->SetTransform(Matrix(mLoanedTransform).
     304          46 :                                     PreTranslate(-quadrantRect.x,
     305         138 :                                                  -quadrantRect.y));
     306             : 
     307          46 :   return mLoanedDrawTarget;
     308             : }
     309             : 
     310             : void
     311          46 : BorrowDrawTarget::ReturnDrawTarget(gfx::DrawTarget*& aReturned)
     312             : {
     313          46 :   MOZ_ASSERT(mLoanedDrawTarget);
     314          46 :   MOZ_ASSERT(aReturned == mLoanedDrawTarget);
     315          46 :   if (mLoanedDrawTarget) {
     316          46 :     mLoanedDrawTarget->SetTransform(mLoanedTransform);
     317          46 :     mLoanedDrawTarget = nullptr;
     318             :   }
     319          46 :   aReturned = nullptr;
     320          46 : }
     321             : 
     322             : gfxContentType
     323         103 : RotatedContentBuffer::BufferContentType()
     324             : {
     325         103 :   if (mBufferProvider || (mDTBuffer && mDTBuffer->IsValid())) {
     326         103 :     SurfaceFormat format = SurfaceFormat::B8G8R8A8;
     327             : 
     328         103 :     if (mBufferProvider) {
     329         103 :       format = mBufferProvider->GetFormat();
     330           0 :     } else if (mDTBuffer && mDTBuffer->IsValid()) {
     331           0 :       format = mDTBuffer->GetFormat();
     332             :     }
     333             : 
     334         103 :     return ContentForFormat(format);
     335             :   }
     336           0 :   return gfxContentType::SENTINEL;
     337             : }
     338             : 
     339             : bool
     340          77 : RotatedContentBuffer::BufferSizeOkFor(const IntSize& aSize)
     341             : {
     342         308 :   return (aSize == mBufferRect.Size() ||
     343          15 :           (SizedToVisibleBounds != mBufferSizePolicy &&
     344         164 :            aSize < mBufferRect.Size()));
     345             : }
     346             : 
     347             : bool
     348          75 : RotatedContentBuffer::EnsureBuffer()
     349             : {
     350          75 :   NS_ASSERTION(!mLoanedDrawTarget, "Loaned draw target must be returned");
     351          75 :   if (!mDTBuffer || !mDTBuffer->IsValid()) {
     352          29 :     if (mBufferProvider) {
     353          29 :       mDTBuffer = mBufferProvider->BorrowDrawTarget();
     354             :     }
     355             :   }
     356             : 
     357          75 :   NS_WARNING_ASSERTION(mDTBuffer && mDTBuffer->IsValid(), "no buffer");
     358          75 :   return !!mDTBuffer;
     359             : }
     360             : 
     361             : bool
     362           0 : RotatedContentBuffer::EnsureBufferOnWhite()
     363             : {
     364           0 :   NS_ASSERTION(!mLoanedDrawTarget, "Loaned draw target must be returned");
     365           0 :   if (!mDTBufferOnWhite) {
     366           0 :     if (mBufferProviderOnWhite) {
     367             :       mDTBufferOnWhite =
     368           0 :         mBufferProviderOnWhite->BorrowDrawTarget();
     369             :     }
     370             :   }
     371             : 
     372           0 :   NS_WARNING_ASSERTION(mDTBufferOnWhite, "no buffer");
     373           0 :   return !!mDTBufferOnWhite;
     374             : }
     375             : 
     376             : bool
     377         236 : RotatedContentBuffer::HaveBuffer() const
     378             : {
     379         236 :   return mBufferProvider || (mDTBuffer && mDTBuffer->IsValid());
     380             : }
     381             : 
     382             : bool
     383         136 : RotatedContentBuffer::HaveBufferOnWhite() const
     384             : {
     385         136 :   return mBufferProviderOnWhite || (mDTBufferOnWhite && mDTBufferOnWhite->IsValid());
     386             : }
     387             : 
     388             : static void
     389          56 : WrapRotationAxis(int32_t* aRotationPoint, int32_t aSize)
     390             : {
     391          56 :   if (*aRotationPoint < 0) {
     392           0 :     *aRotationPoint += aSize;
     393          56 :   } else if (*aRotationPoint >= aSize) {
     394           0 :     *aRotationPoint -= aSize;
     395             :   }
     396          56 : }
     397             : 
     398             : static IntRect
     399          26 : ComputeBufferRect(const IntRect& aRequestedRect)
     400             : {
     401          26 :   IntRect rect(aRequestedRect);
     402             :   // Set a minimum width to guarantee a minimum size of buffers we
     403             :   // allocate (and work around problems on some platforms with smaller
     404             :   // dimensions). 64 used to be the magic number needed to work around
     405             :   // a rendering glitch on b2g (see bug 788411). Now that we don't support
     406             :   // this device anymore we should be fine with 8 pixels as the minimum.
     407          26 :   rect.width = std::max(aRequestedRect.width, 8);
     408          26 :   return rect;
     409             : }
     410             : 
     411             : void
     412           0 : RotatedContentBuffer::FlushBuffers()
     413             : {
     414           0 :   if (mDTBuffer) {
     415           0 :     mDTBuffer->Flush();
     416             :   }
     417           0 :   if (mDTBufferOnWhite) {
     418           0 :     mDTBufferOnWhite->Flush();
     419             :   }
     420           0 : }
     421             : 
     422             : RotatedContentBuffer::PaintState
     423          77 : RotatedContentBuffer::BeginPaint(PaintedLayer* aLayer,
     424             :                                  uint32_t aFlags)
     425             : {
     426          77 :   PaintState result;
     427             :   // We need to disable rotation if we're going to be resampled when
     428             :   // drawing, because we might sample across the rotation boundary.
     429             :   // Also disable buffer rotation when using webrender.
     430         154 :   bool canHaveRotation = gfxPlatform::BufferRotationEnabled() &&
     431         154 :                          !(aFlags & (PAINT_WILL_RESAMPLE | PAINT_NO_ROTATION)) &&
     432         154 :                          !(aLayer->Manager()->AsWebRenderLayerManager());
     433             : 
     434         154 :   nsIntRegion validRegion = aLayer->GetValidRegion();
     435             : 
     436          77 :   bool canUseOpaqueSurface = aLayer->CanUseOpaqueSurface();
     437             :   ContentType layerContentType =
     438          77 :     canUseOpaqueSurface ? gfxContentType::COLOR :
     439          77 :                           gfxContentType::COLOR_ALPHA;
     440             : 
     441             :   SurfaceMode mode;
     442         154 :   nsIntRegion neededRegion;
     443          77 :   IntRect destBufferRect;
     444             : 
     445          77 :   bool canReuseBuffer = HaveBuffer();
     446             : 
     447             :   while (true) {
     448          77 :     mode = aLayer->GetSurfaceMode();
     449          77 :     neededRegion = aLayer->GetVisibleRegion().ToUnknownRegion();
     450          77 :     canReuseBuffer &= BufferSizeOkFor(neededRegion.GetBounds().Size());
     451          77 :     result.mContentType = layerContentType;
     452             : 
     453          77 :     if (canReuseBuffer) {
     454          51 :       if (mBufferRect.Contains(neededRegion.GetBounds())) {
     455             :         // We don't need to adjust mBufferRect.
     456          51 :         destBufferRect = mBufferRect;
     457           0 :       } else if (neededRegion.GetBounds().Size() <= mBufferRect.Size()) {
     458             :         // The buffer's big enough but doesn't contain everything that's
     459             :         // going to be visible. We'll move it.
     460           0 :         destBufferRect = IntRect(neededRegion.GetBounds().TopLeft(), mBufferRect.Size());
     461             :       } else {
     462           0 :         destBufferRect = neededRegion.GetBounds();
     463             :       }
     464             :     } else {
     465             :       // We won't be reusing the buffer.  Compute a new rect.
     466          26 :       destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
     467             :     }
     468             : 
     469          77 :     if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
     470             : #if defined(MOZ_GFX_OPTIMIZE_MOBILE)
     471             :       mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
     472             : #else
     473           0 :       if (!aLayer->GetParent() ||
     474           0 :           !aLayer->GetParent()->SupportsComponentAlphaChildren() ||
     475           0 :           !aLayer->AsShadowableLayer() ||
     476           0 :           !aLayer->AsShadowableLayer()->HasShadow()) {
     477           0 :         mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
     478             :       } else {
     479           0 :         result.mContentType = gfxContentType::COLOR;
     480             :       }
     481             : #endif
     482             :     }
     483             : 
     484         231 :     if ((aFlags & PAINT_WILL_RESAMPLE) &&
     485          77 :         (!neededRegion.GetBounds().IsEqualInterior(destBufferRect) ||
     486           0 :          neededRegion.GetNumRects() > 1))
     487             :     {
     488             :       // The area we add to neededRegion might not be painted opaquely.
     489           0 :       if (mode == SurfaceMode::SURFACE_OPAQUE) {
     490           0 :         result.mContentType = gfxContentType::COLOR_ALPHA;
     491           0 :         mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
     492             :       }
     493             : 
     494             :       // We need to validate the entire buffer, to make sure that only valid
     495             :       // pixels are sampled.
     496           0 :       neededRegion = destBufferRect;
     497             :     }
     498             : 
     499             :     // If we have an existing buffer, but the content type has changed or we
     500             :     // have transitioned into/out of component alpha, then we need to recreate it.
     501         128 :     if (canReuseBuffer &&
     502         102 :         (result.mContentType != BufferContentType() ||
     503          51 :         (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != HaveBufferOnWhite()))
     504             :     {
     505             :       // Restart the decision process; we won't re-enter since we guard on
     506             :       // being able to re-use the buffer.
     507           0 :       canReuseBuffer = false;
     508           0 :       continue;
     509             :     }
     510             : 
     511          77 :     break;
     512             :   }
     513             : 
     514         129 :   if (HaveBuffer() &&
     515         104 :       (result.mContentType != BufferContentType() ||
     516          52 :       (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != HaveBufferOnWhite()))
     517             :   {
     518             :     // We're effectively clearing the valid region, so we need to draw
     519             :     // the entire needed region now.
     520           0 :     canReuseBuffer = false;
     521           0 :     result.mRegionToInvalidate = aLayer->GetValidRegion();
     522           0 :     validRegion.SetEmpty();
     523           0 :     Clear();
     524             : 
     525             : #if defined(MOZ_DUMP_PAINTING)
     526           0 :     if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
     527           0 :       if (result.mContentType != BufferContentType()) {
     528           0 :         printf_stderr("Invalidating entire rotated buffer (layer %p): content type changed\n", aLayer);
     529           0 :       } else if ((mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != HaveBufferOnWhite()) {
     530           0 :         printf_stderr("Invalidating entire rotated buffer (layer %p): component alpha changed\n", aLayer);
     531             :       }
     532             :     }
     533             : #endif
     534             :   }
     535             : 
     536          77 :   NS_ASSERTION(destBufferRect.Contains(neededRegion.GetBounds()),
     537             :                "Destination rect doesn't contain what we need to paint");
     538             : 
     539          77 :   result.mRegionToDraw.Sub(neededRegion, validRegion);
     540             : 
     541          77 :   if (result.mRegionToDraw.IsEmpty())
     542          44 :     return result;
     543             : 
     544          33 :   if (HaveBuffer()) {
     545          29 :     if (LockBuffers()) {
     546             :       // Do not modify result.mRegionToDraw or result.mContentType after this call.
     547             :       // Do not modify mBufferRect, mBufferRotation, or mDidSelfCopy,
     548             :       // or call CreateBuffer before this call.
     549          29 :       FinalizeFrame(result.mRegionToDraw);
     550             :     } else {
     551             :       // Abandon everything and redraw it all. Ideally we'd reallocate and copy
     552             :       // the old to the new and then call FinalizeFrame on the new buffer so that
     553             :       // we only need to draw the latest bits, but we need a big refactor to support
     554             :       // that ordering.
     555           0 :       result.mRegionToDraw = neededRegion;
     556           0 :       canReuseBuffer = false;
     557           0 :       Clear();
     558             :     }
     559             :   }
     560             : 
     561          33 :   IntRect drawBounds = result.mRegionToDraw.GetBounds();
     562          66 :   RefPtr<DrawTarget> destDTBuffer;
     563          66 :   RefPtr<DrawTarget> destDTBufferOnWhite;
     564          33 :   uint32_t bufferFlags = 0;
     565          33 :   if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
     566           0 :     bufferFlags |= BUFFER_COMPONENT_ALPHA;
     567             :   }
     568          33 :   if (canReuseBuffer) {
     569          28 :     if (!EnsureBuffer()) {
     570           0 :       return result;
     571             :     }
     572          28 :     IntRect keepArea;
     573          28 :     if (keepArea.IntersectRect(destBufferRect, mBufferRect)) {
     574             :       // Set mBufferRotation so that the pixels currently in mDTBuffer
     575             :       // will still be rendered in the right place when mBufferRect
     576             :       // changes to destBufferRect.
     577             :       IntPoint newRotation = mBufferRotation +
     578          28 :         (destBufferRect.TopLeft() - mBufferRect.TopLeft());
     579          28 :       WrapRotationAxis(&newRotation.x, mBufferRect.width);
     580          28 :       WrapRotationAxis(&newRotation.y, mBufferRect.height);
     581          28 :       NS_ASSERTION(gfx::IntRect(gfx::IntPoint(0,0), mBufferRect.Size()).Contains(newRotation),
     582             :                    "newRotation out of bounds");
     583          28 :       int32_t xBoundary = destBufferRect.XMost() - newRotation.x;
     584          28 :       int32_t yBoundary = destBufferRect.YMost() - newRotation.y;
     585          56 :       bool drawWrapsBuffer = (drawBounds.x < xBoundary && xBoundary < drawBounds.XMost()) ||
     586          84 :                              (drawBounds.y < yBoundary && yBoundary < drawBounds.YMost());
     587         112 :       if ((drawWrapsBuffer && !(aFlags & PAINT_CAN_DRAW_ROTATED)) ||
     588         112 :           (newRotation != IntPoint(0,0) && !canHaveRotation)) {
     589             :         // The stuff we need to redraw will wrap around an edge of the
     590             :         // buffer (and the caller doesn't know how to support that), so
     591             :         // move the pixels we can keep into a position that lets us
     592             :         // redraw in just one quadrant.
     593           0 :         if (mBufferRotation == IntPoint(0,0)) {
     594           0 :           IntRect srcRect(IntPoint(0, 0), mBufferRect.Size());
     595           0 :           IntPoint dest = mBufferRect.TopLeft() - destBufferRect.TopLeft();
     596           0 :           MOZ_ASSERT(mDTBuffer && mDTBuffer->IsValid());
     597           0 :           mDTBuffer->CopyRect(srcRect, dest);
     598           0 :           if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
     599           0 :             if (!EnsureBufferOnWhite()) {
     600           0 :               return result;
     601             :             }
     602           0 :             MOZ_ASSERT(mDTBufferOnWhite && mDTBufferOnWhite->IsValid());
     603           0 :             mDTBufferOnWhite->CopyRect(srcRect, dest);
     604             :           }
     605           0 :           result.mDidSelfCopy = true;
     606           0 :           mDidSelfCopy = true;
     607             :           // Don't set destBuffer; we special-case self-copies, and
     608             :           // just did the necessary work above.
     609           0 :           mBufferRect = destBufferRect;
     610             :         } else {
     611             :           // With azure and a data surface perform an buffer unrotate
     612             :           // (SelfCopy).
     613             :           unsigned char* data;
     614           0 :           IntSize size;
     615             :           int32_t stride;
     616             :           SurfaceFormat format;
     617             : 
     618           0 :           if (mDTBuffer->LockBits(&data, &size, &stride, &format)) {
     619           0 :             uint8_t bytesPerPixel = BytesPerPixel(format);
     620           0 :             BufferUnrotate(data,
     621           0 :                            size.width * bytesPerPixel,
     622             :                            size.height, stride,
     623           0 :                            newRotation.x * bytesPerPixel, newRotation.y);
     624           0 :             mDTBuffer->ReleaseBits(data);
     625             : 
     626           0 :             if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
     627           0 :               if (!EnsureBufferOnWhite()) {
     628           0 :                 return result;
     629             :               }
     630           0 :               MOZ_ASSERT(mDTBufferOnWhite && mDTBufferOnWhite->IsValid());
     631           0 :               mDTBufferOnWhite->LockBits(&data, &size, &stride, &format);
     632           0 :               uint8_t bytesPerPixel = BytesPerPixel(format);
     633           0 :               BufferUnrotate(data,
     634           0 :                              size.width * bytesPerPixel,
     635             :                              size.height, stride,
     636           0 :                              newRotation.x * bytesPerPixel, newRotation.y);
     637           0 :               mDTBufferOnWhite->ReleaseBits(data);
     638             :             }
     639             : 
     640             :             // Buffer unrotate moves all the pixels, note that
     641             :             // we self copied for SyncBackToFrontBuffer
     642           0 :             result.mDidSelfCopy = true;
     643           0 :             mDidSelfCopy = true;
     644           0 :             mBufferRect = destBufferRect;
     645           0 :             mBufferRotation = IntPoint(0, 0);
     646             :           }
     647             : 
     648           0 :           if (!result.mDidSelfCopy) {
     649           0 :             destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
     650           0 :             CreateBuffer(result.mContentType, destBufferRect, bufferFlags,
     651           0 :                          &destDTBuffer, &destDTBufferOnWhite);
     652           0 :             if (!destDTBuffer ||
     653           0 :                 (!destDTBufferOnWhite && (bufferFlags & BUFFER_COMPONENT_ALPHA))) {
     654           0 :               if (Factory::ReasonableSurfaceSize(IntSize(destBufferRect.width, destBufferRect.height))) {
     655           0 :                 gfxCriticalNote << "Failed 1 buffer db=" << hexa(destDTBuffer.get()) << " dw=" << hexa(destDTBufferOnWhite.get()) << " for " << destBufferRect.x << ", " << destBufferRect.y << ", " << destBufferRect.width << ", " << destBufferRect.height;
     656             :               }
     657           0 :               return result;
     658             :             }
     659             :           }
     660             :         }
     661             :       } else {
     662          28 :         mBufferRect = destBufferRect;
     663          28 :         mBufferRotation = newRotation;
     664             :       }
     665             :     } else {
     666             :       // No pixels are going to be kept. The whole visible region
     667             :       // will be redrawn, so we don't need to copy anything, so we don't
     668             :       // set destBuffer.
     669           0 :       mBufferRect = destBufferRect;
     670           0 :       mBufferRotation = IntPoint(0,0);
     671             :     }
     672             :   } else {
     673             :     // The buffer's not big enough, so allocate a new one
     674           5 :     CreateBuffer(result.mContentType, destBufferRect, bufferFlags,
     675          10 :                  &destDTBuffer, &destDTBufferOnWhite);
     676          10 :     if (!destDTBuffer ||
     677          10 :         (!destDTBufferOnWhite && (bufferFlags & BUFFER_COMPONENT_ALPHA))) {
     678           0 :       if (Factory::ReasonableSurfaceSize(IntSize(destBufferRect.width, destBufferRect.height))) {
     679           0 :         gfxCriticalNote << "Failed 2 buffer db=" << hexa(destDTBuffer.get()) << " dw=" << hexa(destDTBufferOnWhite.get()) << " for " << destBufferRect.x << ", " << destBufferRect.y << ", " << destBufferRect.width << ", " << destBufferRect.height;
     680             :       }
     681           0 :       return result;
     682             :     }
     683             :   }
     684             : 
     685          33 :   NS_ASSERTION(!(aFlags & PAINT_WILL_RESAMPLE) || destBufferRect == neededRegion.GetBounds(),
     686             :                "If we're resampling, we need to validate the entire buffer");
     687             : 
     688             :   // If we have no buffered data already, then destBuffer will be a fresh buffer
     689             :   // and we do not need to clear it below.
     690          33 :   bool isClear = !HaveBuffer();
     691             : 
     692          33 :   if (destDTBuffer) {
     693           5 :     if (!isClear && (mode != SurfaceMode::SURFACE_COMPONENT_ALPHA || HaveBufferOnWhite())) {
     694             :       // Copy the bits
     695           1 :       IntPoint offset = -destBufferRect.TopLeft();
     696           1 :       Matrix mat = Matrix::Translation(offset.x, offset.y);
     697           1 :       destDTBuffer->SetTransform(mat);
     698           1 :       if (!EnsureBuffer()) {
     699           0 :         return result;
     700             :       }
     701           1 :       MOZ_ASSERT(mDTBuffer && mDTBuffer->IsValid(), "Have we got a Thebes buffer for some reason?");
     702           1 :       DrawBufferWithRotation(destDTBuffer, BUFFER_BLACK, 1.0, CompositionOp::OP_SOURCE);
     703           1 :       destDTBuffer->SetTransform(Matrix());
     704             : 
     705           1 :       if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
     706           0 :         if (!destDTBufferOnWhite || !EnsureBufferOnWhite()) {
     707           0 :           return result;
     708             :         }
     709           0 :         MOZ_ASSERT(mDTBufferOnWhite && mDTBufferOnWhite->IsValid(), "Have we got a Thebes buffer for some reason?");
     710           0 :         destDTBufferOnWhite->SetTransform(mat);
     711           0 :         DrawBufferWithRotation(destDTBufferOnWhite, BUFFER_WHITE, 1.0, CompositionOp::OP_SOURCE);
     712           0 :         destDTBufferOnWhite->SetTransform(Matrix());
     713             :       }
     714             :     }
     715             : 
     716           5 :     mDTBuffer = destDTBuffer.forget();
     717           5 :     mDTBufferOnWhite = destDTBufferOnWhite.forget();
     718           5 :     mBufferRect = destBufferRect;
     719           5 :     mBufferRotation = IntPoint(0,0);
     720             :   }
     721          33 :   NS_ASSERTION(canHaveRotation || mBufferRotation == IntPoint(0,0),
     722             :                "Rotation disabled, but we have nonzero rotation?");
     723             : 
     724          66 :   nsIntRegion invalidate;
     725          33 :   invalidate.Sub(aLayer->GetValidRegion(), destBufferRect);
     726          33 :   result.mRegionToInvalidate.Or(result.mRegionToInvalidate, invalidate);
     727          33 :   result.mClip = DrawRegionClip::DRAW;
     728          33 :   result.mMode = mode;
     729             : 
     730          33 :   return result;
     731             : }
     732             : 
     733             : DrawTarget*
     734         110 : RotatedContentBuffer::BorrowDrawTargetForPainting(PaintState& aPaintState,
     735             :                                                   DrawIterator* aIter /* = nullptr */)
     736             : {
     737         110 :   if (aPaintState.mMode == SurfaceMode::SURFACE_NONE) {
     738          44 :     return nullptr;
     739             :   }
     740             : 
     741         132 :   DrawTarget* result = BorrowDrawTargetForQuadrantUpdate(aPaintState.mRegionToDraw.GetBounds(),
     742          66 :                                                          BUFFER_BOTH, aIter);
     743          66 :   if (!result) {
     744          33 :     return nullptr;
     745             :   }
     746             : 
     747          33 :   nsIntRegion* drawPtr = &aPaintState.mRegionToDraw;
     748          33 :   if (aIter) {
     749             :     // The iterators draw region currently only contains the bounds of the region,
     750             :     // this makes it the precise region.
     751          33 :     aIter->mDrawRegion.And(aIter->mDrawRegion, aPaintState.mRegionToDraw);
     752          33 :     drawPtr = &aIter->mDrawRegion;
     753             :   }
     754          66 :   if (result->GetBackendType() == BackendType::DIRECT2D ||
     755          33 :       result->GetBackendType() == BackendType::DIRECT2D1_1) {
     756             :     // Simplify the draw region to avoid hitting expensive drawing paths
     757             :     // for complex regions.
     758           0 :     drawPtr->SimplifyOutwardByArea(100 * 100);
     759             :   }
     760             : 
     761          33 :   if (aPaintState.mMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
     762           0 :     if (!mDTBuffer || !mDTBuffer->IsValid() ||
     763           0 :         !mDTBufferOnWhite || !mDTBufferOnWhite->IsValid()) {
     764             :       // This can happen in release builds if allocating one of the two buffers
     765             :       // failed. This in turn can happen if unreasonably large textures are
     766             :       // requested.
     767           0 :       return nullptr;
     768             :     }
     769           0 :     for (auto iter = drawPtr->RectIter(); !iter.Done(); iter.Next()) {
     770           0 :       const IntRect& rect = iter.Get();
     771           0 :       mDTBuffer->FillRect(Rect(rect.x, rect.y, rect.width, rect.height),
     772           0 :                           ColorPattern(Color(0.0, 0.0, 0.0, 1.0)));
     773           0 :       mDTBufferOnWhite->FillRect(Rect(rect.x, rect.y, rect.width, rect.height),
     774           0 :                                  ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
     775             :     }
     776          33 :   } else if (aPaintState.mContentType == gfxContentType::COLOR_ALPHA && HaveBuffer()) {
     777             :     // HaveBuffer() => we have an existing buffer that we must clear
     778          32 :     for (auto iter = drawPtr->RectIter(); !iter.Done(); iter.Next()) {
     779          16 :       const IntRect& rect = iter.Get();
     780          16 :       result->ClearRect(Rect(rect.x, rect.y, rect.width, rect.height));
     781             :     }
     782             :   }
     783             : 
     784          33 :   return result;
     785             : }
     786             : 
     787             : already_AddRefed<SourceSurface>
     788           1 : RotatedContentBuffer::GetSourceSurface(ContextSource aSource) const
     789             : {
     790           1 :   if (!mDTBuffer || !mDTBuffer->IsValid()) {
     791           0 :     gfxCriticalNote << "Invalid buffer in RotatedContentBuffer::GetSourceSurface " << gfx::hexa(mDTBuffer);
     792           0 :     return nullptr;
     793             :   }
     794             : 
     795           1 :   if (aSource == BUFFER_BLACK) {
     796           1 :     return mDTBuffer->Snapshot();
     797             :   } else {
     798           0 :     if (!mDTBufferOnWhite || !mDTBufferOnWhite->IsValid()) {
     799           0 :     gfxCriticalNote << "Invalid buffer on white in RotatedContentBuffer::GetSourceSurface " << gfx::hexa(mDTBufferOnWhite);
     800           0 :       return nullptr;
     801             :     }
     802           0 :     MOZ_ASSERT(aSource == BUFFER_WHITE);
     803           0 :     return mDTBufferOnWhite->Snapshot();
     804             :   }
     805             : }
     806             : 
     807             : } // namespace layers
     808             : } // namespace mozilla
     809             : 

Generated by: LCOV version 1.13