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
4 : *
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/
9 : *
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
13 : * License.
14 : *
15 : * The Original Code is Mozilla Corporation code.
16 : *
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.
20 : *
21 : * Contributor(s):
22 : * Robert O'Callahan <robert@ocallahan.org>
23 : *
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.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "ThebesLayerBuffer.h"
39 : #include "Layers.h"
40 : #include "gfxContext.h"
41 : #include "gfxPlatform.h"
42 : #include "gfxUtils.h"
43 : #include "nsDeviceContext.h"
44 :
45 : namespace mozilla {
46 : namespace layers {
47 :
48 : nsIntRect
49 0 : ThebesLayerBuffer::GetQuadrantRectangle(XSide aXSide, YSide aYSide)
50 : {
51 : // quadrantTranslation is the amount we translate the top-left
52 : // of the quadrant by to get coordinates relative to the layer
53 0 : nsIntPoint quadrantTranslation = -mBufferRotation;
54 0 : quadrantTranslation.x += aXSide == LEFT ? mBufferRect.width : 0;
55 0 : quadrantTranslation.y += aYSide == TOP ? mBufferRect.height : 0;
56 0 : return mBufferRect + quadrantTranslation;
57 : }
58 :
59 : /**
60 : * @param aXSide LEFT means we draw from the left side of the buffer (which
61 : * is drawn on the right side of mBufferRect). RIGHT means we draw from
62 : * the right side of the buffer (which is drawn on the left side of
63 : * mBufferRect).
64 : * @param aYSide TOP means we draw from the top side of the buffer (which
65 : * is drawn on the bottom side of mBufferRect). BOTTOM means we draw from
66 : * the bottom side of the buffer (which is drawn on the top side of
67 : * mBufferRect).
68 : */
69 : void
70 0 : ThebesLayerBuffer::DrawBufferQuadrant(gfxContext* aTarget,
71 : XSide aXSide, YSide aYSide,
72 : float aOpacity)
73 : {
74 : // The rectangle that we're going to fill. Basically we're going to
75 : // render the buffer at mBufferRect + quadrantTranslation to get the
76 : // pixels in the right place, but we're only going to paint within
77 : // mBufferRect
78 0 : nsIntRect quadrantRect = GetQuadrantRectangle(aXSide, aYSide);
79 0 : nsIntRect fillRect;
80 0 : if (!fillRect.IntersectRect(mBufferRect, quadrantRect))
81 0 : return;
82 :
83 0 : aTarget->NewPath();
84 : aTarget->Rectangle(gfxRect(fillRect.x, fillRect.y,
85 : fillRect.width, fillRect.height),
86 0 : true);
87 :
88 0 : gfxPoint quadrantTranslation(quadrantRect.x, quadrantRect.y);
89 0 : nsRefPtr<gfxPattern> pattern = new gfxPattern(mBuffer);
90 :
91 : #ifdef MOZ_GFX_OPTIMIZE_MOBILE
92 : gfxPattern::GraphicsFilter filter = gfxPattern::FILTER_NEAREST;
93 : pattern->SetFilter(filter);
94 : #endif
95 :
96 0 : gfxContextMatrixAutoSaveRestore saveMatrix(aTarget);
97 :
98 : // Transform from user -> buffer space.
99 0 : gfxMatrix transform;
100 0 : transform.Translate(-quadrantTranslation);
101 :
102 0 : pattern->SetMatrix(transform);
103 0 : aTarget->SetPattern(pattern);
104 :
105 0 : if (aOpacity != 1.0) {
106 0 : aTarget->Save();
107 0 : aTarget->Clip();
108 0 : aTarget->Paint(aOpacity);
109 0 : aTarget->Restore();
110 : } else {
111 0 : aTarget->Fill();
112 : }
113 : }
114 :
115 : void
116 0 : ThebesLayerBuffer::DrawBufferWithRotation(gfxContext* aTarget, float aOpacity)
117 : {
118 : // Draw four quadrants. We could use REPEAT_, but it's probably better
119 : // not to, to be performance-safe.
120 0 : DrawBufferQuadrant(aTarget, LEFT, TOP, aOpacity);
121 0 : DrawBufferQuadrant(aTarget, RIGHT, TOP, aOpacity);
122 0 : DrawBufferQuadrant(aTarget, LEFT, BOTTOM, aOpacity);
123 0 : DrawBufferQuadrant(aTarget, RIGHT, BOTTOM, aOpacity);
124 0 : }
125 :
126 : already_AddRefed<gfxContext>
127 0 : ThebesLayerBuffer::GetContextForQuadrantUpdate(const nsIntRect& aBounds)
128 : {
129 0 : nsRefPtr<gfxContext> ctx = new gfxContext(mBuffer);
130 :
131 : // Figure out which quadrant to draw in
132 0 : PRInt32 xBoundary = mBufferRect.XMost() - mBufferRotation.x;
133 0 : PRInt32 yBoundary = mBufferRect.YMost() - mBufferRotation.y;
134 0 : XSide sideX = aBounds.XMost() <= xBoundary ? RIGHT : LEFT;
135 0 : YSide sideY = aBounds.YMost() <= yBoundary ? BOTTOM : TOP;
136 0 : nsIntRect quadrantRect = GetQuadrantRectangle(sideX, sideY);
137 0 : NS_ASSERTION(quadrantRect.Contains(aBounds), "Messed up quadrants");
138 0 : ctx->Translate(-gfxPoint(quadrantRect.x, quadrantRect.y));
139 :
140 0 : return ctx.forget();
141 : }
142 :
143 : static void
144 0 : WrapRotationAxis(PRInt32* aRotationPoint, PRInt32 aSize)
145 : {
146 0 : if (*aRotationPoint < 0) {
147 0 : *aRotationPoint += aSize;
148 0 : } else if (*aRotationPoint >= aSize) {
149 0 : *aRotationPoint -= aSize;
150 : }
151 0 : }
152 :
153 : ThebesLayerBuffer::PaintState
154 0 : ThebesLayerBuffer::BeginPaint(ThebesLayer* aLayer, ContentType aContentType,
155 : PRUint32 aFlags)
156 : {
157 0 : PaintState result;
158 : // We need to disable rotation if we're going to be resampled when
159 : // drawing, because we might sample across the rotation boundary.
160 0 : bool canHaveRotation = !(aFlags & PAINT_WILL_RESAMPLE);
161 :
162 0 : nsIntRegion validRegion = aLayer->GetValidRegion();
163 :
164 : ContentType contentType;
165 0 : nsIntRegion neededRegion;
166 : bool canReuseBuffer;
167 0 : nsIntRect destBufferRect;
168 :
169 0 : while (true) {
170 0 : contentType = aContentType;
171 0 : neededRegion = aLayer->GetVisibleRegion();
172 0 : canReuseBuffer = mBuffer && BufferSizeOkFor(neededRegion.GetBounds().Size());
173 :
174 0 : if (canReuseBuffer) {
175 0 : if (mBufferRect.Contains(neededRegion.GetBounds())) {
176 : // We don't need to adjust mBufferRect.
177 0 : destBufferRect = mBufferRect;
178 0 : } else if (neededRegion.GetBounds().Size() <= mBufferRect.Size()) {
179 : // The buffer's big enough but doesn't contain everything that's
180 : // going to be visible. We'll move it.
181 0 : destBufferRect = nsIntRect(neededRegion.GetBounds().TopLeft(), mBufferRect.Size());
182 : } else {
183 0 : destBufferRect = neededRegion.GetBounds();
184 : }
185 : } else {
186 0 : destBufferRect = neededRegion.GetBounds();
187 : }
188 :
189 0 : if ((aFlags & PAINT_WILL_RESAMPLE) &&
190 0 : (!neededRegion.GetBounds().IsEqualInterior(destBufferRect) ||
191 0 : neededRegion.GetNumRects() > 1)) {
192 : // The area we add to neededRegion might not be painted opaquely
193 0 : contentType = gfxASurface::CONTENT_COLOR_ALPHA;
194 :
195 : // We need to validate the entire buffer, to make sure that only valid
196 : // pixels are sampled
197 0 : neededRegion = destBufferRect;
198 : }
199 :
200 0 : if (mBuffer && contentType != mBuffer->GetContentType()) {
201 : // We're effectively clearing the valid region, so we need to draw
202 : // the entire needed region now.
203 0 : result.mRegionToInvalidate = aLayer->GetValidRegion();
204 0 : validRegion.SetEmpty();
205 0 : Clear();
206 : // Restart decision process with the cleared buffer. We can only go
207 : // around the loop one more iteration, since mBuffer is null now.
208 0 : continue;
209 : }
210 :
211 : break;
212 : }
213 :
214 0 : NS_ASSERTION(destBufferRect.Contains(neededRegion.GetBounds()),
215 : "Destination rect doesn't contain what we need to paint");
216 :
217 0 : result.mRegionToDraw.Sub(neededRegion, validRegion);
218 0 : if (result.mRegionToDraw.IsEmpty())
219 : return result;
220 :
221 0 : nsIntRect drawBounds = result.mRegionToDraw.GetBounds();
222 0 : nsRefPtr<gfxASurface> destBuffer;
223 0 : PRUint32 bufferFlags = canHaveRotation ? ALLOW_REPEAT : 0;
224 0 : if (canReuseBuffer) {
225 0 : nsIntRect keepArea;
226 0 : if (keepArea.IntersectRect(destBufferRect, mBufferRect)) {
227 : // Set mBufferRotation so that the pixels currently in mBuffer
228 : // will still be rendered in the right place when mBufferRect
229 : // changes to destBufferRect.
230 : nsIntPoint newRotation = mBufferRotation +
231 0 : (destBufferRect.TopLeft() - mBufferRect.TopLeft());
232 0 : WrapRotationAxis(&newRotation.x, mBufferRect.width);
233 0 : WrapRotationAxis(&newRotation.y, mBufferRect.height);
234 0 : NS_ASSERTION(nsIntRect(nsIntPoint(0,0), mBufferRect.Size()).Contains(newRotation),
235 : "newRotation out of bounds");
236 0 : PRInt32 xBoundary = destBufferRect.XMost() - newRotation.x;
237 0 : PRInt32 yBoundary = destBufferRect.YMost() - newRotation.y;
238 0 : if ((drawBounds.x < xBoundary && xBoundary < drawBounds.XMost()) ||
239 0 : (drawBounds.y < yBoundary && yBoundary < drawBounds.YMost()) ||
240 0 : (newRotation != nsIntPoint(0,0) && !canHaveRotation)) {
241 : // The stuff we need to redraw will wrap around an edge of the
242 : // buffer, so move the pixels we can keep into a position that
243 : // lets us redraw in just one quadrant.
244 0 : if (mBufferRotation == nsIntPoint(0,0)) {
245 0 : nsIntRect srcRect(nsIntPoint(0, 0), mBufferRect.Size());
246 0 : nsIntPoint dest = mBufferRect.TopLeft() - destBufferRect.TopLeft();
247 0 : mBuffer->MovePixels(srcRect, dest);
248 0 : result.mDidSelfCopy = true;
249 : // Don't set destBuffer; we special-case self-copies, and
250 : // just did the necessary work above.
251 0 : mBufferRect = destBufferRect;
252 : } else {
253 : // We can't do a real self-copy because the buffer is rotated.
254 : // So allocate a new buffer for the destination.
255 0 : destBufferRect = neededRegion.GetBounds();
256 0 : destBuffer = CreateBuffer(contentType, destBufferRect.Size(), bufferFlags);
257 0 : if (!destBuffer)
258 : return result;
259 : }
260 : } else {
261 0 : mBufferRect = destBufferRect;
262 0 : mBufferRotation = newRotation;
263 : }
264 : } else {
265 : // No pixels are going to be kept. The whole visible region
266 : // will be redrawn, so we don't need to copy anything, so we don't
267 : // set destBuffer.
268 0 : mBufferRect = destBufferRect;
269 0 : mBufferRotation = nsIntPoint(0,0);
270 : }
271 : } else {
272 : // The buffer's not big enough, so allocate a new one
273 0 : destBuffer = CreateBuffer(contentType, destBufferRect.Size(), bufferFlags);
274 0 : if (!destBuffer)
275 : return result;
276 : }
277 0 : NS_ASSERTION(!(aFlags & PAINT_WILL_RESAMPLE) || destBufferRect == neededRegion.GetBounds(),
278 : "If we're resampling, we need to validate the entire buffer");
279 :
280 : // If we have no buffered data already, then destBuffer will be a fresh buffer
281 : // and we do not need to clear it below.
282 0 : bool isClear = mBuffer == nsnull;
283 :
284 0 : if (destBuffer) {
285 0 : if (mBuffer) {
286 : // Copy the bits
287 0 : nsRefPtr<gfxContext> tmpCtx = new gfxContext(destBuffer);
288 0 : nsIntPoint offset = -destBufferRect.TopLeft();
289 0 : tmpCtx->SetOperator(gfxContext::OPERATOR_SOURCE);
290 0 : tmpCtx->Translate(gfxPoint(offset.x, offset.y));
291 0 : DrawBufferWithRotation(tmpCtx, 1.0);
292 : }
293 :
294 0 : mBuffer = destBuffer.forget();
295 0 : mBufferRect = destBufferRect;
296 0 : mBufferRotation = nsIntPoint(0,0);
297 : }
298 0 : NS_ASSERTION(canHaveRotation || mBufferRotation == nsIntPoint(0,0),
299 : "Rotation disabled, but we have nonzero rotation?");
300 :
301 0 : nsIntRegion invalidate;
302 0 : invalidate.Sub(aLayer->GetValidRegion(), destBufferRect);
303 0 : result.mRegionToInvalidate.Or(result.mRegionToInvalidate, invalidate);
304 :
305 0 : result.mContext = GetContextForQuadrantUpdate(drawBounds);
306 :
307 0 : gfxUtils::ClipToRegionSnapped(result.mContext, result.mRegionToDraw);
308 0 : if (contentType == gfxASurface::CONTENT_COLOR_ALPHA && !isClear) {
309 0 : result.mContext->SetOperator(gfxContext::OPERATOR_CLEAR);
310 0 : result.mContext->Paint();
311 0 : result.mContext->SetOperator(gfxContext::OPERATOR_OVER);
312 : }
313 : return result;
314 : }
315 :
316 : }
317 : }
318 :
|