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 +