1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "base/basictypes.h"
8 #include "BasicLayersImpl.h"
9 #include "ThebesLayerBuffer.h"
11 #include "gfxContext.h"
12 #include "gfxPlatform.h"
13 #include "gfxTeeSurface.h"
15 #include "ipc/AutoOpenSurface.h"
16 #include "nsDeviceContext.h"
17 #include "GeckoProfiler.h"
27 RotatedBuffer::GetQuadrantRectangle(XSide aXSide
, YSide aYSide
) const
29 // quadrantTranslation is the amount we translate the top-left
30 // of the quadrant by to get coordinates relative to the layer
31 nsIntPoint quadrantTranslation
= -mBufferRotation
;
32 quadrantTranslation
.x
+= aXSide
== LEFT
? mBufferRect
.width
: 0;
33 quadrantTranslation
.y
+= aYSide
== TOP
? mBufferRect
.height
: 0;
34 return mBufferRect
+ quadrantTranslation
;
38 * @param aXSide LEFT means we draw from the left side of the buffer (which
39 * is drawn on the right side of mBufferRect). RIGHT means we draw from
40 * the right side of the buffer (which is drawn on the left side of
42 * @param aYSide TOP means we draw from the top side of the buffer (which
43 * is drawn on the bottom side of mBufferRect). BOTTOM means we draw from
44 * the bottom side of the buffer (which is drawn on the top side of
48 RotatedBuffer::DrawBufferQuadrant(gfxContext
* aTarget
,
49 XSide aXSide
, YSide aYSide
,
50 ContextSource aSource
,
53 const gfxMatrix
* aMaskTransform
) const
55 // The rectangle that we're going to fill. Basically we're going to
56 // render the buffer at mBufferRect + quadrantTranslation to get the
57 // pixels in the right place, but we're only going to paint within
59 nsIntRect quadrantRect
= GetQuadrantRectangle(aXSide
, aYSide
);
61 if (!fillRect
.IntersectRect(mBufferRect
, quadrantRect
)) {
65 nsRefPtr
<gfxASurface
> source
;
67 if (aSource
== BUFFER_BLACK
) {
70 } else if (mDTBuffer
) {
71 source
= gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mDTBuffer
);
73 NS_RUNTIMEABORT("Can't draw a RotatedBuffer without any buffer!");
76 MOZ_ASSERT(aSource
== BUFFER_WHITE
);
78 source
= mBufferOnWhite
;
79 } else if (mDTBufferOnWhite
) {
80 source
= gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mDTBufferOnWhite
);
82 NS_RUNTIMEABORT("Can't draw a RotatedBuffer without any buffer!");
88 aTarget
->Rectangle(gfxRect(fillRect
.x
, fillRect
.y
,
89 fillRect
.width
, fillRect
.height
),
92 gfxPoint
quadrantTranslation(quadrantRect
.x
, quadrantRect
.y
);
93 nsRefPtr
<gfxPattern
> pattern
= new gfxPattern(source
);
95 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
96 gfxPattern::GraphicsFilter filter
= gfxPattern::FILTER_NEAREST
;
97 pattern
->SetFilter(filter
);
100 gfxContextMatrixAutoSaveRestore
saveMatrix(aTarget
);
102 // Transform from user -> buffer space.
104 transform
.Translate(-quadrantTranslation
);
106 pattern
->SetMatrix(transform
);
107 aTarget
->SetPattern(pattern
);
110 if (aOpacity
== 1.0) {
111 aTarget
->SetMatrix(*aMaskTransform
);
112 aTarget
->Mask(aMask
);
114 aTarget
->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA
);
115 aTarget
->Paint(aOpacity
);
116 aTarget
->PopGroupToSource();
117 aTarget
->SetMatrix(*aMaskTransform
);
118 aTarget
->Mask(aMask
);
121 if (aOpacity
== 1.0) {
126 aTarget
->Paint(aOpacity
);
131 nsRefPtr
<gfxASurface
> surf
= aTarget
->CurrentSurface();
136 * @param aXSide LEFT means we draw from the left side of the buffer (which
137 * is drawn on the right side of mBufferRect). RIGHT means we draw from
138 * the right side of the buffer (which is drawn on the left side of
140 * @param aYSide TOP means we draw from the top side of the buffer (which
141 * is drawn on the bottom side of mBufferRect). BOTTOM means we draw from
142 * the bottom side of the buffer (which is drawn on the top side of
146 RotatedBuffer::DrawBufferQuadrant(gfx::DrawTarget
* aTarget
,
147 XSide aXSide
, YSide aYSide
,
148 ContextSource aSource
,
150 gfx::SourceSurface
* aMask
,
151 const gfx::Matrix
* aMaskTransform
) const
153 // The rectangle that we're going to fill. Basically we're going to
154 // render the buffer at mBufferRect + quadrantTranslation to get the
155 // pixels in the right place, but we're only going to paint within
157 nsIntRect quadrantRect
= GetQuadrantRectangle(aXSide
, aYSide
);
159 if (!fillRect
.IntersectRect(mBufferRect
, quadrantRect
))
162 gfx::Point
quadrantTranslation(quadrantRect
.x
, quadrantRect
.y
);
164 RefPtr
<SourceSurface
> snapshot
;
165 if (aSource
== BUFFER_BLACK
) {
166 snapshot
= mDTBuffer
->Snapshot();
168 MOZ_ASSERT(aSource
== BUFFER_WHITE
);
169 snapshot
= mDTBufferOnWhite
->Snapshot();
172 // Transform from user -> buffer space.
174 transform
.Translate(quadrantTranslation
.x
, quadrantTranslation
.y
);
176 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
177 SurfacePattern
source(snapshot
, EXTEND_CLAMP
, transform
, FILTER_POINT
);
179 SurfacePattern
source(snapshot
, EXTEND_CLAMP
, transform
);
183 SurfacePattern
mask(aMask
, EXTEND_CLAMP
, *aMaskTransform
);
185 aTarget
->Mask(source
, mask
, DrawOptions(aOpacity
));
187 aTarget
->FillRect(gfx::Rect(fillRect
.x
, fillRect
.y
,
188 fillRect
.width
, fillRect
.height
),
189 source
, DrawOptions(aOpacity
));
196 RotatedBuffer::DrawBufferWithRotation(gfxContext
* aTarget
, ContextSource aSource
,
199 const gfxMatrix
* aMaskTransform
) const
201 PROFILER_LABEL("RotatedBuffer", "DrawBufferWithRotation");
202 // Draw four quadrants. We could use REPEAT_, but it's probably better
203 // not to, to be performance-safe.
204 DrawBufferQuadrant(aTarget
, LEFT
, TOP
, aSource
, aOpacity
, aMask
, aMaskTransform
);
205 DrawBufferQuadrant(aTarget
, RIGHT
, TOP
, aSource
, aOpacity
, aMask
, aMaskTransform
);
206 DrawBufferQuadrant(aTarget
, LEFT
, BOTTOM
, aSource
, aOpacity
, aMask
, aMaskTransform
);
207 DrawBufferQuadrant(aTarget
, RIGHT
, BOTTOM
, aSource
, aOpacity
, aMask
, aMaskTransform
);
211 RotatedBuffer::DrawBufferWithRotation(gfx::DrawTarget
*aTarget
, ContextSource aSource
,
213 gfx::SourceSurface
* aMask
,
214 const gfx::Matrix
* aMaskTransform
) const
216 PROFILER_LABEL("RotatedBuffer", "DrawBufferWithRotation");
217 // See above, in Azure Repeat should always be a safe, even faster choice
218 // though! Particularly on D2D Repeat should be a lot faster, need to look
219 // into that. TODO[Bas]
220 DrawBufferQuadrant(aTarget
, LEFT
, TOP
, aSource
, aOpacity
, aMask
, aMaskTransform
);
221 DrawBufferQuadrant(aTarget
, RIGHT
, TOP
, aSource
, aOpacity
, aMask
, aMaskTransform
);
222 DrawBufferQuadrant(aTarget
, LEFT
, BOTTOM
, aSource
, aOpacity
, aMask
, aMaskTransform
);
223 DrawBufferQuadrant(aTarget
, RIGHT
, BOTTOM
, aSource
, aOpacity
, aMask
, aMaskTransform
);
227 ThebesLayerBuffer::IsClippingCheap(gfxContext
* aTarget
, const nsIntRegion
& aRegion
)
229 // Assume clipping is cheap if the context just has an integer
230 // translation, and the visible region is simple.
231 return !aTarget
->CurrentMatrix().HasNonIntegerTranslation() &&
232 aRegion
.GetNumRects() <= 1;
236 ThebesLayerBuffer::DrawTo(ThebesLayer
* aLayer
,
240 const gfxMatrix
* aMaskTransform
)
244 if (aTarget
->IsCairo()) {
246 // If the entire buffer is valid, we can just draw the whole thing,
247 // no need to clip. But we'll still clip if clipping is cheap ---
248 // that might let us copy a smaller region of the buffer.
249 // Also clip to the visible region if we're told to.
250 if (!aLayer
->GetValidRegion().Contains(BufferRect()) ||
251 (ToData(aLayer
)->GetClipToVisibleRegion() &&
252 !aLayer
->GetVisibleRegion().Contains(BufferRect())) ||
253 IsClippingCheap(aTarget
, aLayer
->GetEffectiveVisibleRegion())) {
254 // We don't want to draw invalid stuff, so we need to clip. Might as
255 // well clip to the smallest area possible --- the visible region.
256 // Bug 599189 if there is a non-integer-translation transform in aTarget,
257 // we might sample pixels outside GetEffectiveVisibleRegion(), which is wrong
258 // and may cause gray lines.
259 gfxUtils::ClipToRegionSnapped(aTarget
, aLayer
->GetEffectiveVisibleRegion());
262 DrawBufferWithRotation(aTarget
, BUFFER_BLACK
, aOpacity
, aMask
, aMaskTransform
);
265 RefPtr
<DrawTarget
> dt
= aTarget
->GetDrawTarget();
267 // If the entire buffer is valid, we can just draw the whole thing,
268 // no need to clip. But we'll still clip if clipping is cheap ---
269 // that might let us copy a smaller region of the buffer.
270 // Also clip to the visible region if we're told to.
271 if (!aLayer
->GetValidRegion().Contains(BufferRect()) ||
272 (ToData(aLayer
)->GetClipToVisibleRegion() &&
273 !aLayer
->GetVisibleRegion().Contains(BufferRect())) ||
274 IsClippingCheap(aTarget
, aLayer
->GetEffectiveVisibleRegion())) {
275 // We don't want to draw invalid stuff, so we need to clip. Might as
276 // well clip to the smallest area possible --- the visible region.
277 // Bug 599189 if there is a non-integer-translation transform in aTarget,
278 // we might sample pixels outside GetEffectiveVisibleRegion(), which is wrong
279 // and may cause gray lines.
280 gfxUtils::ClipToRegionSnapped(dt
, aLayer
->GetEffectiveVisibleRegion());
283 DrawBufferWithRotation(aTarget
, BUFFER_BLACK
, aOpacity
, aMask
, aMaskTransform
);
289 FillSurface(gfxASurface
* aSurface
, const nsIntRegion
& aRegion
,
290 const nsIntPoint
& aOffset
, const gfxRGBA
& aColor
)
292 nsRefPtr
<gfxContext
> ctx
= new gfxContext(aSurface
);
293 ctx
->Translate(-gfxPoint(aOffset
.x
, aOffset
.y
));
294 gfxUtils::ClipToRegion(ctx
, aRegion
);
295 ctx
->SetColor(aColor
);
299 already_AddRefed
<gfxContext
>
300 ThebesLayerBuffer::GetContextForQuadrantUpdate(const nsIntRect
& aBounds
, ContextSource aSource
, nsIntPoint
*aTopLeft
)
304 nsRefPtr
<gfxContext
> ctx
;
305 if (aSource
== BUFFER_BOTH
&& HaveBufferOnWhite()) {
306 EnsureBufferOnWhite();
307 MOZ_ASSERT(mBuffer
, "We don't support azure here yet");
308 gfxASurface
* surfaces
[2] = { mBuffer
, mBufferOnWhite
};
309 nsRefPtr
<gfxTeeSurface
> surf
= new gfxTeeSurface(surfaces
, ArrayLength(surfaces
));
311 // XXX If the device offset is set on the individual surfaces instead of on
312 // the tee surface, we render in the wrong place. Why?
313 gfxPoint deviceOffset
= mBuffer
->GetDeviceOffset();
314 surfaces
[0]->SetDeviceOffset(gfxPoint(0, 0));
315 surfaces
[1]->SetDeviceOffset(gfxPoint(0, 0));
316 surf
->SetDeviceOffset(deviceOffset
);
318 surf
->SetAllowUseAsSource(false);
319 ctx
= new gfxContext(surf
);
320 } else if (aSource
== BUFFER_WHITE
) {
321 EnsureBufferOnWhite();
322 if (mBufferOnWhite
) {
323 ctx
= new gfxContext(mBufferOnWhite
);
325 ctx
= new gfxContext(mDTBufferOnWhite
);
328 // BUFFER_BLACK, or BUFFER_BOTH with a single buffer.
330 ctx
= new gfxContext(mBuffer
);
332 ctx
= new gfxContext(mDTBuffer
);
336 // Figure out which quadrant to draw in
337 int32_t xBoundary
= mBufferRect
.XMost() - mBufferRotation
.x
;
338 int32_t yBoundary
= mBufferRect
.YMost() - mBufferRotation
.y
;
339 XSide sideX
= aBounds
.XMost() <= xBoundary
? RIGHT
: LEFT
;
340 YSide sideY
= aBounds
.YMost() <= yBoundary
? BOTTOM
: TOP
;
341 nsIntRect quadrantRect
= GetQuadrantRectangle(sideX
, sideY
);
342 NS_ASSERTION(quadrantRect
.Contains(aBounds
), "Messed up quadrants");
343 ctx
->Translate(-gfxPoint(quadrantRect
.x
, quadrantRect
.y
));
346 *aTopLeft
= nsIntPoint(quadrantRect
.x
, quadrantRect
.y
);
352 gfxASurface::gfxContentType
353 ThebesLayerBuffer::BufferContentType()
356 return mBuffer
->GetContentType();
358 if (mBufferProvider
) {
359 return mBufferProvider
->GetContentType();
362 switch (mDTBuffer
->GetFormat()) {
364 return gfxASurface::CONTENT_ALPHA
;
365 case FORMAT_B8G8R8A8
:
366 case FORMAT_R8G8B8A8
:
367 return gfxASurface::CONTENT_COLOR_ALPHA
;
369 return gfxASurface::CONTENT_COLOR
;
372 return gfxASurface::CONTENT_SENTINEL
;
376 ThebesLayerBuffer::BufferSizeOkFor(const nsIntSize
& aSize
)
378 return (aSize
== mBufferRect
.Size() ||
379 (SizedToVisibleBounds
!= mBufferSizePolicy
&&
380 aSize
< mBufferRect
.Size()));
384 ThebesLayerBuffer::EnsureBuffer()
386 if ((!mBuffer
&& !mDTBuffer
) && mBufferProvider
) {
387 if (SupportsAzureContent()) {
388 mDTBuffer
= mBufferProvider
->LockDrawTarget();
390 mBuffer
= mBufferProvider
->LockSurface();
396 ThebesLayerBuffer::EnsureBufferOnWhite()
398 if ((!mBufferOnWhite
&& !mDTBufferOnWhite
) && mBufferProviderOnWhite
) {
399 if (SupportsAzureContent()) {
400 mDTBufferOnWhite
= mBufferProviderOnWhite
->LockDrawTarget();
402 mBufferOnWhite
= mBufferProviderOnWhite
->LockSurface();
408 ThebesLayerBuffer::HaveBuffer() const
410 return mDTBuffer
|| mBuffer
|| mBufferProvider
;
414 ThebesLayerBuffer::HaveBufferOnWhite() const
416 return mDTBufferOnWhite
|| mBufferOnWhite
|| mBufferProviderOnWhite
;
420 WrapRotationAxis(int32_t* aRotationPoint
, int32_t aSize
)
422 if (*aRotationPoint
< 0) {
423 *aRotationPoint
+= aSize
;
424 } else if (*aRotationPoint
>= aSize
) {
425 *aRotationPoint
-= aSize
;
430 ComputeBufferRect(const nsIntRect
& aRequestedRect
)
432 nsIntRect
rect(aRequestedRect
);
433 // Set a minimum width to guarantee a minimum size of buffers we
434 // allocate (and work around problems on some platforms with smaller
435 // dimensions). 64 is the magic number needed to work around the
436 // rendering glitch, and guarantees image rows can be SIMD'd for
437 // even r5g6b5 surfaces pretty much everywhere.
438 rect
.width
= std::max(aRequestedRect
.width
, 64);
439 #ifdef MOZ_WIDGET_GONK
440 // Set a minumum height to guarantee a minumum height of buffers we
441 // allocate. Some GL implementations fail to render gralloc textures
442 // with a height 9px-16px. It happens on Adreno 200. Adreno 320 does not
443 // have this problem. 32 is choosed as alignment of gralloc buffers.
445 // XXX it might be better to disable it on the gpu that does not have
446 // the height problem.
447 rect
.height
= std::max(aRequestedRect
.height
, 32);
452 ThebesLayerBuffer::PaintState
453 ThebesLayerBuffer::BeginPaint(ThebesLayer
* aLayer
, ContentType aContentType
,
457 // We need to disable rotation if we're going to be resampled when
458 // drawing, because we might sample across the rotation boundary.
459 bool canHaveRotation
= !(aFlags
& (PAINT_WILL_RESAMPLE
| PAINT_NO_ROTATION
));
461 nsIntRegion validRegion
= aLayer
->GetValidRegion();
463 Layer::SurfaceMode mode
;
464 ContentType contentType
;
465 nsIntRegion neededRegion
;
467 nsIntRect destBufferRect
;
470 mode
= aLayer
->GetSurfaceMode();
471 contentType
= aContentType
;
472 neededRegion
= aLayer
->GetVisibleRegion();
473 canReuseBuffer
= HaveBuffer() && BufferSizeOkFor(neededRegion
.GetBounds().Size());
475 if (canReuseBuffer
) {
476 if (mBufferRect
.Contains(neededRegion
.GetBounds())) {
477 // We don't need to adjust mBufferRect.
478 destBufferRect
= mBufferRect
;
479 } else if (neededRegion
.GetBounds().Size() <= mBufferRect
.Size()) {
480 // The buffer's big enough but doesn't contain everything that's
481 // going to be visible. We'll move it.
482 destBufferRect
= nsIntRect(neededRegion
.GetBounds().TopLeft(), mBufferRect
.Size());
484 destBufferRect
= neededRegion
.GetBounds();
487 // We won't be reusing the buffer. Compute a new rect.
488 destBufferRect
= ComputeBufferRect(neededRegion
.GetBounds());
491 if (mode
== Layer::SURFACE_COMPONENT_ALPHA
) {
492 #if defined(MOZ_GFX_OPTIMIZE_MOBILE) || defined(MOZ_WIDGET_GONK)
493 mode
= Layer::SURFACE_SINGLE_CHANNEL_ALPHA
;
495 if (!aLayer
->GetParent() ||
496 !aLayer
->GetParent()->SupportsComponentAlphaChildren() ||
497 !aLayer
->Manager()->IsCompositingCheap() ||
498 !aLayer
->AsShadowableLayer() ||
499 !aLayer
->AsShadowableLayer()->HasShadow() ||
500 SupportsAzureContent()) {
501 mode
= Layer::SURFACE_SINGLE_CHANNEL_ALPHA
;
503 contentType
= gfxASurface::CONTENT_COLOR
;
508 if ((aFlags
& PAINT_WILL_RESAMPLE
) &&
509 (!neededRegion
.GetBounds().IsEqualInterior(destBufferRect
) ||
510 neededRegion
.GetNumRects() > 1)) {
511 // The area we add to neededRegion might not be painted opaquely
512 if (mode
== Layer::SURFACE_OPAQUE
) {
513 contentType
= gfxASurface::CONTENT_COLOR_ALPHA
;
514 mode
= Layer::SURFACE_SINGLE_CHANNEL_ALPHA
;
517 // We need to validate the entire buffer, to make sure that only valid
518 // pixels are sampled
519 neededRegion
= destBufferRect
;
522 // If we have an existing buffer, but the content type has changed or we
523 // have transitioned into/out of component alpha, then we need to recreate it.
525 (contentType
!= BufferContentType() ||
526 mode
== Layer::SURFACE_COMPONENT_ALPHA
) != (HaveBufferOnWhite())) {
528 // We're effectively clearing the valid region, so we need to draw
529 // the entire needed region now.
530 result
.mRegionToInvalidate
= aLayer
->GetValidRegion();
531 validRegion
.SetEmpty();
533 // Restart decision process with the cleared buffer. We can only go
534 // around the loop one more iteration, since mBuffer is null now.
541 NS_ASSERTION(destBufferRect
.Contains(neededRegion
.GetBounds()),
542 "Destination rect doesn't contain what we need to paint");
544 result
.mRegionToDraw
.Sub(neededRegion
, validRegion
);
545 if (result
.mRegionToDraw
.IsEmpty())
548 nsIntRect drawBounds
= result
.mRegionToDraw
.GetBounds();
549 nsRefPtr
<gfxASurface
> destBuffer
;
550 nsRefPtr
<gfxASurface
> destBufferOnWhite
;
551 RefPtr
<DrawTarget
> destDTBuffer
;
552 RefPtr
<DrawTarget
> destDTBufferOnWhite
;
553 uint32_t bufferFlags
= canHaveRotation
? ALLOW_REPEAT
: 0;
554 if (mode
== Layer::SURFACE_COMPONENT_ALPHA
) {
555 bufferFlags
|= BUFFER_COMPONENT_ALPHA
;
557 if (canReuseBuffer
) {
560 if (keepArea
.IntersectRect(destBufferRect
, mBufferRect
)) {
561 // Set mBufferRotation so that the pixels currently in mBuffer
562 // will still be rendered in the right place when mBufferRect
563 // changes to destBufferRect.
564 nsIntPoint newRotation
= mBufferRotation
+
565 (destBufferRect
.TopLeft() - mBufferRect
.TopLeft());
566 WrapRotationAxis(&newRotation
.x
, mBufferRect
.width
);
567 WrapRotationAxis(&newRotation
.y
, mBufferRect
.height
);
568 NS_ASSERTION(nsIntRect(nsIntPoint(0,0), mBufferRect
.Size()).Contains(newRotation
),
569 "newRotation out of bounds");
570 int32_t xBoundary
= destBufferRect
.XMost() - newRotation
.x
;
571 int32_t yBoundary
= destBufferRect
.YMost() - newRotation
.y
;
572 if ((drawBounds
.x
< xBoundary
&& xBoundary
< drawBounds
.XMost()) ||
573 (drawBounds
.y
< yBoundary
&& yBoundary
< drawBounds
.YMost()) ||
574 (newRotation
!= nsIntPoint(0,0) && !canHaveRotation
)) {
575 // The stuff we need to redraw will wrap around an edge of the
576 // buffer, so move the pixels we can keep into a position that
577 // lets us redraw in just one quadrant.
578 if (mBufferRotation
== nsIntPoint(0,0)) {
579 nsIntRect
srcRect(nsIntPoint(0, 0), mBufferRect
.Size());
580 nsIntPoint dest
= mBufferRect
.TopLeft() - destBufferRect
.TopLeft();
582 mBuffer
->MovePixels(srcRect
, dest
);
583 if (mode
== Layer::SURFACE_COMPONENT_ALPHA
) {
584 EnsureBufferOnWhite();
585 MOZ_ASSERT(mBufferOnWhite
);
586 mBufferOnWhite
->MovePixels(srcRect
, dest
);
589 RefPtr
<SourceSurface
> source
= mDTBuffer
->Snapshot();
590 mDTBuffer
->CopySurface(source
,
591 IntRect(srcRect
.x
, srcRect
.y
, srcRect
.width
, srcRect
.height
),
592 IntPoint(dest
.x
, dest
.y
));
594 result
.mDidSelfCopy
= true;
595 // Don't set destBuffer; we special-case self-copies, and
596 // just did the necessary work above.
597 mBufferRect
= destBufferRect
;
599 // We can't do a real self-copy because the buffer is rotated.
600 // So allocate a new buffer for the destination.
601 destBufferRect
= ComputeBufferRect(neededRegion
.GetBounds());
602 if (SupportsAzureContent()) {
603 destDTBuffer
= CreateDTBuffer(contentType
, destBufferRect
, bufferFlags
);
605 destBuffer
= CreateBuffer(contentType
, destBufferRect
, bufferFlags
, getter_AddRefs(destBufferOnWhite
));
607 if (!destBuffer
&& !destDTBuffer
)
611 mBufferRect
= destBufferRect
;
612 mBufferRotation
= newRotation
;
615 // No pixels are going to be kept. The whole visible region
616 // will be redrawn, so we don't need to copy anything, so we don't
618 mBufferRect
= destBufferRect
;
619 mBufferRotation
= nsIntPoint(0,0);
622 // The buffer's not big enough, so allocate a new one
623 if (SupportsAzureContent()) {
624 destDTBuffer
= CreateDTBuffer(contentType
, destBufferRect
, bufferFlags
);
626 destBuffer
= CreateBuffer(contentType
, destBufferRect
, bufferFlags
, getter_AddRefs(destBufferOnWhite
));
628 if (!destBuffer
&& !destDTBuffer
)
632 NS_ASSERTION(!(aFlags
& PAINT_WILL_RESAMPLE
) || destBufferRect
== neededRegion
.GetBounds(),
633 "If we're resampling, we need to validate the entire buffer");
635 // If we have no buffered data already, then destBuffer will be a fresh buffer
636 // and we do not need to clear it below.
637 bool isClear
= !HaveBuffer();
640 if (!isClear
&& (mode
!= Layer::SURFACE_COMPONENT_ALPHA
|| HaveBufferOnWhite())) {
642 nsRefPtr
<gfxContext
> tmpCtx
= new gfxContext(destBuffer
);
643 nsIntPoint offset
= -destBufferRect
.TopLeft();
644 tmpCtx
->SetOperator(gfxContext::OPERATOR_SOURCE
);
645 tmpCtx
->Translate(gfxPoint(offset
.x
, offset
.y
));
647 DrawBufferWithRotation(tmpCtx
, BUFFER_BLACK
);
649 if (mode
== Layer::SURFACE_COMPONENT_ALPHA
) {
650 EnsureBufferOnWhite();
651 NS_ASSERTION(destBufferOnWhite
, "Must have a white buffer!");
652 nsRefPtr
<gfxContext
> tmpCtx
= new gfxContext(destBufferOnWhite
);
653 nsIntPoint offset
= -destBufferRect
.TopLeft();
654 tmpCtx
->SetOperator(gfxContext::OPERATOR_SOURCE
);
655 tmpCtx
->Translate(gfxPoint(offset
.x
, offset
.y
));
656 DrawBufferWithRotation(tmpCtx
, BUFFER_WHITE
);
660 mBuffer
= destBuffer
.forget();
661 mBufferRect
= destBufferRect
;
662 mBufferOnWhite
= destBufferOnWhite
.forget();
663 mBufferRotation
= nsIntPoint(0,0);
664 } else if (destDTBuffer
) {
665 if (!isClear
&& (mode
!= Layer::SURFACE_COMPONENT_ALPHA
|| HaveBufferOnWhite())) {
666 MOZ_ASSERT(mode
!= Layer::SURFACE_COMPONENT_ALPHA
, "We don't support azure here yet");
668 nsIntPoint offset
= -destBufferRect
.TopLeft();
670 mat
.Translate(offset
.x
, offset
.y
);
671 destDTBuffer
->SetTransform(mat
);
673 DrawBufferWithRotation(destDTBuffer
, BUFFER_BLACK
);
674 destDTBuffer
->SetTransform(Matrix());
677 mDTBuffer
= destDTBuffer
.forget();
678 mBufferRect
= destBufferRect
;
679 mBufferRotation
= nsIntPoint(0,0);
681 NS_ASSERTION(canHaveRotation
|| mBufferRotation
== nsIntPoint(0,0),
682 "Rotation disabled, but we have nonzero rotation?");
684 nsIntRegion invalidate
;
685 invalidate
.Sub(aLayer
->GetValidRegion(), destBufferRect
);
686 result
.mRegionToInvalidate
.Or(result
.mRegionToInvalidate
, invalidate
);
689 result
.mContext
= GetContextForQuadrantUpdate(drawBounds
, BUFFER_BOTH
, &topLeft
);
691 if (mode
== Layer::SURFACE_COMPONENT_ALPHA
) {
692 MOZ_ASSERT(mBuffer
&& mBufferOnWhite
, "Must not be using azure!");
693 FillSurface(mBuffer
, result
.mRegionToDraw
, topLeft
, gfxRGBA(0.0, 0.0, 0.0, 1.0));
694 FillSurface(mBufferOnWhite
, result
.mRegionToDraw
, topLeft
, gfxRGBA(1.0, 1.0, 1.0, 1.0));
695 gfxUtils::ClipToRegionSnapped(result
.mContext
, result
.mRegionToDraw
);
696 } else if (contentType
== gfxASurface::CONTENT_COLOR_ALPHA
&& !isClear
) {
697 if (result
.mContext
->IsCairo()) {
698 gfxUtils::ClipToRegionSnapped(result
.mContext
, result
.mRegionToDraw
);
699 result
.mContext
->SetOperator(gfxContext::OPERATOR_CLEAR
);
700 result
.mContext
->Paint();
701 result
.mContext
->SetOperator(gfxContext::OPERATOR_OVER
);
703 nsIntRegionRectIterator
iter(result
.mRegionToDraw
);
704 const nsIntRect
*iterRect
;
705 while ((iterRect
= iter
.Next())) {
706 result
.mContext
->GetDrawTarget()->ClearRect(Rect(iterRect
->x
, iterRect
->y
, iterRect
->width
, iterRect
->height
));
708 // Clear will do something expensive with a complex clip pushed, so clip
710 gfxUtils::ClipToRegionSnapped(result
.mContext
, result
.mRegionToDraw
);
713 gfxUtils::ClipToRegionSnapped(result
.mContext
, result
.mRegionToDraw
);