|
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 PROFILER_LABEL |
|
13 #include "Layers.h" // for ThebesLayer, 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 "nsSize.h" // for nsIntSize |
|
28 #include "gfx2DGlue.h" |
|
29 |
|
30 namespace mozilla { |
|
31 |
|
32 using namespace gfx; |
|
33 |
|
34 namespace layers { |
|
35 |
|
36 nsIntRect |
|
37 RotatedBuffer::GetQuadrantRectangle(XSide aXSide, YSide aYSide) const |
|
38 { |
|
39 // quadrantTranslation is the amount we translate the top-left |
|
40 // of the quadrant by to get coordinates relative to the layer |
|
41 nsIntPoint quadrantTranslation = -mBufferRotation; |
|
42 quadrantTranslation.x += aXSide == LEFT ? mBufferRect.width : 0; |
|
43 quadrantTranslation.y += aYSide == TOP ? mBufferRect.height : 0; |
|
44 return mBufferRect + quadrantTranslation; |
|
45 } |
|
46 |
|
47 Rect |
|
48 RotatedBuffer::GetSourceRectangle(XSide aXSide, YSide aYSide) const |
|
49 { |
|
50 Rect result; |
|
51 if (aXSide == LEFT) { |
|
52 result.x = 0; |
|
53 result.width = mBufferRotation.x; |
|
54 } else { |
|
55 result.x = mBufferRotation.x; |
|
56 result.width = mBufferRect.width - mBufferRotation.x; |
|
57 } |
|
58 if (aYSide == TOP) { |
|
59 result.y = 0; |
|
60 result.height = mBufferRotation.y; |
|
61 } else { |
|
62 result.y = mBufferRotation.y; |
|
63 result.height = mBufferRect.height - mBufferRotation.y; |
|
64 } |
|
65 return result; |
|
66 } |
|
67 |
|
68 /** |
|
69 * @param aXSide LEFT means we draw from the left side of the buffer (which |
|
70 * is drawn on the right side of mBufferRect). RIGHT means we draw from |
|
71 * the right side of the buffer (which is drawn on the left side of |
|
72 * mBufferRect). |
|
73 * @param aYSide TOP means we draw from the top side of the buffer (which |
|
74 * is drawn on the bottom side of mBufferRect). BOTTOM means we draw from |
|
75 * the bottom side of the buffer (which is drawn on the top side of |
|
76 * mBufferRect). |
|
77 */ |
|
78 void |
|
79 RotatedBuffer::DrawBufferQuadrant(gfx::DrawTarget* aTarget, |
|
80 XSide aXSide, YSide aYSide, |
|
81 ContextSource aSource, |
|
82 float aOpacity, |
|
83 gfx::CompositionOp aOperator, |
|
84 gfx::SourceSurface* aMask, |
|
85 const gfx::Matrix* aMaskTransform) const |
|
86 { |
|
87 // The rectangle that we're going to fill. Basically we're going to |
|
88 // render the buffer at mBufferRect + quadrantTranslation to get the |
|
89 // pixels in the right place, but we're only going to paint within |
|
90 // mBufferRect |
|
91 nsIntRect quadrantRect = GetQuadrantRectangle(aXSide, aYSide); |
|
92 nsIntRect fillRect; |
|
93 if (!fillRect.IntersectRect(mBufferRect, quadrantRect)) |
|
94 return; |
|
95 |
|
96 gfx::Point quadrantTranslation(quadrantRect.x, quadrantRect.y); |
|
97 |
|
98 MOZ_ASSERT(aOperator == CompositionOp::OP_OVER || aOperator == CompositionOp::OP_SOURCE); |
|
99 // direct2d is much slower when using OP_SOURCE so use OP_OVER and |
|
100 // (maybe) a clear instead. Normally we need to draw in a single operation |
|
101 // (to avoid flickering) but direct2d is ok since it defers rendering. |
|
102 // We should try abstract this logic in a helper when we have other use |
|
103 // cases. |
|
104 if (aTarget->GetType() == BackendType::DIRECT2D && aOperator == CompositionOp::OP_SOURCE) { |
|
105 aOperator = CompositionOp::OP_OVER; |
|
106 if (mDTBuffer->GetFormat() == SurfaceFormat::B8G8R8A8) { |
|
107 aTarget->ClearRect(ToRect(fillRect)); |
|
108 } |
|
109 } |
|
110 |
|
111 RefPtr<gfx::SourceSurface> snapshot; |
|
112 if (aSource == BUFFER_BLACK) { |
|
113 snapshot = mDTBuffer->Snapshot(); |
|
114 } else { |
|
115 MOZ_ASSERT(aSource == BUFFER_WHITE); |
|
116 snapshot = mDTBufferOnWhite->Snapshot(); |
|
117 } |
|
118 |
|
119 if (aOperator == CompositionOp::OP_SOURCE) { |
|
120 // OP_SOURCE is unbounded in Azure, and we really don't want that behaviour here. |
|
121 // We also can't do a ClearRect+FillRect since we need the drawing to happen |
|
122 // as an atomic operation (to prevent flickering). |
|
123 aTarget->PushClipRect(gfx::Rect(fillRect.x, fillRect.y, |
|
124 fillRect.width, fillRect.height)); |
|
125 } |
|
126 |
|
127 if (aMask) { |
|
128 Matrix oldTransform = aTarget->GetTransform(); |
|
129 |
|
130 // Transform from user -> buffer space. |
|
131 Matrix transform; |
|
132 transform.Translate(quadrantTranslation.x, quadrantTranslation.y); |
|
133 |
|
134 Matrix inverseMask = *aMaskTransform; |
|
135 inverseMask.Invert(); |
|
136 |
|
137 transform *= oldTransform; |
|
138 transform *= inverseMask; |
|
139 |
|
140 #ifdef MOZ_GFX_OPTIMIZE_MOBILE |
|
141 SurfacePattern source(snapshot, ExtendMode::CLAMP, transform, Filter::POINT); |
|
142 #else |
|
143 SurfacePattern source(snapshot, ExtendMode::CLAMP, transform); |
|
144 #endif |
|
145 |
|
146 aTarget->SetTransform(*aMaskTransform); |
|
147 aTarget->MaskSurface(source, aMask, Point(0, 0), DrawOptions(aOpacity, aOperator)); |
|
148 aTarget->SetTransform(oldTransform); |
|
149 } else { |
|
150 #ifdef MOZ_GFX_OPTIMIZE_MOBILE |
|
151 DrawSurfaceOptions options(Filter::POINT); |
|
152 #else |
|
153 DrawSurfaceOptions options; |
|
154 #endif |
|
155 aTarget->DrawSurface(snapshot, ToRect(fillRect), |
|
156 GetSourceRectangle(aXSide, aYSide), |
|
157 options, |
|
158 DrawOptions(aOpacity, aOperator)); |
|
159 } |
|
160 |
|
161 if (aOperator == CompositionOp::OP_SOURCE) { |
|
162 aTarget->PopClip(); |
|
163 } |
|
164 } |
|
165 |
|
166 void |
|
167 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 PROFILER_LABEL("RotatedBuffer", "DrawBufferWithRotation"); |
|
174 // See above, in Azure Repeat should always be a safe, even faster choice |
|
175 // though! Particularly on D2D Repeat should be a lot faster, need to look |
|
176 // into that. TODO[Bas] |
|
177 DrawBufferQuadrant(aTarget, LEFT, TOP, aSource, aOpacity, aOperator, aMask, aMaskTransform); |
|
178 DrawBufferQuadrant(aTarget, RIGHT, TOP, aSource, aOpacity, aOperator, aMask, aMaskTransform); |
|
179 DrawBufferQuadrant(aTarget, LEFT, BOTTOM, aSource, aOpacity, aOperator, aMask, aMaskTransform); |
|
180 DrawBufferQuadrant(aTarget, RIGHT, BOTTOM, aSource, aOpacity, aOperator,aMask, aMaskTransform); |
|
181 } |
|
182 |
|
183 /* static */ bool |
|
184 RotatedContentBuffer::IsClippingCheap(DrawTarget* aTarget, const nsIntRegion& aRegion) |
|
185 { |
|
186 // Assume clipping is cheap if the draw target just has an integer |
|
187 // translation, and the visible region is simple. |
|
188 return !aTarget->GetTransform().HasNonIntegerTranslation() && |
|
189 aRegion.GetNumRects() <= 1; |
|
190 } |
|
191 |
|
192 void |
|
193 RotatedContentBuffer::DrawTo(ThebesLayer* aLayer, |
|
194 DrawTarget* aTarget, |
|
195 float aOpacity, |
|
196 CompositionOp aOp, |
|
197 SourceSurface* aMask, |
|
198 const Matrix* aMaskTransform) |
|
199 { |
|
200 if (!EnsureBuffer()) { |
|
201 return; |
|
202 } |
|
203 |
|
204 bool clipped = false; |
|
205 |
|
206 // If the entire buffer is valid, we can just draw the whole thing, |
|
207 // no need to clip. But we'll still clip if clipping is cheap --- |
|
208 // that might let us copy a smaller region of the buffer. |
|
209 // Also clip to the visible region if we're told to. |
|
210 if (!aLayer->GetValidRegion().Contains(BufferRect()) || |
|
211 (ToData(aLayer)->GetClipToVisibleRegion() && |
|
212 !aLayer->GetVisibleRegion().Contains(BufferRect())) || |
|
213 IsClippingCheap(aTarget, aLayer->GetEffectiveVisibleRegion())) { |
|
214 // We don't want to draw invalid stuff, so we need to clip. Might as |
|
215 // well clip to the smallest area possible --- the visible region. |
|
216 // Bug 599189 if there is a non-integer-translation transform in aTarget, |
|
217 // we might sample pixels outside GetEffectiveVisibleRegion(), which is wrong |
|
218 // and may cause gray lines. |
|
219 gfxUtils::ClipToRegionSnapped(aTarget, aLayer->GetEffectiveVisibleRegion()); |
|
220 clipped = true; |
|
221 } |
|
222 |
|
223 DrawBufferWithRotation(aTarget, BUFFER_BLACK, aOpacity, aOp, aMask, aMaskTransform); |
|
224 if (clipped) { |
|
225 aTarget->PopClip(); |
|
226 } |
|
227 } |
|
228 |
|
229 DrawTarget* |
|
230 RotatedContentBuffer::BorrowDrawTargetForQuadrantUpdate(const nsIntRect& aBounds, |
|
231 ContextSource aSource, |
|
232 DrawIterator* aIter) |
|
233 { |
|
234 nsIntRect bounds = aBounds; |
|
235 if (aIter) { |
|
236 // If an iterator was provided, then BeginPaint must have been run with |
|
237 // PAINT_CAN_DRAW_ROTATED, and the draw region might cover multiple quadrants. |
|
238 // Iterate over each of them, and return an appropriate buffer each time we find |
|
239 // one that intersects the draw region. The iterator mCount value tracks which |
|
240 // quadrants we have considered across multiple calls to this function. |
|
241 aIter->mDrawRegion.SetEmpty(); |
|
242 while (aIter->mCount < 4) { |
|
243 nsIntRect quadrant = GetQuadrantRectangle((aIter->mCount & 1) ? LEFT : RIGHT, |
|
244 (aIter->mCount & 2) ? TOP : BOTTOM); |
|
245 aIter->mDrawRegion.And(aBounds, quadrant); |
|
246 aIter->mCount++; |
|
247 if (!aIter->mDrawRegion.IsEmpty()) { |
|
248 break; |
|
249 } |
|
250 } |
|
251 if (aIter->mDrawRegion.IsEmpty()) { |
|
252 return nullptr; |
|
253 } |
|
254 bounds = aIter->mDrawRegion.GetBounds(); |
|
255 } |
|
256 |
|
257 if (!EnsureBuffer()) { |
|
258 return nullptr; |
|
259 } |
|
260 |
|
261 MOZ_ASSERT(!mLoanedDrawTarget, "draw target has been borrowed and not returned"); |
|
262 if (aSource == BUFFER_BOTH && HaveBufferOnWhite()) { |
|
263 if (!EnsureBufferOnWhite()) { |
|
264 return nullptr; |
|
265 } |
|
266 MOZ_ASSERT(mDTBuffer && mDTBufferOnWhite); |
|
267 mLoanedDrawTarget = Factory::CreateDualDrawTarget(mDTBuffer, mDTBufferOnWhite); |
|
268 } else if (aSource == BUFFER_WHITE) { |
|
269 if (!EnsureBufferOnWhite()) { |
|
270 return nullptr; |
|
271 } |
|
272 mLoanedDrawTarget = mDTBufferOnWhite; |
|
273 } else { |
|
274 // BUFFER_BLACK, or BUFFER_BOTH with a single buffer. |
|
275 mLoanedDrawTarget = mDTBuffer; |
|
276 } |
|
277 |
|
278 // Figure out which quadrant to draw in |
|
279 int32_t xBoundary = mBufferRect.XMost() - mBufferRotation.x; |
|
280 int32_t yBoundary = mBufferRect.YMost() - mBufferRotation.y; |
|
281 XSide sideX = bounds.XMost() <= xBoundary ? RIGHT : LEFT; |
|
282 YSide sideY = bounds.YMost() <= yBoundary ? BOTTOM : TOP; |
|
283 nsIntRect quadrantRect = GetQuadrantRectangle(sideX, sideY); |
|
284 NS_ASSERTION(quadrantRect.Contains(bounds), "Messed up quadrants"); |
|
285 |
|
286 mLoanedTransform = mLoanedDrawTarget->GetTransform(); |
|
287 mLoanedTransform.Translate(-quadrantRect.x, -quadrantRect.y); |
|
288 mLoanedDrawTarget->SetTransform(mLoanedTransform); |
|
289 mLoanedTransform.Translate(quadrantRect.x, quadrantRect.y); |
|
290 |
|
291 return mLoanedDrawTarget; |
|
292 } |
|
293 |
|
294 void |
|
295 BorrowDrawTarget::ReturnDrawTarget(gfx::DrawTarget*& aReturned) |
|
296 { |
|
297 MOZ_ASSERT(aReturned == mLoanedDrawTarget); |
|
298 mLoanedDrawTarget->SetTransform(mLoanedTransform); |
|
299 mLoanedDrawTarget = nullptr; |
|
300 aReturned = nullptr; |
|
301 } |
|
302 |
|
303 gfxContentType |
|
304 RotatedContentBuffer::BufferContentType() |
|
305 { |
|
306 if (mBufferProvider || mDTBuffer) { |
|
307 SurfaceFormat format; |
|
308 |
|
309 if (mBufferProvider) { |
|
310 format = mBufferProvider->GetFormat(); |
|
311 } else if (mDTBuffer) { |
|
312 format = mDTBuffer->GetFormat(); |
|
313 } |
|
314 |
|
315 return ContentForFormat(format); |
|
316 } |
|
317 return gfxContentType::SENTINEL; |
|
318 } |
|
319 |
|
320 bool |
|
321 RotatedContentBuffer::BufferSizeOkFor(const nsIntSize& aSize) |
|
322 { |
|
323 return (aSize == mBufferRect.Size() || |
|
324 (SizedToVisibleBounds != mBufferSizePolicy && |
|
325 aSize < mBufferRect.Size())); |
|
326 } |
|
327 |
|
328 bool |
|
329 RotatedContentBuffer::EnsureBuffer() |
|
330 { |
|
331 NS_ASSERTION(!mLoanedDrawTarget, "Loaned draw target must be returned"); |
|
332 if (!mDTBuffer) { |
|
333 if (mBufferProvider) { |
|
334 mDTBuffer = mBufferProvider->GetAsDrawTarget(); |
|
335 } |
|
336 } |
|
337 |
|
338 NS_WARN_IF_FALSE(mDTBuffer, "no buffer"); |
|
339 return !!mDTBuffer; |
|
340 } |
|
341 |
|
342 bool |
|
343 RotatedContentBuffer::EnsureBufferOnWhite() |
|
344 { |
|
345 NS_ASSERTION(!mLoanedDrawTarget, "Loaned draw target must be returned"); |
|
346 if (!mDTBufferOnWhite) { |
|
347 if (mBufferProviderOnWhite) { |
|
348 mDTBufferOnWhite = |
|
349 mBufferProviderOnWhite->GetAsDrawTarget(); |
|
350 } |
|
351 } |
|
352 |
|
353 NS_WARN_IF_FALSE(mDTBufferOnWhite, "no buffer"); |
|
354 return mDTBufferOnWhite; |
|
355 } |
|
356 |
|
357 bool |
|
358 RotatedContentBuffer::HaveBuffer() const |
|
359 { |
|
360 return mDTBuffer || mBufferProvider; |
|
361 } |
|
362 |
|
363 bool |
|
364 RotatedContentBuffer::HaveBufferOnWhite() const |
|
365 { |
|
366 return mDTBufferOnWhite || mBufferProviderOnWhite; |
|
367 } |
|
368 |
|
369 static void |
|
370 WrapRotationAxis(int32_t* aRotationPoint, int32_t aSize) |
|
371 { |
|
372 if (*aRotationPoint < 0) { |
|
373 *aRotationPoint += aSize; |
|
374 } else if (*aRotationPoint >= aSize) { |
|
375 *aRotationPoint -= aSize; |
|
376 } |
|
377 } |
|
378 |
|
379 static nsIntRect |
|
380 ComputeBufferRect(const nsIntRect& aRequestedRect) |
|
381 { |
|
382 nsIntRect rect(aRequestedRect); |
|
383 // Set a minimum width to guarantee a minimum size of buffers we |
|
384 // allocate (and work around problems on some platforms with smaller |
|
385 // dimensions). 64 is the magic number needed to work around the |
|
386 // rendering glitch, and guarantees image rows can be SIMD'd for |
|
387 // even r5g6b5 surfaces pretty much everywhere. |
|
388 rect.width = std::max(aRequestedRect.width, 64); |
|
389 #ifdef MOZ_WIDGET_GONK |
|
390 // Set a minumum height to guarantee a minumum height of buffers we |
|
391 // allocate. Some GL implementations fail to render gralloc textures |
|
392 // with a height 9px-16px. It happens on Adreno 200. Adreno 320 does not |
|
393 // have this problem. 32 is choosed as alignment of gralloc buffers. |
|
394 // See Bug 873937. |
|
395 // Increase the height only when the requested height is more than 0. |
|
396 // See Bug 895976. |
|
397 // XXX it might be better to disable it on the gpu that does not have |
|
398 // the height problem. |
|
399 if (rect.height > 0) { |
|
400 rect.height = std::max(aRequestedRect.height, 32); |
|
401 } |
|
402 #endif |
|
403 return rect; |
|
404 } |
|
405 |
|
406 void |
|
407 RotatedContentBuffer::FlushBuffers() |
|
408 { |
|
409 if (mDTBuffer) { |
|
410 mDTBuffer->Flush(); |
|
411 } |
|
412 if (mDTBufferOnWhite) { |
|
413 mDTBufferOnWhite->Flush(); |
|
414 } |
|
415 } |
|
416 |
|
417 RotatedContentBuffer::PaintState |
|
418 RotatedContentBuffer::BeginPaint(ThebesLayer* aLayer, |
|
419 uint32_t aFlags) |
|
420 { |
|
421 PaintState result; |
|
422 // We need to disable rotation if we're going to be resampled when |
|
423 // drawing, because we might sample across the rotation boundary. |
|
424 bool canHaveRotation = gfxPlatform::BufferRotationEnabled() && |
|
425 !(aFlags & (PAINT_WILL_RESAMPLE | PAINT_NO_ROTATION)); |
|
426 |
|
427 nsIntRegion validRegion = aLayer->GetValidRegion(); |
|
428 |
|
429 bool canUseOpaqueSurface = aLayer->CanUseOpaqueSurface(); |
|
430 ContentType layerContentType = |
|
431 canUseOpaqueSurface ? gfxContentType::COLOR : |
|
432 gfxContentType::COLOR_ALPHA; |
|
433 |
|
434 SurfaceMode mode; |
|
435 nsIntRegion neededRegion; |
|
436 bool canReuseBuffer; |
|
437 nsIntRect destBufferRect; |
|
438 |
|
439 while (true) { |
|
440 mode = aLayer->GetSurfaceMode(); |
|
441 neededRegion = aLayer->GetVisibleRegion(); |
|
442 canReuseBuffer = HaveBuffer() && BufferSizeOkFor(neededRegion.GetBounds().Size()); |
|
443 result.mContentType = layerContentType; |
|
444 |
|
445 if (canReuseBuffer) { |
|
446 if (mBufferRect.Contains(neededRegion.GetBounds())) { |
|
447 // We don't need to adjust mBufferRect. |
|
448 destBufferRect = mBufferRect; |
|
449 } else if (neededRegion.GetBounds().Size() <= mBufferRect.Size()) { |
|
450 // The buffer's big enough but doesn't contain everything that's |
|
451 // going to be visible. We'll move it. |
|
452 destBufferRect = nsIntRect(neededRegion.GetBounds().TopLeft(), mBufferRect.Size()); |
|
453 } else { |
|
454 destBufferRect = neededRegion.GetBounds(); |
|
455 } |
|
456 } else { |
|
457 // We won't be reusing the buffer. Compute a new rect. |
|
458 destBufferRect = ComputeBufferRect(neededRegion.GetBounds()); |
|
459 } |
|
460 |
|
461 if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) { |
|
462 #if defined(MOZ_GFX_OPTIMIZE_MOBILE) || defined(MOZ_WIDGET_GONK) |
|
463 mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA; |
|
464 #else |
|
465 if (!aLayer->GetParent() || |
|
466 !aLayer->GetParent()->SupportsComponentAlphaChildren() || |
|
467 !aLayer->Manager()->IsCompositingCheap() || |
|
468 !aLayer->AsShadowableLayer() || |
|
469 !aLayer->AsShadowableLayer()->HasShadow() || |
|
470 !gfxPrefs::ComponentAlphaEnabled()) { |
|
471 mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA; |
|
472 } else { |
|
473 result.mContentType = gfxContentType::COLOR; |
|
474 } |
|
475 #endif |
|
476 } |
|
477 |
|
478 if ((aFlags & PAINT_WILL_RESAMPLE) && |
|
479 (!neededRegion.GetBounds().IsEqualInterior(destBufferRect) || |
|
480 neededRegion.GetNumRects() > 1)) { |
|
481 // The area we add to neededRegion might not be painted opaquely |
|
482 if (mode == SurfaceMode::SURFACE_OPAQUE) { |
|
483 result.mContentType = gfxContentType::COLOR_ALPHA; |
|
484 mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA; |
|
485 } |
|
486 |
|
487 // We need to validate the entire buffer, to make sure that only valid |
|
488 // pixels are sampled |
|
489 neededRegion = destBufferRect; |
|
490 } |
|
491 |
|
492 // If we have an existing buffer, but the content type has changed or we |
|
493 // have transitioned into/out of component alpha, then we need to recreate it. |
|
494 if (HaveBuffer() && |
|
495 (result.mContentType != BufferContentType() || |
|
496 (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != HaveBufferOnWhite())) { |
|
497 |
|
498 // We're effectively clearing the valid region, so we need to draw |
|
499 // the entire needed region now. |
|
500 result.mRegionToInvalidate = aLayer->GetValidRegion(); |
|
501 validRegion.SetEmpty(); |
|
502 Clear(); |
|
503 // Restart decision process with the cleared buffer. We can only go |
|
504 // around the loop one more iteration, since mDTBuffer is null now. |
|
505 continue; |
|
506 } |
|
507 |
|
508 break; |
|
509 } |
|
510 |
|
511 NS_ASSERTION(destBufferRect.Contains(neededRegion.GetBounds()), |
|
512 "Destination rect doesn't contain what we need to paint"); |
|
513 |
|
514 result.mRegionToDraw.Sub(neededRegion, validRegion); |
|
515 |
|
516 if (result.mRegionToDraw.IsEmpty()) |
|
517 return result; |
|
518 |
|
519 // Do not modify result.mRegionToDraw or result.mContentType after this call. |
|
520 // Do not modify mBufferRect, mBufferRotation, or mDidSelfCopy, |
|
521 // or call CreateBuffer before this call. |
|
522 FinalizeFrame(result.mRegionToDraw); |
|
523 |
|
524 nsIntRect drawBounds = result.mRegionToDraw.GetBounds(); |
|
525 RefPtr<DrawTarget> destDTBuffer; |
|
526 RefPtr<DrawTarget> destDTBufferOnWhite; |
|
527 uint32_t bufferFlags = canHaveRotation ? ALLOW_REPEAT : 0; |
|
528 if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) { |
|
529 bufferFlags |= BUFFER_COMPONENT_ALPHA; |
|
530 } |
|
531 if (canReuseBuffer) { |
|
532 if (!EnsureBuffer()) { |
|
533 return result; |
|
534 } |
|
535 nsIntRect keepArea; |
|
536 if (keepArea.IntersectRect(destBufferRect, mBufferRect)) { |
|
537 // Set mBufferRotation so that the pixels currently in mDTBuffer |
|
538 // will still be rendered in the right place when mBufferRect |
|
539 // changes to destBufferRect. |
|
540 nsIntPoint newRotation = mBufferRotation + |
|
541 (destBufferRect.TopLeft() - mBufferRect.TopLeft()); |
|
542 WrapRotationAxis(&newRotation.x, mBufferRect.width); |
|
543 WrapRotationAxis(&newRotation.y, mBufferRect.height); |
|
544 NS_ASSERTION(nsIntRect(nsIntPoint(0,0), mBufferRect.Size()).Contains(newRotation), |
|
545 "newRotation out of bounds"); |
|
546 int32_t xBoundary = destBufferRect.XMost() - newRotation.x; |
|
547 int32_t yBoundary = destBufferRect.YMost() - newRotation.y; |
|
548 bool drawWrapsBuffer = (drawBounds.x < xBoundary && xBoundary < drawBounds.XMost()) || |
|
549 (drawBounds.y < yBoundary && yBoundary < drawBounds.YMost()); |
|
550 if ((drawWrapsBuffer && !(aFlags & PAINT_CAN_DRAW_ROTATED)) || |
|
551 (newRotation != nsIntPoint(0,0) && !canHaveRotation)) { |
|
552 // The stuff we need to redraw will wrap around an edge of the |
|
553 // buffer (and the caller doesn't know how to support that), so |
|
554 // move the pixels we can keep into a position that lets us |
|
555 // redraw in just one quadrant. |
|
556 if (mBufferRotation == nsIntPoint(0,0)) { |
|
557 nsIntRect srcRect(nsIntPoint(0, 0), mBufferRect.Size()); |
|
558 nsIntPoint dest = mBufferRect.TopLeft() - destBufferRect.TopLeft(); |
|
559 MOZ_ASSERT(mDTBuffer); |
|
560 mDTBuffer->CopyRect(IntRect(srcRect.x, srcRect.y, srcRect.width, srcRect.height), |
|
561 IntPoint(dest.x, dest.y)); |
|
562 if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) { |
|
563 if (!EnsureBufferOnWhite()) { |
|
564 return result; |
|
565 } |
|
566 MOZ_ASSERT(mDTBufferOnWhite); |
|
567 mDTBufferOnWhite->CopyRect(IntRect(srcRect.x, srcRect.y, srcRect.width, srcRect.height), |
|
568 IntPoint(dest.x, dest.y)); |
|
569 } |
|
570 result.mDidSelfCopy = true; |
|
571 mDidSelfCopy = true; |
|
572 // Don't set destBuffer; we special-case self-copies, and |
|
573 // just did the necessary work above. |
|
574 mBufferRect = destBufferRect; |
|
575 } else { |
|
576 // With azure and a data surface perform an buffer unrotate |
|
577 // (SelfCopy). |
|
578 unsigned char* data; |
|
579 IntSize size; |
|
580 int32_t stride; |
|
581 SurfaceFormat format; |
|
582 |
|
583 if (mDTBuffer->LockBits(&data, &size, &stride, &format)) { |
|
584 uint8_t bytesPerPixel = BytesPerPixel(format); |
|
585 BufferUnrotate(data, |
|
586 size.width * bytesPerPixel, |
|
587 size.height, stride, |
|
588 newRotation.x * bytesPerPixel, newRotation.y); |
|
589 mDTBuffer->ReleaseBits(data); |
|
590 |
|
591 if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) { |
|
592 if (!EnsureBufferOnWhite()) { |
|
593 return result; |
|
594 } |
|
595 MOZ_ASSERT(mDTBufferOnWhite); |
|
596 mDTBufferOnWhite->LockBits(&data, &size, &stride, &format); |
|
597 uint8_t bytesPerPixel = BytesPerPixel(format); |
|
598 BufferUnrotate(data, |
|
599 size.width * bytesPerPixel, |
|
600 size.height, stride, |
|
601 newRotation.x * bytesPerPixel, newRotation.y); |
|
602 mDTBufferOnWhite->ReleaseBits(data); |
|
603 } |
|
604 |
|
605 // Buffer unrotate moves all the pixels, note that |
|
606 // we self copied for SyncBackToFrontBuffer |
|
607 result.mDidSelfCopy = true; |
|
608 mDidSelfCopy = true; |
|
609 mBufferRect = destBufferRect; |
|
610 mBufferRotation = nsIntPoint(0, 0); |
|
611 } |
|
612 |
|
613 if (!result.mDidSelfCopy) { |
|
614 destBufferRect = ComputeBufferRect(neededRegion.GetBounds()); |
|
615 CreateBuffer(result.mContentType, destBufferRect, bufferFlags, |
|
616 &destDTBuffer, &destDTBufferOnWhite); |
|
617 if (!destDTBuffer) { |
|
618 return result; |
|
619 } |
|
620 } |
|
621 } |
|
622 } else { |
|
623 mBufferRect = destBufferRect; |
|
624 mBufferRotation = newRotation; |
|
625 } |
|
626 } else { |
|
627 // No pixels are going to be kept. The whole visible region |
|
628 // will be redrawn, so we don't need to copy anything, so we don't |
|
629 // set destBuffer. |
|
630 mBufferRect = destBufferRect; |
|
631 mBufferRotation = nsIntPoint(0,0); |
|
632 } |
|
633 } else { |
|
634 // The buffer's not big enough, so allocate a new one |
|
635 CreateBuffer(result.mContentType, destBufferRect, bufferFlags, |
|
636 &destDTBuffer, &destDTBufferOnWhite); |
|
637 if (!destDTBuffer) { |
|
638 return result; |
|
639 } |
|
640 } |
|
641 |
|
642 NS_ASSERTION(!(aFlags & PAINT_WILL_RESAMPLE) || destBufferRect == neededRegion.GetBounds(), |
|
643 "If we're resampling, we need to validate the entire buffer"); |
|
644 |
|
645 // If we have no buffered data already, then destBuffer will be a fresh buffer |
|
646 // and we do not need to clear it below. |
|
647 bool isClear = !HaveBuffer(); |
|
648 |
|
649 if (destDTBuffer) { |
|
650 if (!isClear && (mode != SurfaceMode::SURFACE_COMPONENT_ALPHA || HaveBufferOnWhite())) { |
|
651 // Copy the bits |
|
652 nsIntPoint offset = -destBufferRect.TopLeft(); |
|
653 Matrix mat; |
|
654 mat.Translate(offset.x, offset.y); |
|
655 destDTBuffer->SetTransform(mat); |
|
656 if (!EnsureBuffer()) { |
|
657 return result; |
|
658 } |
|
659 MOZ_ASSERT(mDTBuffer, "Have we got a Thebes buffer for some reason?"); |
|
660 DrawBufferWithRotation(destDTBuffer, BUFFER_BLACK, 1.0, CompositionOp::OP_SOURCE); |
|
661 destDTBuffer->SetTransform(Matrix()); |
|
662 |
|
663 if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) { |
|
664 NS_ASSERTION(destDTBufferOnWhite, "Must have a white buffer!"); |
|
665 destDTBufferOnWhite->SetTransform(mat); |
|
666 if (!EnsureBufferOnWhite()) { |
|
667 return result; |
|
668 } |
|
669 MOZ_ASSERT(mDTBufferOnWhite, "Have we got a Thebes buffer for some reason?"); |
|
670 DrawBufferWithRotation(destDTBufferOnWhite, BUFFER_WHITE, 1.0, CompositionOp::OP_SOURCE); |
|
671 destDTBufferOnWhite->SetTransform(Matrix()); |
|
672 } |
|
673 } |
|
674 |
|
675 mDTBuffer = destDTBuffer.forget(); |
|
676 mDTBufferOnWhite = destDTBufferOnWhite.forget(); |
|
677 mBufferRect = destBufferRect; |
|
678 mBufferRotation = nsIntPoint(0,0); |
|
679 } |
|
680 NS_ASSERTION(canHaveRotation || mBufferRotation == nsIntPoint(0,0), |
|
681 "Rotation disabled, but we have nonzero rotation?"); |
|
682 |
|
683 nsIntRegion invalidate; |
|
684 invalidate.Sub(aLayer->GetValidRegion(), destBufferRect); |
|
685 result.mRegionToInvalidate.Or(result.mRegionToInvalidate, invalidate); |
|
686 result.mClip = DrawRegionClip::DRAW_SNAPPED; |
|
687 result.mMode = mode; |
|
688 |
|
689 return result; |
|
690 } |
|
691 |
|
692 DrawTarget* |
|
693 RotatedContentBuffer::BorrowDrawTargetForPainting(const PaintState& aPaintState, |
|
694 DrawIterator* aIter /* = nullptr */) |
|
695 { |
|
696 if (aPaintState.mMode == SurfaceMode::SURFACE_NONE) { |
|
697 return nullptr; |
|
698 } |
|
699 |
|
700 DrawTarget* result = BorrowDrawTargetForQuadrantUpdate(aPaintState.mRegionToDraw.GetBounds(), |
|
701 BUFFER_BOTH, aIter); |
|
702 if (!result) { |
|
703 return nullptr; |
|
704 } |
|
705 const nsIntRegion* drawPtr = &aPaintState.mRegionToDraw; |
|
706 if (aIter) { |
|
707 // The iterators draw region currently only contains the bounds of the region, |
|
708 // this makes it the precise region. |
|
709 aIter->mDrawRegion.And(aIter->mDrawRegion, aPaintState.mRegionToDraw); |
|
710 drawPtr = &aIter->mDrawRegion; |
|
711 } |
|
712 |
|
713 if (aPaintState.mMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) { |
|
714 MOZ_ASSERT(mDTBuffer && mDTBufferOnWhite); |
|
715 nsIntRegionRectIterator iter(*drawPtr); |
|
716 const nsIntRect *iterRect; |
|
717 while ((iterRect = iter.Next())) { |
|
718 mDTBuffer->FillRect(Rect(iterRect->x, iterRect->y, iterRect->width, iterRect->height), |
|
719 ColorPattern(Color(0.0, 0.0, 0.0, 1.0))); |
|
720 mDTBufferOnWhite->FillRect(Rect(iterRect->x, iterRect->y, iterRect->width, iterRect->height), |
|
721 ColorPattern(Color(1.0, 1.0, 1.0, 1.0))); |
|
722 } |
|
723 } else if (aPaintState.mContentType == gfxContentType::COLOR_ALPHA && HaveBuffer()) { |
|
724 // HaveBuffer() => we have an existing buffer that we must clear |
|
725 nsIntRegionRectIterator iter(*drawPtr); |
|
726 const nsIntRect *iterRect; |
|
727 while ((iterRect = iter.Next())) { |
|
728 result->ClearRect(Rect(iterRect->x, iterRect->y, iterRect->width, iterRect->height)); |
|
729 } |
|
730 } |
|
731 |
|
732 return result; |
|
733 } |
|
734 |
|
735 } |
|
736 } |
|
737 |