gfx/layers/RotatedBuffer.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/layers/RotatedBuffer.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,737 @@
     1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     1.5 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "RotatedBuffer.h"
    1.10 +#include <sys/types.h>                  // for int32_t
    1.11 +#include <algorithm>                    // for max
    1.12 +#include "BasicImplData.h"              // for BasicImplData
    1.13 +#include "BasicLayersImpl.h"            // for ToData
    1.14 +#include "BufferUnrotate.h"             // for BufferUnrotate
    1.15 +#include "GeckoProfiler.h"              // for PROFILER_LABEL
    1.16 +#include "Layers.h"                     // for ThebesLayer, Layer, etc
    1.17 +#include "gfxPlatform.h"                // for gfxPlatform
    1.18 +#include "gfxPrefs.h"                   // for gfxPrefs
    1.19 +#include "gfxUtils.h"                   // for gfxUtils
    1.20 +#include "mozilla/ArrayUtils.h"         // for ArrayLength
    1.21 +#include "mozilla/gfx/BasePoint.h"      // for BasePoint
    1.22 +#include "mozilla/gfx/BaseRect.h"       // for BaseRect
    1.23 +#include "mozilla/gfx/BaseSize.h"       // for BaseSize
    1.24 +#include "mozilla/gfx/Matrix.h"         // for Matrix
    1.25 +#include "mozilla/gfx/Point.h"          // for Point, IntPoint
    1.26 +#include "mozilla/gfx/Rect.h"           // for Rect, IntRect
    1.27 +#include "mozilla/gfx/Types.h"          // for ExtendMode::ExtendMode::CLAMP, etc
    1.28 +#include "mozilla/layers/ShadowLayers.h"  // for ShadowableLayer
    1.29 +#include "mozilla/layers/TextureClient.h"  // for TextureClient
    1.30 +#include "nsSize.h"                     // for nsIntSize
    1.31 +#include "gfx2DGlue.h"
    1.32 +
    1.33 +namespace mozilla {
    1.34 +
    1.35 +using namespace gfx;
    1.36 +
    1.37 +namespace layers {
    1.38 +
    1.39 +nsIntRect
    1.40 +RotatedBuffer::GetQuadrantRectangle(XSide aXSide, YSide aYSide) const
    1.41 +{
    1.42 +  // quadrantTranslation is the amount we translate the top-left
    1.43 +  // of the quadrant by to get coordinates relative to the layer
    1.44 +  nsIntPoint quadrantTranslation = -mBufferRotation;
    1.45 +  quadrantTranslation.x += aXSide == LEFT ? mBufferRect.width : 0;
    1.46 +  quadrantTranslation.y += aYSide == TOP ? mBufferRect.height : 0;
    1.47 +  return mBufferRect + quadrantTranslation;
    1.48 +}
    1.49 +
    1.50 +Rect
    1.51 +RotatedBuffer::GetSourceRectangle(XSide aXSide, YSide aYSide) const
    1.52 +{
    1.53 +  Rect result;
    1.54 +  if (aXSide == LEFT) {
    1.55 +    result.x = 0;
    1.56 +    result.width = mBufferRotation.x;
    1.57 +  } else {
    1.58 +    result.x = mBufferRotation.x;
    1.59 +    result.width = mBufferRect.width - mBufferRotation.x;
    1.60 +  }
    1.61 +  if (aYSide == TOP) {
    1.62 +    result.y = 0;
    1.63 +    result.height = mBufferRotation.y;
    1.64 +  } else {
    1.65 +    result.y = mBufferRotation.y;
    1.66 +    result.height = mBufferRect.height - mBufferRotation.y;
    1.67 +  }
    1.68 +  return result;
    1.69 +}
    1.70 +
    1.71 +/**
    1.72 + * @param aXSide LEFT means we draw from the left side of the buffer (which
    1.73 + * is drawn on the right side of mBufferRect). RIGHT means we draw from
    1.74 + * the right side of the buffer (which is drawn on the left side of
    1.75 + * mBufferRect).
    1.76 + * @param aYSide TOP means we draw from the top side of the buffer (which
    1.77 + * is drawn on the bottom side of mBufferRect). BOTTOM means we draw from
    1.78 + * the bottom side of the buffer (which is drawn on the top side of
    1.79 + * mBufferRect).
    1.80 + */
    1.81 +void
    1.82 +RotatedBuffer::DrawBufferQuadrant(gfx::DrawTarget* aTarget,
    1.83 +                                  XSide aXSide, YSide aYSide,
    1.84 +                                  ContextSource aSource,
    1.85 +                                  float aOpacity,
    1.86 +                                  gfx::CompositionOp aOperator,
    1.87 +                                  gfx::SourceSurface* aMask,
    1.88 +                                  const gfx::Matrix* aMaskTransform) const
    1.89 +{
    1.90 +  // The rectangle that we're going to fill. Basically we're going to
    1.91 +  // render the buffer at mBufferRect + quadrantTranslation to get the
    1.92 +  // pixels in the right place, but we're only going to paint within
    1.93 +  // mBufferRect
    1.94 +  nsIntRect quadrantRect = GetQuadrantRectangle(aXSide, aYSide);
    1.95 +  nsIntRect fillRect;
    1.96 +  if (!fillRect.IntersectRect(mBufferRect, quadrantRect))
    1.97 +    return;
    1.98 +
    1.99 +  gfx::Point quadrantTranslation(quadrantRect.x, quadrantRect.y);
   1.100 +
   1.101 +  MOZ_ASSERT(aOperator == CompositionOp::OP_OVER || aOperator == CompositionOp::OP_SOURCE);
   1.102 +  // direct2d is much slower when using OP_SOURCE so use OP_OVER and
   1.103 +  // (maybe) a clear instead. Normally we need to draw in a single operation
   1.104 +  // (to avoid flickering) but direct2d is ok since it defers rendering.
   1.105 +  // We should try abstract this logic in a helper when we have other use
   1.106 +  // cases.
   1.107 +  if (aTarget->GetType() == BackendType::DIRECT2D && aOperator == CompositionOp::OP_SOURCE) {
   1.108 +    aOperator = CompositionOp::OP_OVER;
   1.109 +    if (mDTBuffer->GetFormat() == SurfaceFormat::B8G8R8A8) {
   1.110 +      aTarget->ClearRect(ToRect(fillRect));
   1.111 +    }
   1.112 +  }
   1.113 +
   1.114 +  RefPtr<gfx::SourceSurface> snapshot;
   1.115 +  if (aSource == BUFFER_BLACK) {
   1.116 +    snapshot = mDTBuffer->Snapshot();
   1.117 +  } else {
   1.118 +    MOZ_ASSERT(aSource == BUFFER_WHITE);
   1.119 +    snapshot = mDTBufferOnWhite->Snapshot();
   1.120 +  }
   1.121 +
   1.122 +  if (aOperator == CompositionOp::OP_SOURCE) {
   1.123 +    // OP_SOURCE is unbounded in Azure, and we really don't want that behaviour here.
   1.124 +    // We also can't do a ClearRect+FillRect since we need the drawing to happen
   1.125 +    // as an atomic operation (to prevent flickering).
   1.126 +    aTarget->PushClipRect(gfx::Rect(fillRect.x, fillRect.y,
   1.127 +                                    fillRect.width, fillRect.height));
   1.128 +  }
   1.129 +
   1.130 +  if (aMask) {
   1.131 +    Matrix oldTransform = aTarget->GetTransform();
   1.132 +
   1.133 +    // Transform from user -> buffer space.
   1.134 +    Matrix transform;
   1.135 +    transform.Translate(quadrantTranslation.x, quadrantTranslation.y);
   1.136 +
   1.137 +    Matrix inverseMask = *aMaskTransform;
   1.138 +    inverseMask.Invert();
   1.139 +
   1.140 +    transform *= oldTransform;
   1.141 +    transform *= inverseMask;
   1.142 +
   1.143 +#ifdef MOZ_GFX_OPTIMIZE_MOBILE
   1.144 +    SurfacePattern source(snapshot, ExtendMode::CLAMP, transform, Filter::POINT);
   1.145 +#else
   1.146 +    SurfacePattern source(snapshot, ExtendMode::CLAMP, transform);
   1.147 +#endif
   1.148 +
   1.149 +    aTarget->SetTransform(*aMaskTransform);
   1.150 +    aTarget->MaskSurface(source, aMask, Point(0, 0), DrawOptions(aOpacity, aOperator));
   1.151 +    aTarget->SetTransform(oldTransform);
   1.152 +  } else {
   1.153 +#ifdef MOZ_GFX_OPTIMIZE_MOBILE
   1.154 +    DrawSurfaceOptions options(Filter::POINT);
   1.155 +#else
   1.156 +    DrawSurfaceOptions options;
   1.157 +#endif
   1.158 +    aTarget->DrawSurface(snapshot, ToRect(fillRect),
   1.159 +                         GetSourceRectangle(aXSide, aYSide),
   1.160 +                         options,
   1.161 +                         DrawOptions(aOpacity, aOperator));
   1.162 +  }
   1.163 +
   1.164 +  if (aOperator == CompositionOp::OP_SOURCE) {
   1.165 +    aTarget->PopClip();
   1.166 +  }
   1.167 +}
   1.168 +
   1.169 +void
   1.170 +RotatedBuffer::DrawBufferWithRotation(gfx::DrawTarget *aTarget, ContextSource aSource,
   1.171 +                                      float aOpacity,
   1.172 +                                      gfx::CompositionOp aOperator,
   1.173 +                                      gfx::SourceSurface* aMask,
   1.174 +                                      const gfx::Matrix* aMaskTransform) const
   1.175 +{
   1.176 +  PROFILER_LABEL("RotatedBuffer", "DrawBufferWithRotation");
   1.177 +  // See above, in Azure Repeat should always be a safe, even faster choice
   1.178 +  // though! Particularly on D2D Repeat should be a lot faster, need to look
   1.179 +  // into that. TODO[Bas]
   1.180 +  DrawBufferQuadrant(aTarget, LEFT, TOP, aSource, aOpacity, aOperator, aMask, aMaskTransform);
   1.181 +  DrawBufferQuadrant(aTarget, RIGHT, TOP, aSource, aOpacity, aOperator, aMask, aMaskTransform);
   1.182 +  DrawBufferQuadrant(aTarget, LEFT, BOTTOM, aSource, aOpacity, aOperator, aMask, aMaskTransform);
   1.183 +  DrawBufferQuadrant(aTarget, RIGHT, BOTTOM, aSource, aOpacity, aOperator,aMask, aMaskTransform);
   1.184 +}
   1.185 +
   1.186 +/* static */ bool
   1.187 +RotatedContentBuffer::IsClippingCheap(DrawTarget* aTarget, const nsIntRegion& aRegion)
   1.188 +{
   1.189 +  // Assume clipping is cheap if the draw target just has an integer
   1.190 +  // translation, and the visible region is simple.
   1.191 +  return !aTarget->GetTransform().HasNonIntegerTranslation() &&
   1.192 +         aRegion.GetNumRects() <= 1;
   1.193 +}
   1.194 +
   1.195 +void
   1.196 +RotatedContentBuffer::DrawTo(ThebesLayer* aLayer,
   1.197 +                             DrawTarget* aTarget,
   1.198 +                             float aOpacity,
   1.199 +                             CompositionOp aOp,
   1.200 +                             SourceSurface* aMask,
   1.201 +                             const Matrix* aMaskTransform)
   1.202 +{
   1.203 +  if (!EnsureBuffer()) {
   1.204 +    return;
   1.205 +  }
   1.206 +
   1.207 +  bool clipped = false;
   1.208 +
   1.209 +  // If the entire buffer is valid, we can just draw the whole thing,
   1.210 +  // no need to clip. But we'll still clip if clipping is cheap ---
   1.211 +  // that might let us copy a smaller region of the buffer.
   1.212 +  // Also clip to the visible region if we're told to.
   1.213 +  if (!aLayer->GetValidRegion().Contains(BufferRect()) ||
   1.214 +      (ToData(aLayer)->GetClipToVisibleRegion() &&
   1.215 +       !aLayer->GetVisibleRegion().Contains(BufferRect())) ||
   1.216 +      IsClippingCheap(aTarget, aLayer->GetEffectiveVisibleRegion())) {
   1.217 +    // We don't want to draw invalid stuff, so we need to clip. Might as
   1.218 +    // well clip to the smallest area possible --- the visible region.
   1.219 +    // Bug 599189 if there is a non-integer-translation transform in aTarget,
   1.220 +    // we might sample pixels outside GetEffectiveVisibleRegion(), which is wrong
   1.221 +    // and may cause gray lines.
   1.222 +    gfxUtils::ClipToRegionSnapped(aTarget, aLayer->GetEffectiveVisibleRegion());
   1.223 +    clipped = true;
   1.224 +  }
   1.225 +
   1.226 +  DrawBufferWithRotation(aTarget, BUFFER_BLACK, aOpacity, aOp, aMask, aMaskTransform);
   1.227 +  if (clipped) {
   1.228 +    aTarget->PopClip();
   1.229 +  }
   1.230 +}
   1.231 +
   1.232 +DrawTarget*
   1.233 +RotatedContentBuffer::BorrowDrawTargetForQuadrantUpdate(const nsIntRect& aBounds,
   1.234 +                                                        ContextSource aSource,
   1.235 +                                                        DrawIterator* aIter)
   1.236 +{
   1.237 +  nsIntRect bounds = aBounds;
   1.238 +  if (aIter) {
   1.239 +    // If an iterator was provided, then BeginPaint must have been run with
   1.240 +    // PAINT_CAN_DRAW_ROTATED, and the draw region might cover multiple quadrants.
   1.241 +    // Iterate over each of them, and return an appropriate buffer each time we find
   1.242 +    // one that intersects the draw region. The iterator mCount value tracks which
   1.243 +    // quadrants we have considered across multiple calls to this function.
   1.244 +    aIter->mDrawRegion.SetEmpty();
   1.245 +    while (aIter->mCount < 4) {
   1.246 +      nsIntRect quadrant = GetQuadrantRectangle((aIter->mCount & 1) ? LEFT : RIGHT,
   1.247 +        (aIter->mCount & 2) ? TOP : BOTTOM);
   1.248 +      aIter->mDrawRegion.And(aBounds, quadrant);
   1.249 +      aIter->mCount++;
   1.250 +      if (!aIter->mDrawRegion.IsEmpty()) {
   1.251 +        break;
   1.252 +      }
   1.253 +    }
   1.254 +    if (aIter->mDrawRegion.IsEmpty()) {
   1.255 +      return nullptr;
   1.256 +    }
   1.257 +    bounds = aIter->mDrawRegion.GetBounds();
   1.258 +  }
   1.259 +
   1.260 +  if (!EnsureBuffer()) {
   1.261 +    return nullptr;
   1.262 +  }
   1.263 +
   1.264 +  MOZ_ASSERT(!mLoanedDrawTarget, "draw target has been borrowed and not returned");
   1.265 +  if (aSource == BUFFER_BOTH && HaveBufferOnWhite()) {
   1.266 +    if (!EnsureBufferOnWhite()) {
   1.267 +      return nullptr;
   1.268 +    }
   1.269 +    MOZ_ASSERT(mDTBuffer && mDTBufferOnWhite);
   1.270 +    mLoanedDrawTarget = Factory::CreateDualDrawTarget(mDTBuffer, mDTBufferOnWhite);
   1.271 +  } else if (aSource == BUFFER_WHITE) {
   1.272 +    if (!EnsureBufferOnWhite()) {
   1.273 +      return nullptr;
   1.274 +    }
   1.275 +    mLoanedDrawTarget = mDTBufferOnWhite;
   1.276 +  } else {
   1.277 +    // BUFFER_BLACK, or BUFFER_BOTH with a single buffer.
   1.278 +    mLoanedDrawTarget = mDTBuffer;
   1.279 +  }
   1.280 +
   1.281 +  // Figure out which quadrant to draw in
   1.282 +  int32_t xBoundary = mBufferRect.XMost() - mBufferRotation.x;
   1.283 +  int32_t yBoundary = mBufferRect.YMost() - mBufferRotation.y;
   1.284 +  XSide sideX = bounds.XMost() <= xBoundary ? RIGHT : LEFT;
   1.285 +  YSide sideY = bounds.YMost() <= yBoundary ? BOTTOM : TOP;
   1.286 +  nsIntRect quadrantRect = GetQuadrantRectangle(sideX, sideY);
   1.287 +  NS_ASSERTION(quadrantRect.Contains(bounds), "Messed up quadrants");
   1.288 +
   1.289 +  mLoanedTransform = mLoanedDrawTarget->GetTransform();
   1.290 +  mLoanedTransform.Translate(-quadrantRect.x, -quadrantRect.y);
   1.291 +  mLoanedDrawTarget->SetTransform(mLoanedTransform);
   1.292 +  mLoanedTransform.Translate(quadrantRect.x, quadrantRect.y);
   1.293 +
   1.294 +  return mLoanedDrawTarget;
   1.295 +}
   1.296 +
   1.297 +void
   1.298 +BorrowDrawTarget::ReturnDrawTarget(gfx::DrawTarget*& aReturned)
   1.299 +{
   1.300 +  MOZ_ASSERT(aReturned == mLoanedDrawTarget);
   1.301 +  mLoanedDrawTarget->SetTransform(mLoanedTransform);
   1.302 +  mLoanedDrawTarget = nullptr;
   1.303 +  aReturned = nullptr;
   1.304 +}
   1.305 +
   1.306 +gfxContentType
   1.307 +RotatedContentBuffer::BufferContentType()
   1.308 +{
   1.309 +  if (mBufferProvider || mDTBuffer) {
   1.310 +    SurfaceFormat format;
   1.311 +
   1.312 +    if (mBufferProvider) {
   1.313 +      format = mBufferProvider->GetFormat();
   1.314 +    } else if (mDTBuffer) {
   1.315 +      format = mDTBuffer->GetFormat();
   1.316 +    }
   1.317 +
   1.318 +    return ContentForFormat(format);
   1.319 +  }
   1.320 +  return gfxContentType::SENTINEL;
   1.321 +}
   1.322 +
   1.323 +bool
   1.324 +RotatedContentBuffer::BufferSizeOkFor(const nsIntSize& aSize)
   1.325 +{
   1.326 +  return (aSize == mBufferRect.Size() ||
   1.327 +          (SizedToVisibleBounds != mBufferSizePolicy &&
   1.328 +           aSize < mBufferRect.Size()));
   1.329 +}
   1.330 +
   1.331 +bool
   1.332 +RotatedContentBuffer::EnsureBuffer()
   1.333 +{
   1.334 +  NS_ASSERTION(!mLoanedDrawTarget, "Loaned draw target must be returned");
   1.335 +  if (!mDTBuffer) {
   1.336 +    if (mBufferProvider) {
   1.337 +      mDTBuffer = mBufferProvider->GetAsDrawTarget();
   1.338 +    }
   1.339 +  }
   1.340 +
   1.341 +  NS_WARN_IF_FALSE(mDTBuffer, "no buffer");
   1.342 +  return !!mDTBuffer;
   1.343 +}
   1.344 +
   1.345 +bool
   1.346 +RotatedContentBuffer::EnsureBufferOnWhite()
   1.347 +{
   1.348 +  NS_ASSERTION(!mLoanedDrawTarget, "Loaned draw target must be returned");
   1.349 +  if (!mDTBufferOnWhite) {
   1.350 +    if (mBufferProviderOnWhite) {
   1.351 +      mDTBufferOnWhite =
   1.352 +        mBufferProviderOnWhite->GetAsDrawTarget();
   1.353 +    }
   1.354 +  }
   1.355 +
   1.356 +  NS_WARN_IF_FALSE(mDTBufferOnWhite, "no buffer");
   1.357 +  return mDTBufferOnWhite;
   1.358 +}
   1.359 +
   1.360 +bool
   1.361 +RotatedContentBuffer::HaveBuffer() const
   1.362 +{
   1.363 +  return mDTBuffer || mBufferProvider;
   1.364 +}
   1.365 +
   1.366 +bool
   1.367 +RotatedContentBuffer::HaveBufferOnWhite() const
   1.368 +{
   1.369 +  return mDTBufferOnWhite || mBufferProviderOnWhite;
   1.370 +}
   1.371 +
   1.372 +static void
   1.373 +WrapRotationAxis(int32_t* aRotationPoint, int32_t aSize)
   1.374 +{
   1.375 +  if (*aRotationPoint < 0) {
   1.376 +    *aRotationPoint += aSize;
   1.377 +  } else if (*aRotationPoint >= aSize) {
   1.378 +    *aRotationPoint -= aSize;
   1.379 +  }
   1.380 +}
   1.381 +
   1.382 +static nsIntRect
   1.383 +ComputeBufferRect(const nsIntRect& aRequestedRect)
   1.384 +{
   1.385 +  nsIntRect rect(aRequestedRect);
   1.386 +  // Set a minimum width to guarantee a minimum size of buffers we
   1.387 +  // allocate (and work around problems on some platforms with smaller
   1.388 +  // dimensions).  64 is the magic number needed to work around the
   1.389 +  // rendering glitch, and guarantees image rows can be SIMD'd for
   1.390 +  // even r5g6b5 surfaces pretty much everywhere.
   1.391 +  rect.width = std::max(aRequestedRect.width, 64);
   1.392 +#ifdef MOZ_WIDGET_GONK
   1.393 +  // Set a minumum height to guarantee a minumum height of buffers we
   1.394 +  // allocate. Some GL implementations fail to render gralloc textures
   1.395 +  // with a height 9px-16px. It happens on Adreno 200. Adreno 320 does not
   1.396 +  // have this problem. 32 is choosed as alignment of gralloc buffers.
   1.397 +  // See Bug 873937.
   1.398 +  // Increase the height only when the requested height is more than 0.
   1.399 +  // See Bug 895976.
   1.400 +  // XXX it might be better to disable it on the gpu that does not have
   1.401 +  // the height problem.
   1.402 +  if (rect.height > 0) {
   1.403 +    rect.height = std::max(aRequestedRect.height, 32);
   1.404 +  }
   1.405 +#endif
   1.406 +  return rect;
   1.407 +}
   1.408 +
   1.409 +void
   1.410 +RotatedContentBuffer::FlushBuffers()
   1.411 +{
   1.412 +  if (mDTBuffer) {
   1.413 +    mDTBuffer->Flush();
   1.414 +  }
   1.415 +  if (mDTBufferOnWhite) {
   1.416 +    mDTBufferOnWhite->Flush();
   1.417 +  }
   1.418 +}
   1.419 +
   1.420 +RotatedContentBuffer::PaintState
   1.421 +RotatedContentBuffer::BeginPaint(ThebesLayer* aLayer,
   1.422 +                                 uint32_t aFlags)
   1.423 +{
   1.424 +  PaintState result;
   1.425 +  // We need to disable rotation if we're going to be resampled when
   1.426 +  // drawing, because we might sample across the rotation boundary.
   1.427 +  bool canHaveRotation = gfxPlatform::BufferRotationEnabled() &&
   1.428 +                         !(aFlags & (PAINT_WILL_RESAMPLE | PAINT_NO_ROTATION));
   1.429 +
   1.430 +  nsIntRegion validRegion = aLayer->GetValidRegion();
   1.431 +
   1.432 +  bool canUseOpaqueSurface = aLayer->CanUseOpaqueSurface();
   1.433 +  ContentType layerContentType =
   1.434 +    canUseOpaqueSurface ? gfxContentType::COLOR :
   1.435 +                          gfxContentType::COLOR_ALPHA;
   1.436 +
   1.437 +  SurfaceMode mode;
   1.438 +  nsIntRegion neededRegion;
   1.439 +  bool canReuseBuffer;
   1.440 +  nsIntRect destBufferRect;
   1.441 +
   1.442 +  while (true) {
   1.443 +    mode = aLayer->GetSurfaceMode();
   1.444 +    neededRegion = aLayer->GetVisibleRegion();
   1.445 +    canReuseBuffer = HaveBuffer() && BufferSizeOkFor(neededRegion.GetBounds().Size());
   1.446 +    result.mContentType = layerContentType;
   1.447 +
   1.448 +    if (canReuseBuffer) {
   1.449 +      if (mBufferRect.Contains(neededRegion.GetBounds())) {
   1.450 +        // We don't need to adjust mBufferRect.
   1.451 +        destBufferRect = mBufferRect;
   1.452 +      } else if (neededRegion.GetBounds().Size() <= mBufferRect.Size()) {
   1.453 +        // The buffer's big enough but doesn't contain everything that's
   1.454 +        // going to be visible. We'll move it.
   1.455 +        destBufferRect = nsIntRect(neededRegion.GetBounds().TopLeft(), mBufferRect.Size());
   1.456 +      } else {
   1.457 +        destBufferRect = neededRegion.GetBounds();
   1.458 +      }
   1.459 +    } else {
   1.460 +      // We won't be reusing the buffer.  Compute a new rect.
   1.461 +      destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
   1.462 +    }
   1.463 +
   1.464 +    if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
   1.465 +#if defined(MOZ_GFX_OPTIMIZE_MOBILE) || defined(MOZ_WIDGET_GONK)
   1.466 +      mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
   1.467 +#else
   1.468 +      if (!aLayer->GetParent() ||
   1.469 +          !aLayer->GetParent()->SupportsComponentAlphaChildren() ||
   1.470 +          !aLayer->Manager()->IsCompositingCheap() ||
   1.471 +          !aLayer->AsShadowableLayer() ||
   1.472 +          !aLayer->AsShadowableLayer()->HasShadow() ||
   1.473 +          !gfxPrefs::ComponentAlphaEnabled()) {
   1.474 +        mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
   1.475 +      } else {
   1.476 +        result.mContentType = gfxContentType::COLOR;
   1.477 +      }
   1.478 +#endif
   1.479 +    }
   1.480 +
   1.481 +    if ((aFlags & PAINT_WILL_RESAMPLE) &&
   1.482 +        (!neededRegion.GetBounds().IsEqualInterior(destBufferRect) ||
   1.483 +         neededRegion.GetNumRects() > 1)) {
   1.484 +      // The area we add to neededRegion might not be painted opaquely
   1.485 +      if (mode == SurfaceMode::SURFACE_OPAQUE) {
   1.486 +        result.mContentType = gfxContentType::COLOR_ALPHA;
   1.487 +        mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
   1.488 +      }
   1.489 +
   1.490 +      // We need to validate the entire buffer, to make sure that only valid
   1.491 +      // pixels are sampled
   1.492 +      neededRegion = destBufferRect;
   1.493 +    }
   1.494 +
   1.495 +    // If we have an existing buffer, but the content type has changed or we
   1.496 +    // have transitioned into/out of component alpha, then we need to recreate it.
   1.497 +    if (HaveBuffer() &&
   1.498 +        (result.mContentType != BufferContentType() ||
   1.499 +        (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != HaveBufferOnWhite())) {
   1.500 +
   1.501 +      // We're effectively clearing the valid region, so we need to draw
   1.502 +      // the entire needed region now.
   1.503 +      result.mRegionToInvalidate = aLayer->GetValidRegion();
   1.504 +      validRegion.SetEmpty();
   1.505 +      Clear();
   1.506 +      // Restart decision process with the cleared buffer. We can only go
   1.507 +      // around the loop one more iteration, since mDTBuffer is null now.
   1.508 +      continue;
   1.509 +    }
   1.510 +
   1.511 +    break;
   1.512 +  }
   1.513 +
   1.514 +  NS_ASSERTION(destBufferRect.Contains(neededRegion.GetBounds()),
   1.515 +               "Destination rect doesn't contain what we need to paint");
   1.516 +
   1.517 +  result.mRegionToDraw.Sub(neededRegion, validRegion);
   1.518 +
   1.519 +  if (result.mRegionToDraw.IsEmpty())
   1.520 +    return result;
   1.521 +
   1.522 +  // Do not modify result.mRegionToDraw or result.mContentType after this call.
   1.523 +  // Do not modify mBufferRect, mBufferRotation, or mDidSelfCopy,
   1.524 +  // or call CreateBuffer before this call.
   1.525 +  FinalizeFrame(result.mRegionToDraw);
   1.526 +
   1.527 +  nsIntRect drawBounds = result.mRegionToDraw.GetBounds();
   1.528 +  RefPtr<DrawTarget> destDTBuffer;
   1.529 +  RefPtr<DrawTarget> destDTBufferOnWhite;
   1.530 +  uint32_t bufferFlags = canHaveRotation ? ALLOW_REPEAT : 0;
   1.531 +  if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
   1.532 +    bufferFlags |= BUFFER_COMPONENT_ALPHA;
   1.533 +  }
   1.534 +  if (canReuseBuffer) {
   1.535 +    if (!EnsureBuffer()) {
   1.536 +      return result;
   1.537 +    }
   1.538 +    nsIntRect keepArea;
   1.539 +    if (keepArea.IntersectRect(destBufferRect, mBufferRect)) {
   1.540 +      // Set mBufferRotation so that the pixels currently in mDTBuffer
   1.541 +      // will still be rendered in the right place when mBufferRect
   1.542 +      // changes to destBufferRect.
   1.543 +      nsIntPoint newRotation = mBufferRotation +
   1.544 +        (destBufferRect.TopLeft() - mBufferRect.TopLeft());
   1.545 +      WrapRotationAxis(&newRotation.x, mBufferRect.width);
   1.546 +      WrapRotationAxis(&newRotation.y, mBufferRect.height);
   1.547 +      NS_ASSERTION(nsIntRect(nsIntPoint(0,0), mBufferRect.Size()).Contains(newRotation),
   1.548 +                   "newRotation out of bounds");
   1.549 +      int32_t xBoundary = destBufferRect.XMost() - newRotation.x;
   1.550 +      int32_t yBoundary = destBufferRect.YMost() - newRotation.y;
   1.551 +      bool drawWrapsBuffer = (drawBounds.x < xBoundary && xBoundary < drawBounds.XMost()) ||
   1.552 +                             (drawBounds.y < yBoundary && yBoundary < drawBounds.YMost());
   1.553 +      if ((drawWrapsBuffer && !(aFlags & PAINT_CAN_DRAW_ROTATED)) ||
   1.554 +          (newRotation != nsIntPoint(0,0) && !canHaveRotation)) {
   1.555 +        // The stuff we need to redraw will wrap around an edge of the
   1.556 +        // buffer (and the caller doesn't know how to support that), so
   1.557 +        // move the pixels we can keep into a position that lets us
   1.558 +        // redraw in just one quadrant.
   1.559 +        if (mBufferRotation == nsIntPoint(0,0)) {
   1.560 +          nsIntRect srcRect(nsIntPoint(0, 0), mBufferRect.Size());
   1.561 +          nsIntPoint dest = mBufferRect.TopLeft() - destBufferRect.TopLeft();
   1.562 +          MOZ_ASSERT(mDTBuffer);
   1.563 +          mDTBuffer->CopyRect(IntRect(srcRect.x, srcRect.y, srcRect.width, srcRect.height),
   1.564 +                              IntPoint(dest.x, dest.y));
   1.565 +          if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
   1.566 +            if (!EnsureBufferOnWhite()) {
   1.567 +              return result;
   1.568 +            }
   1.569 +            MOZ_ASSERT(mDTBufferOnWhite);
   1.570 +            mDTBufferOnWhite->CopyRect(IntRect(srcRect.x, srcRect.y, srcRect.width, srcRect.height),
   1.571 +                                       IntPoint(dest.x, dest.y));
   1.572 +          }
   1.573 +          result.mDidSelfCopy = true;
   1.574 +          mDidSelfCopy = true;
   1.575 +          // Don't set destBuffer; we special-case self-copies, and
   1.576 +          // just did the necessary work above.
   1.577 +          mBufferRect = destBufferRect;
   1.578 +        } else {
   1.579 +          // With azure and a data surface perform an buffer unrotate
   1.580 +          // (SelfCopy).
   1.581 +          unsigned char* data;
   1.582 +          IntSize size;
   1.583 +          int32_t stride;
   1.584 +          SurfaceFormat format;
   1.585 +
   1.586 +          if (mDTBuffer->LockBits(&data, &size, &stride, &format)) {
   1.587 +            uint8_t bytesPerPixel = BytesPerPixel(format);
   1.588 +            BufferUnrotate(data,
   1.589 +                           size.width * bytesPerPixel,
   1.590 +                           size.height, stride,
   1.591 +                           newRotation.x * bytesPerPixel, newRotation.y);
   1.592 +            mDTBuffer->ReleaseBits(data);
   1.593 +
   1.594 +            if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
   1.595 +              if (!EnsureBufferOnWhite()) {
   1.596 +                return result;
   1.597 +              }
   1.598 +              MOZ_ASSERT(mDTBufferOnWhite);
   1.599 +              mDTBufferOnWhite->LockBits(&data, &size, &stride, &format);
   1.600 +              uint8_t bytesPerPixel = BytesPerPixel(format);
   1.601 +              BufferUnrotate(data,
   1.602 +                             size.width * bytesPerPixel,
   1.603 +                             size.height, stride,
   1.604 +                             newRotation.x * bytesPerPixel, newRotation.y);
   1.605 +              mDTBufferOnWhite->ReleaseBits(data);
   1.606 +            }
   1.607 +
   1.608 +            // Buffer unrotate moves all the pixels, note that
   1.609 +            // we self copied for SyncBackToFrontBuffer
   1.610 +            result.mDidSelfCopy = true;
   1.611 +            mDidSelfCopy = true;
   1.612 +            mBufferRect = destBufferRect;
   1.613 +            mBufferRotation = nsIntPoint(0, 0);
   1.614 +          }
   1.615 +
   1.616 +          if (!result.mDidSelfCopy) {
   1.617 +            destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
   1.618 +            CreateBuffer(result.mContentType, destBufferRect, bufferFlags,
   1.619 +                         &destDTBuffer, &destDTBufferOnWhite);
   1.620 +            if (!destDTBuffer) {
   1.621 +              return result;
   1.622 +            }
   1.623 +          }
   1.624 +        }
   1.625 +      } else {
   1.626 +        mBufferRect = destBufferRect;
   1.627 +        mBufferRotation = newRotation;
   1.628 +      }
   1.629 +    } else {
   1.630 +      // No pixels are going to be kept. The whole visible region
   1.631 +      // will be redrawn, so we don't need to copy anything, so we don't
   1.632 +      // set destBuffer.
   1.633 +      mBufferRect = destBufferRect;
   1.634 +      mBufferRotation = nsIntPoint(0,0);
   1.635 +    }
   1.636 +  } else {
   1.637 +    // The buffer's not big enough, so allocate a new one
   1.638 +    CreateBuffer(result.mContentType, destBufferRect, bufferFlags,
   1.639 +                 &destDTBuffer, &destDTBufferOnWhite);
   1.640 +    if (!destDTBuffer) {
   1.641 +      return result;
   1.642 +    }
   1.643 +  }
   1.644 +
   1.645 +  NS_ASSERTION(!(aFlags & PAINT_WILL_RESAMPLE) || destBufferRect == neededRegion.GetBounds(),
   1.646 +               "If we're resampling, we need to validate the entire buffer");
   1.647 +
   1.648 +  // If we have no buffered data already, then destBuffer will be a fresh buffer
   1.649 +  // and we do not need to clear it below.
   1.650 +  bool isClear = !HaveBuffer();
   1.651 +
   1.652 +  if (destDTBuffer) {
   1.653 +    if (!isClear && (mode != SurfaceMode::SURFACE_COMPONENT_ALPHA || HaveBufferOnWhite())) {
   1.654 +      // Copy the bits
   1.655 +      nsIntPoint offset = -destBufferRect.TopLeft();
   1.656 +      Matrix mat;
   1.657 +      mat.Translate(offset.x, offset.y);
   1.658 +      destDTBuffer->SetTransform(mat);
   1.659 +      if (!EnsureBuffer()) {
   1.660 +        return result;
   1.661 +      }
   1.662 +       MOZ_ASSERT(mDTBuffer, "Have we got a Thebes buffer for some reason?");
   1.663 +      DrawBufferWithRotation(destDTBuffer, BUFFER_BLACK, 1.0, CompositionOp::OP_SOURCE);
   1.664 +      destDTBuffer->SetTransform(Matrix());
   1.665 +
   1.666 +      if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
   1.667 +        NS_ASSERTION(destDTBufferOnWhite, "Must have a white buffer!");
   1.668 +        destDTBufferOnWhite->SetTransform(mat);
   1.669 +        if (!EnsureBufferOnWhite()) {
   1.670 +          return result;
   1.671 +        }
   1.672 +        MOZ_ASSERT(mDTBufferOnWhite, "Have we got a Thebes buffer for some reason?");
   1.673 +        DrawBufferWithRotation(destDTBufferOnWhite, BUFFER_WHITE, 1.0, CompositionOp::OP_SOURCE);
   1.674 +        destDTBufferOnWhite->SetTransform(Matrix());
   1.675 +      }
   1.676 +    }
   1.677 +
   1.678 +    mDTBuffer = destDTBuffer.forget();
   1.679 +    mDTBufferOnWhite = destDTBufferOnWhite.forget();
   1.680 +    mBufferRect = destBufferRect;
   1.681 +    mBufferRotation = nsIntPoint(0,0);
   1.682 +  }
   1.683 +  NS_ASSERTION(canHaveRotation || mBufferRotation == nsIntPoint(0,0),
   1.684 +               "Rotation disabled, but we have nonzero rotation?");
   1.685 +
   1.686 +  nsIntRegion invalidate;
   1.687 +  invalidate.Sub(aLayer->GetValidRegion(), destBufferRect);
   1.688 +  result.mRegionToInvalidate.Or(result.mRegionToInvalidate, invalidate);
   1.689 +  result.mClip = DrawRegionClip::DRAW_SNAPPED;
   1.690 +  result.mMode = mode;
   1.691 +
   1.692 +  return result;
   1.693 +}
   1.694 +
   1.695 +DrawTarget*
   1.696 +RotatedContentBuffer::BorrowDrawTargetForPainting(const PaintState& aPaintState,
   1.697 +                                                  DrawIterator* aIter /* = nullptr */)
   1.698 +{
   1.699 +  if (aPaintState.mMode == SurfaceMode::SURFACE_NONE) {
   1.700 +    return nullptr;
   1.701 +  }
   1.702 +
   1.703 +  DrawTarget* result = BorrowDrawTargetForQuadrantUpdate(aPaintState.mRegionToDraw.GetBounds(),
   1.704 +                                                         BUFFER_BOTH, aIter);
   1.705 +  if (!result) {
   1.706 +    return nullptr;
   1.707 +  }
   1.708 +  const nsIntRegion* drawPtr = &aPaintState.mRegionToDraw;
   1.709 +  if (aIter) {
   1.710 +    // The iterators draw region currently only contains the bounds of the region,
   1.711 +    // this makes it the precise region.
   1.712 +    aIter->mDrawRegion.And(aIter->mDrawRegion, aPaintState.mRegionToDraw);
   1.713 +    drawPtr = &aIter->mDrawRegion;
   1.714 +  }
   1.715 +
   1.716 +  if (aPaintState.mMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
   1.717 +    MOZ_ASSERT(mDTBuffer && mDTBufferOnWhite);
   1.718 +    nsIntRegionRectIterator iter(*drawPtr);
   1.719 +    const nsIntRect *iterRect;
   1.720 +    while ((iterRect = iter.Next())) {
   1.721 +      mDTBuffer->FillRect(Rect(iterRect->x, iterRect->y, iterRect->width, iterRect->height),
   1.722 +                          ColorPattern(Color(0.0, 0.0, 0.0, 1.0)));
   1.723 +      mDTBufferOnWhite->FillRect(Rect(iterRect->x, iterRect->y, iterRect->width, iterRect->height),
   1.724 +                                 ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
   1.725 +    }
   1.726 +  } else if (aPaintState.mContentType == gfxContentType::COLOR_ALPHA && HaveBuffer()) {
   1.727 +    // HaveBuffer() => we have an existing buffer that we must clear
   1.728 +    nsIntRegionRectIterator iter(*drawPtr);
   1.729 +    const nsIntRect *iterRect;
   1.730 +    while ((iterRect = iter.Next())) {
   1.731 +      result->ClearRect(Rect(iterRect->x, iterRect->y, iterRect->width, iterRect->height));
   1.732 +    }
   1.733 +  }
   1.734 +
   1.735 +  return result;
   1.736 +}
   1.737 +
   1.738 +}
   1.739 +}
   1.740 +

mercurial