Back out a5a5d2c176f7 (bug 882865) because of Android test failures on a CLOSED TREE
[gecko.git] / gfx / layers / ThebesLayerBuffer.cpp
blobf8d30ce9c2d843c53db13b65c268d28184c02f82
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"
10 #include "Layers.h"
11 #include "gfxContext.h"
12 #include "gfxPlatform.h"
13 #include "gfxTeeSurface.h"
14 #include "gfxUtils.h"
15 #include "ipc/AutoOpenSurface.h"
16 #include "nsDeviceContext.h"
17 #include "GeckoProfiler.h"
18 #include <algorithm>
20 namespace mozilla {
22 using namespace gfx;
24 namespace layers {
26 nsIntRect
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;
37 /**
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
41 * mBufferRect).
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
45 * mBufferRect).
47 void
48 RotatedBuffer::DrawBufferQuadrant(gfxContext* aTarget,
49 XSide aXSide, YSide aYSide,
50 ContextSource aSource,
51 float aOpacity,
52 gfxASurface* aMask,
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
58 // mBufferRect
59 nsIntRect quadrantRect = GetQuadrantRectangle(aXSide, aYSide);
60 nsIntRect fillRect;
61 if (!fillRect.IntersectRect(mBufferRect, quadrantRect)) {
62 return;
65 nsRefPtr<gfxASurface> source;
67 if (aSource == BUFFER_BLACK) {
68 if (mBuffer) {
69 source = mBuffer;
70 } else if (mDTBuffer) {
71 source = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mDTBuffer);
72 } else {
73 NS_RUNTIMEABORT("Can't draw a RotatedBuffer without any buffer!");
75 } else {
76 MOZ_ASSERT(aSource == BUFFER_WHITE);
77 if (mBufferOnWhite) {
78 source = mBufferOnWhite;
79 } else if (mDTBufferOnWhite) {
80 source = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mDTBufferOnWhite);
81 } else {
82 NS_RUNTIMEABORT("Can't draw a RotatedBuffer without any buffer!");
87 aTarget->NewPath();
88 aTarget->Rectangle(gfxRect(fillRect.x, fillRect.y,
89 fillRect.width, fillRect.height),
90 true);
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);
98 #endif
100 gfxContextMatrixAutoSaveRestore saveMatrix(aTarget);
102 // Transform from user -> buffer space.
103 gfxMatrix transform;
104 transform.Translate(-quadrantTranslation);
106 pattern->SetMatrix(transform);
107 aTarget->SetPattern(pattern);
109 if (aMask) {
110 if (aOpacity == 1.0) {
111 aTarget->SetMatrix(*aMaskTransform);
112 aTarget->Mask(aMask);
113 } else {
114 aTarget->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
115 aTarget->Paint(aOpacity);
116 aTarget->PopGroupToSource();
117 aTarget->SetMatrix(*aMaskTransform);
118 aTarget->Mask(aMask);
120 } else {
121 if (aOpacity == 1.0) {
122 aTarget->Fill();
123 } else {
124 aTarget->Save();
125 aTarget->Clip();
126 aTarget->Paint(aOpacity);
127 aTarget->Restore();
131 nsRefPtr<gfxASurface> surf = aTarget->CurrentSurface();
132 surf->Flush();
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
139 * mBufferRect).
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
143 * mBufferRect).
145 void
146 RotatedBuffer::DrawBufferQuadrant(gfx::DrawTarget* aTarget,
147 XSide aXSide, YSide aYSide,
148 ContextSource aSource,
149 float aOpacity,
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
156 // mBufferRect
157 nsIntRect quadrantRect = GetQuadrantRectangle(aXSide, aYSide);
158 nsIntRect fillRect;
159 if (!fillRect.IntersectRect(mBufferRect, quadrantRect))
160 return;
162 gfx::Point quadrantTranslation(quadrantRect.x, quadrantRect.y);
164 RefPtr<SourceSurface> snapshot;
165 if (aSource == BUFFER_BLACK) {
166 snapshot = mDTBuffer->Snapshot();
167 } else {
168 MOZ_ASSERT(aSource == BUFFER_WHITE);
169 snapshot = mDTBufferOnWhite->Snapshot();
172 // Transform from user -> buffer space.
173 Matrix transform;
174 transform.Translate(quadrantTranslation.x, quadrantTranslation.y);
176 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
177 SurfacePattern source(snapshot, EXTEND_CLAMP, transform, FILTER_POINT);
178 #else
179 SurfacePattern source(snapshot, EXTEND_CLAMP, transform);
180 #endif
182 if (aMask) {
183 SurfacePattern mask(aMask, EXTEND_CLAMP, *aMaskTransform);
185 aTarget->Mask(source, mask, DrawOptions(aOpacity));
186 } else {
187 aTarget->FillRect(gfx::Rect(fillRect.x, fillRect.y,
188 fillRect.width, fillRect.height),
189 source, DrawOptions(aOpacity));
192 aTarget->Flush();
195 void
196 RotatedBuffer::DrawBufferWithRotation(gfxContext* aTarget, ContextSource aSource,
197 float aOpacity,
198 gfxASurface* aMask,
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);
210 void
211 RotatedBuffer::DrawBufferWithRotation(gfx::DrawTarget *aTarget, ContextSource aSource,
212 float aOpacity,
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);
226 /* static */ bool
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;
235 void
236 ThebesLayerBuffer::DrawTo(ThebesLayer* aLayer,
237 gfxContext* aTarget,
238 float aOpacity,
239 gfxASurface* aMask,
240 const gfxMatrix* aMaskTransform)
242 EnsureBuffer();
244 if (aTarget->IsCairo()) {
245 aTarget->Save();
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);
263 aTarget->Restore();
264 } else {
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);
284 aTarget->Restore();
288 static void
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);
296 ctx->Paint();
299 already_AddRefed<gfxContext>
300 ThebesLayerBuffer::GetContextForQuadrantUpdate(const nsIntRect& aBounds, ContextSource aSource, nsIntPoint *aTopLeft)
302 EnsureBuffer();
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);
324 } else {
325 ctx = new gfxContext(mDTBufferOnWhite);
327 } else {
328 // BUFFER_BLACK, or BUFFER_BOTH with a single buffer.
329 if (mBuffer) {
330 ctx = new gfxContext(mBuffer);
331 } else {
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));
345 if (aTopLeft) {
346 *aTopLeft = nsIntPoint(quadrantRect.x, quadrantRect.y);
349 return ctx.forget();
352 gfxASurface::gfxContentType
353 ThebesLayerBuffer::BufferContentType()
355 if (mBuffer) {
356 return mBuffer->GetContentType();
358 if (mBufferProvider) {
359 return mBufferProvider->GetContentType();
361 if (mDTBuffer) {
362 switch (mDTBuffer->GetFormat()) {
363 case FORMAT_A8:
364 return gfxASurface::CONTENT_ALPHA;
365 case FORMAT_B8G8R8A8:
366 case FORMAT_R8G8B8A8:
367 return gfxASurface::CONTENT_COLOR_ALPHA;
368 default:
369 return gfxASurface::CONTENT_COLOR;
372 return gfxASurface::CONTENT_SENTINEL;
375 bool
376 ThebesLayerBuffer::BufferSizeOkFor(const nsIntSize& aSize)
378 return (aSize == mBufferRect.Size() ||
379 (SizedToVisibleBounds != mBufferSizePolicy &&
380 aSize < mBufferRect.Size()));
383 void
384 ThebesLayerBuffer::EnsureBuffer()
386 if ((!mBuffer && !mDTBuffer) && mBufferProvider) {
387 if (SupportsAzureContent()) {
388 mDTBuffer = mBufferProvider->LockDrawTarget();
389 } else {
390 mBuffer = mBufferProvider->LockSurface();
395 void
396 ThebesLayerBuffer::EnsureBufferOnWhite()
398 if ((!mBufferOnWhite && !mDTBufferOnWhite) && mBufferProviderOnWhite) {
399 if (SupportsAzureContent()) {
400 mDTBufferOnWhite = mBufferProviderOnWhite->LockDrawTarget();
401 } else {
402 mBufferOnWhite = mBufferProviderOnWhite->LockSurface();
407 bool
408 ThebesLayerBuffer::HaveBuffer() const
410 return mDTBuffer || mBuffer || mBufferProvider;
413 bool
414 ThebesLayerBuffer::HaveBufferOnWhite() const
416 return mDTBufferOnWhite || mBufferOnWhite || mBufferProviderOnWhite;
419 static void
420 WrapRotationAxis(int32_t* aRotationPoint, int32_t aSize)
422 if (*aRotationPoint < 0) {
423 *aRotationPoint += aSize;
424 } else if (*aRotationPoint >= aSize) {
425 *aRotationPoint -= aSize;
429 static nsIntRect
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.
444 // See Bug 873937.
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);
448 #endif
449 return rect;
452 ThebesLayerBuffer::PaintState
453 ThebesLayerBuffer::BeginPaint(ThebesLayer* aLayer, ContentType aContentType,
454 uint32_t aFlags)
456 PaintState result;
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;
466 bool canReuseBuffer;
467 nsIntRect destBufferRect;
469 while (true) {
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());
483 } else {
484 destBufferRect = neededRegion.GetBounds();
486 } else {
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;
494 #else
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;
502 } else {
503 contentType = gfxASurface::CONTENT_COLOR;
505 #endif
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.
524 if (HaveBuffer() &&
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();
532 Clear();
533 // Restart decision process with the cleared buffer. We can only go
534 // around the loop one more iteration, since mBuffer is null now.
535 continue;
538 break;
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())
546 return result;
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) {
558 EnsureBuffer();
559 nsIntRect keepArea;
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();
581 if (mBuffer) {
582 mBuffer->MovePixels(srcRect, dest);
583 if (mode == Layer::SURFACE_COMPONENT_ALPHA) {
584 EnsureBufferOnWhite();
585 MOZ_ASSERT(mBufferOnWhite);
586 mBufferOnWhite->MovePixels(srcRect, dest);
588 } else {
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;
598 } else {
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);
604 } else {
605 destBuffer = CreateBuffer(contentType, destBufferRect, bufferFlags, getter_AddRefs(destBufferOnWhite));
607 if (!destBuffer && !destDTBuffer)
608 return result;
610 } else {
611 mBufferRect = destBufferRect;
612 mBufferRotation = newRotation;
614 } else {
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
617 // set destBuffer.
618 mBufferRect = destBufferRect;
619 mBufferRotation = nsIntPoint(0,0);
621 } else {
622 // The buffer's not big enough, so allocate a new one
623 if (SupportsAzureContent()) {
624 destDTBuffer = CreateDTBuffer(contentType, destBufferRect, bufferFlags);
625 } else {
626 destBuffer = CreateBuffer(contentType, destBufferRect, bufferFlags, getter_AddRefs(destBufferOnWhite));
628 if (!destBuffer && !destDTBuffer)
629 return result;
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();
639 if (destBuffer) {
640 if (!isClear && (mode != Layer::SURFACE_COMPONENT_ALPHA || HaveBufferOnWhite())) {
641 // Copy the bits
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));
646 EnsureBuffer();
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");
667 // Copy the bits
668 nsIntPoint offset = -destBufferRect.TopLeft();
669 Matrix mat;
670 mat.Translate(offset.x, offset.y);
671 destDTBuffer->SetTransform(mat);
672 EnsureBuffer();
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);
688 nsIntPoint topLeft;
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);
702 } else {
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
709 // here.
710 gfxUtils::ClipToRegionSnapped(result.mContext, result.mRegionToDraw);
712 } else {
713 gfxUtils::ClipToRegionSnapped(result.mContext, result.mRegionToDraw);
716 return result;