1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Mozilla Corporation code.
17 * The Initial Developer of the Original Code is Mozilla Foundation.
18 * Portions created by the Initial Developer are Copyright (C) 2010
19 * the Initial Developer. All Rights Reserved.
22 * Robert O'Callahan <robert@ocallahan.org>
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 #include "ThebesLayerBuffer.h"
40 #include "gfxContext.h"
41 #include "gfxPlatform.h"
48 ScaledSize(const nsIntSize
& aSize
, float aXScale
, float aYScale
)
50 if (aXScale
== 1.0 && aYScale
== 1.0) {
54 gfxRect
rect(0, 0, aSize
.width
, aSize
.height
);
55 rect
.Scale(aXScale
, aYScale
);
57 return nsIntSize(rect
.size
.width
, rect
.size
.height
);
61 ThebesLayerBuffer::GetQuadrantRectangle(XSide aXSide
, YSide aYSide
)
63 // quadrantTranslation is the amount we translate the top-left
64 // of the quadrant by to get coordinates relative to the layer
65 nsIntPoint quadrantTranslation
= -mBufferRotation
;
66 quadrantTranslation
.x
+= aXSide
== LEFT
? mBufferRect
.width
: 0;
67 quadrantTranslation
.y
+= aYSide
== TOP
? mBufferRect
.height
: 0;
68 return mBufferRect
+ quadrantTranslation
;
72 * @param aXSide LEFT means we draw from the left side of the buffer (which
73 * is drawn on the right side of mBufferRect). RIGHT means we draw from
74 * the right side of the buffer (which is drawn on the left side of
76 * @param aYSide TOP means we draw from the top side of the buffer (which
77 * is drawn on the bottom side of mBufferRect). BOTTOM means we draw from
78 * the bottom side of the buffer (which is drawn on the top side of
82 ThebesLayerBuffer::DrawBufferQuadrant(gfxContext
* aTarget
,
83 XSide aXSide
, YSide aYSide
,
85 float aXRes
, float aYRes
)
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
91 nsIntRect quadrantRect
= GetQuadrantRectangle(aXSide
, aYSide
);
93 if (!fillRect
.IntersectRect(mBufferRect
, quadrantRect
))
97 aTarget
->Rectangle(gfxRect(fillRect
.x
, fillRect
.y
,
98 fillRect
.width
, fillRect
.height
),
101 gfxPoint
quadrantTranslation(quadrantRect
.x
, quadrantRect
.y
);
102 nsRefPtr
<gfxPattern
> pattern
= new gfxPattern(mBuffer
);
104 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
105 gfxPattern::GraphicsFilter filter
= gfxPattern::FILTER_NEAREST
;
106 pattern
->SetFilter(filter
);
109 gfxContextMatrixAutoSaveRestore
saveMatrix(aTarget
);
111 // Transform from user -> buffer space.
113 transform
.Scale(aXRes
, aYRes
);
114 transform
.Translate(-quadrantTranslation
);
116 // in common cases the matrix after scaling by 1/aRes is close to 1.0,
117 // so we want to make it 1.0 in both cases
118 transform
.Scale(1.0 / aXRes
, 1.0 / aYRes
);
119 transform
.NudgeToIntegers();
121 gfxMatrix ctxMatrix
= aTarget
->CurrentMatrix();
122 ctxMatrix
.Scale(1.0 / aXRes
, 1.0 / aYRes
);
123 ctxMatrix
.NudgeToIntegers();
124 aTarget
->SetMatrix(ctxMatrix
);
126 pattern
->SetMatrix(transform
);
127 aTarget
->SetPattern(pattern
);
129 if (aOpacity
!= 1.0) {
132 aTarget
->Paint(aOpacity
);
140 ThebesLayerBuffer::DrawBufferWithRotation(gfxContext
* aTarget
, float aOpacity
,
141 float aXRes
, float aYRes
)
143 // Draw four quadrants. We could use REPEAT_, but it's probably better
144 // not to, to be performance-safe.
145 DrawBufferQuadrant(aTarget
, LEFT
, TOP
, aOpacity
, aXRes
, aYRes
);
146 DrawBufferQuadrant(aTarget
, RIGHT
, TOP
, aOpacity
, aXRes
, aYRes
);
147 DrawBufferQuadrant(aTarget
, LEFT
, BOTTOM
, aOpacity
, aXRes
, aYRes
);
148 DrawBufferQuadrant(aTarget
, RIGHT
, BOTTOM
, aOpacity
, aXRes
, aYRes
);
151 already_AddRefed
<gfxContext
>
152 ThebesLayerBuffer::GetContextForQuadrantUpdate(const nsIntRect
& aBounds
,
156 nsRefPtr
<gfxContext
> ctx
= new gfxContext(mBuffer
);
158 // Figure out which quadrant to draw in
159 PRInt32 xBoundary
= mBufferRect
.XMost() - mBufferRotation
.x
;
160 PRInt32 yBoundary
= mBufferRect
.YMost() - mBufferRotation
.y
;
161 XSide sideX
= aBounds
.XMost() <= xBoundary
? RIGHT
: LEFT
;
162 YSide sideY
= aBounds
.YMost() <= yBoundary
? BOTTOM
: TOP
;
163 nsIntRect quadrantRect
= GetQuadrantRectangle(sideX
, sideY
);
164 NS_ASSERTION(quadrantRect
.Contains(aBounds
), "Messed up quadrants");
165 ctx
->Scale(aXResolution
, aYResolution
);
166 ctx
->Translate(-gfxPoint(quadrantRect
.x
, quadrantRect
.y
));
172 WrapRotationAxis(PRInt32
* aRotationPoint
, PRInt32 aSize
)
174 if (*aRotationPoint
< 0) {
175 *aRotationPoint
+= aSize
;
176 } else if (*aRotationPoint
>= aSize
) {
177 *aRotationPoint
-= aSize
;
181 ThebesLayerBuffer::PaintState
182 ThebesLayerBuffer::BeginPaint(ThebesLayer
* aLayer
, ContentType aContentType
,
183 float aXResolution
, float aYResolution
)
187 result
.mRegionToDraw
.Sub(aLayer
->GetVisibleRegion(), aLayer
->GetValidRegion());
189 float curXRes
= aLayer
->GetXResolution();
190 float curYRes
= aLayer
->GetYResolution();
192 (aContentType
!= mBuffer
->GetContentType() ||
193 aXResolution
!= curXRes
|| aYResolution
!= curYRes
)) {
194 // We're effectively clearing the valid region, so we need to draw
195 // the entire visible region now.
197 // XXX/cjones: a possibly worthwhile optimization to keep in mind
198 // is to re-use buffers when the resolution and visible region
199 // have changed in such a way that the buffer size stays the same.
200 // It might make even more sense to allocate buffers from a
201 // recyclable pool, so that we could keep this logic simple and
202 // still get back the same buffer.
203 result
.mRegionToDraw
= aLayer
->GetVisibleRegion();
204 result
.mRegionToInvalidate
= aLayer
->GetValidRegion();
208 if (result
.mRegionToDraw
.IsEmpty())
210 nsIntRect drawBounds
= result
.mRegionToDraw
.GetBounds();
212 nsIntRect visibleBounds
= aLayer
->GetVisibleRegion().GetBounds();
213 nsIntSize destBufferDims
= ScaledSize(visibleBounds
.Size(),
214 aXResolution
, aYResolution
);
215 nsRefPtr
<gfxASurface
> destBuffer
;
216 nsIntRect destBufferRect
;
217 PRBool bufferDimsChanged
= PR_FALSE
;
219 if (BufferSizeOkFor(destBufferDims
)) {
220 NS_ASSERTION(curXRes
== aXResolution
&& curYRes
== aYResolution
,
221 "resolution changes must Clear()!");
223 // The current buffer is big enough to hold the visible area.
224 if (mBufferRect
.Contains(visibleBounds
)) {
225 // We don't need to adjust mBufferRect.
226 destBufferRect
= mBufferRect
;
228 // The buffer's big enough but doesn't contain everything that's
229 // going to be visible. We'll move it.
230 destBufferRect
= nsIntRect(visibleBounds
.TopLeft(), mBufferRect
.Size());
233 if (keepArea
.IntersectRect(destBufferRect
, mBufferRect
)) {
234 // Set mBufferRotation so that the pixels currently in mBuffer
235 // will still be rendered in the right place when mBufferRect
236 // changes to destBufferRect.
237 nsIntPoint newRotation
= mBufferRotation
+
238 (destBufferRect
.TopLeft() - mBufferRect
.TopLeft());
239 WrapRotationAxis(&newRotation
.x
, mBufferRect
.width
);
240 WrapRotationAxis(&newRotation
.y
, mBufferRect
.height
);
241 NS_ASSERTION(nsIntRect(nsIntPoint(0,0), mBufferRect
.Size()).Contains(newRotation
),
242 "newRotation out of bounds");
243 PRInt32 xBoundary
= destBufferRect
.XMost() - newRotation
.x
;
244 PRInt32 yBoundary
= destBufferRect
.YMost() - newRotation
.y
;
245 if ((drawBounds
.x
< xBoundary
&& xBoundary
< drawBounds
.XMost()) ||
246 (drawBounds
.y
< yBoundary
&& yBoundary
< drawBounds
.YMost())) {
247 // The stuff we need to redraw will wrap around an edge of the
248 // buffer, so we will need to do a self-copy
249 if (mBuffer
->SupportsSelfCopy() && mBufferRotation
== nsIntPoint(0,0)) {
250 destBuffer
= mBuffer
;
252 // We can't do a real self-copy because the buffer is rotated.
253 // So allocate a new buffer for the destination.
254 destBufferRect
= visibleBounds
;
255 destBufferDims
= ScaledSize(destBufferRect
.Size(),
256 aXResolution
, aYResolution
);
257 bufferDimsChanged
= PR_TRUE
;
258 destBuffer
= CreateBuffer(aContentType
, destBufferDims
);
263 mBufferRect
= destBufferRect
;
264 mBufferRotation
= newRotation
;
267 // No pixels are going to be kept. The whole visible region
268 // will be redrawn, so we don't need to copy anything, so we don't
270 mBufferRect
= destBufferRect
;
271 mBufferRotation
= nsIntPoint(0,0);
274 // The buffer's not big enough, so allocate a new one
275 destBufferRect
= visibleBounds
;
276 destBufferDims
= ScaledSize(destBufferRect
.Size(),
277 aXResolution
, aYResolution
);
278 bufferDimsChanged
= PR_TRUE
;
279 destBuffer
= CreateBuffer(aContentType
, destBufferDims
);
284 // If we have no buffered data already, then destBuffer will be a fresh buffer
285 // and we do not need to clear it below.
286 PRBool isClear
= mBuffer
== nsnull
;
291 nsRefPtr
<gfxContext
> tmpCtx
= new gfxContext(destBuffer
);
292 nsIntPoint offset
= -destBufferRect
.TopLeft();
293 tmpCtx
->SetOperator(gfxContext::OPERATOR_SOURCE
);
294 tmpCtx
->Scale(aXResolution
, aYResolution
);
295 tmpCtx
->Translate(gfxPoint(offset
.x
, offset
.y
));
296 NS_ASSERTION(curXRes
== aXResolution
&& curYRes
== aYResolution
,
297 "resolution changes must Clear()!");
298 DrawBufferWithRotation(tmpCtx
, 1.0, aXResolution
, aYResolution
);
301 mBuffer
= destBuffer
.forget();
302 mBufferRect
= destBufferRect
;
303 mBufferRotation
= nsIntPoint(0,0);
305 if (bufferDimsChanged
) {
306 mBufferDims
= destBufferDims
;
309 nsIntRegion invalidate
;
310 invalidate
.Sub(aLayer
->GetValidRegion(), destBufferRect
);
311 result
.mRegionToInvalidate
.Or(result
.mRegionToInvalidate
, invalidate
);
313 result
.mContext
= GetContextForQuadrantUpdate(drawBounds
,
314 aXResolution
, aYResolution
);
316 gfxUtils::ClipToRegionSnapped(result
.mContext
, result
.mRegionToDraw
);
317 if (aContentType
== gfxASurface::CONTENT_COLOR_ALPHA
&& !isClear
) {
318 result
.mContext
->SetOperator(gfxContext::OPERATOR_CLEAR
);
319 result
.mContext
->Paint();
320 result
.mContext
->SetOperator(gfxContext::OPERATOR_OVER
);