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 :
|