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) 2009
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Bas Schouten <bschouten@mozilla.org>
23 : * Vladimir Vukicevic <vladimir@pobox.com>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "mozilla/layers/PLayers.h"
40 :
41 : /* This must occur *after* layers/PLayers.h to avoid typedefs conflicts. */
42 : #include "mozilla/Util.h"
43 :
44 : #include "mozilla/layers/ShadowLayers.h"
45 :
46 : #include "ThebesLayerBuffer.h"
47 : #include "ThebesLayerOGL.h"
48 : #include "gfxUtils.h"
49 : #include "gfxTeeSurface.h"
50 :
51 : namespace mozilla {
52 : namespace layers {
53 :
54 : using gl::GLContext;
55 : using gl::TextureImage;
56 :
57 : static const int ALLOW_REPEAT = ThebesLayerBuffer::ALLOW_REPEAT;
58 :
59 : // BindAndDrawQuadWithTextureRect can work with either GL_REPEAT (preferred)
60 : // or GL_CLAMP_TO_EDGE textures. If ALLOW_REPEAT is set in aFlags, we
61 : // select based on whether REPEAT is valid for non-power-of-two textures --
62 : // if we have NPOT support we use it, otherwise we stick with CLAMP_TO_EDGE and
63 : // decompose.
64 : // If ALLOW_REPEAT is not set, we always use GL_CLAMP_TO_EDGE.
65 : static already_AddRefed<TextureImage>
66 0 : CreateClampOrRepeatTextureImage(GLContext *aGl,
67 : const nsIntSize& aSize,
68 : TextureImage::ContentType aContentType,
69 : PRUint32 aFlags)
70 : {
71 0 : GLenum wrapMode = LOCAL_GL_CLAMP_TO_EDGE;
72 0 : if ((aFlags & ALLOW_REPEAT) &&
73 0 : (aGl->IsExtensionSupported(GLContext::ARB_texture_non_power_of_two) ||
74 0 : aGl->IsExtensionSupported(GLContext::OES_texture_npot)))
75 : {
76 0 : wrapMode = LOCAL_GL_REPEAT;
77 : }
78 :
79 0 : return aGl->CreateTextureImage(aSize, aContentType, wrapMode);
80 : }
81 :
82 : static void
83 0 : SetAntialiasingFlags(Layer* aLayer, gfxContext* aTarget)
84 : {
85 0 : nsRefPtr<gfxASurface> surface = aTarget->CurrentSurface();
86 0 : if (surface->GetContentType() != gfxASurface::CONTENT_COLOR_ALPHA) {
87 : // Destination doesn't have alpha channel; no need to set any special flags
88 : return;
89 : }
90 :
91 : surface->SetSubpixelAntialiasingEnabled(
92 0 : !(aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA));
93 : }
94 :
95 : class ThebesLayerBufferOGL
96 : {
97 0 : NS_INLINE_DECL_REFCOUNTING(ThebesLayerBufferOGL)
98 : public:
99 : typedef TextureImage::ContentType ContentType;
100 : typedef ThebesLayerBuffer::PaintState PaintState;
101 :
102 0 : ThebesLayerBufferOGL(ThebesLayer* aLayer, LayerOGL* aOGLLayer)
103 : : mLayer(aLayer)
104 0 : , mOGLLayer(aOGLLayer)
105 0 : {}
106 0 : virtual ~ThebesLayerBufferOGL() {}
107 :
108 : enum { PAINT_WILL_RESAMPLE = ThebesLayerBuffer::PAINT_WILL_RESAMPLE };
109 : virtual PaintState BeginPaint(ContentType aContentType,
110 : PRUint32 aFlags) = 0;
111 :
112 : void RenderTo(const nsIntPoint& aOffset, LayerManagerOGL* aManager,
113 : PRUint32 aFlags);
114 :
115 0 : nsIntSize GetSize() {
116 0 : if (mTexImage)
117 0 : return mTexImage->GetSize();
118 0 : return nsIntSize(0, 0);
119 : }
120 :
121 : protected:
122 : virtual nsIntPoint GetOriginOffset() = 0;
123 :
124 0 : GLContext* gl() const { return mOGLLayer->gl(); }
125 :
126 : ThebesLayer* mLayer;
127 : LayerOGL* mOGLLayer;
128 : nsRefPtr<TextureImage> mTexImage;
129 : nsRefPtr<TextureImage> mTexImageOnWhite;
130 : };
131 :
132 : void
133 0 : ThebesLayerBufferOGL::RenderTo(const nsIntPoint& aOffset,
134 : LayerManagerOGL* aManager,
135 : PRUint32 aFlags)
136 : {
137 0 : if (!mTexImage)
138 0 : return;
139 :
140 0 : if (mTexImage->InUpdate()) {
141 0 : mTexImage->EndUpdate();
142 : }
143 :
144 0 : if (mTexImageOnWhite && mTexImageOnWhite->InUpdate()) {
145 0 : mTexImageOnWhite->EndUpdate();
146 : }
147 :
148 : #ifdef MOZ_DUMP_PAINTING
149 0 : if (gfxUtils::sDumpPainting) {
150 : nsRefPtr<gfxImageSurface> surf =
151 0 : gl()->GetTexImage(mTexImage->GetTextureID(), false, mTexImage->GetShaderProgramType());
152 :
153 0 : WriteSnapshotToDumpFile(mLayer, surf);
154 : }
155 : #endif
156 :
157 0 : PRInt32 passes = mTexImageOnWhite ? 2 : 1;
158 0 : for (PRInt32 pass = 1; pass <= passes; ++pass) {
159 : LayerProgram *program;
160 :
161 0 : if (passes == 2) {
162 : ComponentAlphaTextureLayerProgram *alphaProgram;
163 0 : if (pass == 1) {
164 0 : alphaProgram = aManager->GetComponentAlphaPass1LayerProgram();
165 : gl()->fBlendFuncSeparate(LOCAL_GL_ZERO, LOCAL_GL_ONE_MINUS_SRC_COLOR,
166 0 : LOCAL_GL_ONE, LOCAL_GL_ONE);
167 : } else {
168 0 : alphaProgram = aManager->GetComponentAlphaPass2LayerProgram();
169 : gl()->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE,
170 0 : LOCAL_GL_ONE, LOCAL_GL_ONE);
171 : }
172 :
173 0 : alphaProgram->Activate();
174 0 : alphaProgram->SetBlackTextureUnit(0);
175 0 : alphaProgram->SetWhiteTextureUnit(1);
176 0 : program = alphaProgram;
177 : } else {
178 : // Note BGR: Cairo's image surfaces are always in what
179 : // OpenGL and our shaders consider BGR format.
180 : ColorTextureLayerProgram *basicProgram =
181 0 : aManager->GetColorTextureLayerProgram(mTexImage->GetShaderProgramType());
182 :
183 0 : basicProgram->Activate();
184 0 : basicProgram->SetTextureUnit(0);
185 0 : program = basicProgram;
186 : }
187 :
188 0 : program->SetLayerOpacity(mLayer->GetEffectiveOpacity());
189 0 : program->SetLayerTransform(mLayer->GetEffectiveTransform());
190 0 : program->SetRenderOffset(aOffset);
191 :
192 0 : const nsIntRegion& visibleRegion = mLayer->GetEffectiveVisibleRegion();
193 0 : nsIntRegion tmpRegion;
194 : const nsIntRegion* renderRegion;
195 0 : if (aFlags & PAINT_WILL_RESAMPLE) {
196 : // If we're resampling, then the texture image will contain exactly the
197 : // entire visible region's bounds, and we should draw it all in one quad
198 : // to avoid unexpected aliasing.
199 0 : tmpRegion = visibleRegion.GetBounds();
200 0 : renderRegion = &tmpRegion;
201 : } else {
202 0 : renderRegion = &visibleRegion;
203 : }
204 :
205 0 : nsIntRegion region(*renderRegion);
206 0 : nsIntPoint origin = GetOriginOffset();
207 0 : region.MoveBy(-origin); // translate into TexImage space, buffer origin might not be at texture (0,0)
208 :
209 : // Figure out the intersecting draw region
210 0 : nsIntSize texSize = mTexImage->GetSize();
211 0 : nsIntRect textureRect = nsIntRect(0, 0, texSize.width, texSize.height);
212 0 : textureRect.MoveBy(region.GetBounds().TopLeft());
213 0 : nsIntRegion subregion;
214 0 : subregion.And(region, textureRect);
215 0 : if (subregion.IsEmpty()) // Region is empty, nothing to draw
216 : return;
217 :
218 0 : nsIntRegion screenRects;
219 0 : nsIntRegion regionRects;
220 :
221 : // Collect texture/screen coordinates for drawing
222 0 : nsIntRegionRectIterator iter(subregion);
223 0 : while (const nsIntRect* iterRect = iter.Next()) {
224 0 : nsIntRect regionRect = *iterRect;
225 0 : nsIntRect screenRect = regionRect;
226 0 : screenRect.MoveBy(origin);
227 :
228 0 : screenRects.Or(screenRects, screenRect);
229 0 : regionRects.Or(regionRects, regionRect);
230 : }
231 :
232 0 : mTexImage->BeginTileIteration();
233 0 : if (mTexImageOnWhite) {
234 0 : NS_ASSERTION(mTexImage->GetTileCount() == mTexImageOnWhite->GetTileCount(),
235 : "Tile count mismatch on component alpha texture");
236 0 : mTexImageOnWhite->BeginTileIteration();
237 : }
238 :
239 0 : bool usingTiles = (mTexImage->GetTileCount() > 1);
240 0 : do {
241 0 : if (mTexImageOnWhite) {
242 0 : NS_ASSERTION(mTexImageOnWhite->GetTileRect() == mTexImage->GetTileRect(), "component alpha textures should be the same size.");
243 : }
244 :
245 0 : nsIntRect tileRect = mTexImage->GetTileRect();
246 :
247 : // Bind textures.
248 0 : TextureImage::ScopedBindTexture texBind(mTexImage, LOCAL_GL_TEXTURE0);
249 0 : TextureImage::ScopedBindTexture texOnWhiteBind(mTexImageOnWhite, LOCAL_GL_TEXTURE1);
250 :
251 : // Draw texture. If we're using tiles, we do repeating manually, as texture
252 : // repeat would cause each individual tile to repeat instead of the
253 : // compound texture as a whole. This involves drawing at most 4 sections,
254 : // 2 for each axis that has texture repeat.
255 0 : for (int y = 0; y < (usingTiles ? 2 : 1); y++) {
256 0 : for (int x = 0; x < (usingTiles ? 2 : 1); x++) {
257 0 : nsIntRect currentTileRect(tileRect);
258 0 : currentTileRect.MoveBy(x * texSize.width, y * texSize.height);
259 :
260 0 : nsIntRegionRectIterator screenIter(screenRects);
261 0 : nsIntRegionRectIterator regionIter(regionRects);
262 :
263 : const nsIntRect* screenRect;
264 : const nsIntRect* regionRect;
265 0 : while ((screenRect = screenIter.Next()) &&
266 : (regionRect = regionIter.Next())) {
267 0 : nsIntRect tileScreenRect(*screenRect);
268 0 : nsIntRect tileRegionRect(*regionRect);
269 :
270 : // When we're using tiles, find the intersection between the tile
271 : // rect and this region rect. Tiling is then handled by the
272 : // outer for-loops and modifying the tile rect.
273 0 : if (usingTiles) {
274 0 : tileScreenRect.MoveBy(-origin);
275 0 : tileScreenRect = tileScreenRect.Intersect(currentTileRect);
276 0 : tileScreenRect.MoveBy(origin);
277 :
278 0 : if (tileScreenRect.IsEmpty())
279 0 : continue;
280 :
281 0 : tileRegionRect = regionRect->Intersect(currentTileRect);
282 0 : tileRegionRect.MoveBy(-currentTileRect.TopLeft());
283 : }
284 :
285 : #ifdef ANDROID
286 : // Bug 691354
287 : // Using the LINEAR filter we get unexplained artifacts.
288 : // Use NEAREST when no scaling is required.
289 : gfxMatrix matrix;
290 : bool is2D = mLayer->GetEffectiveTransform().Is2D(&matrix);
291 : if (is2D && !matrix.HasNonTranslationOrFlip()) {
292 : gl()->ApplyFilterToBoundTexture(gfxPattern::FILTER_NEAREST);
293 : } else {
294 : mTexImage->ApplyFilter();
295 : }
296 : #endif
297 0 : program->SetLayerQuadRect(tileScreenRect);
298 : aManager->BindAndDrawQuadWithTextureRect(program, tileRegionRect,
299 : tileRect.Size(),
300 0 : mTexImage->GetWrapMode());
301 : }
302 : }
303 : }
304 :
305 0 : if (mTexImageOnWhite)
306 0 : mTexImageOnWhite->NextTile();
307 0 : } while (mTexImage->NextTile());
308 : }
309 :
310 0 : if (mTexImageOnWhite) {
311 : // Restore defaults
312 : gl()->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
313 0 : LOCAL_GL_ONE, LOCAL_GL_ONE);
314 : }
315 : }
316 :
317 :
318 : // This implementation is the fast-path for when our TextureImage is
319 : // permanently backed with a server-side ASurface. We can simply
320 : // reuse the ThebesLayerBuffer logic in its entirety and profit.
321 : class SurfaceBufferOGL : public ThebesLayerBufferOGL, private ThebesLayerBuffer
322 : {
323 : public:
324 : typedef ThebesLayerBufferOGL::ContentType ContentType;
325 : typedef ThebesLayerBufferOGL::PaintState PaintState;
326 :
327 0 : SurfaceBufferOGL(ThebesLayerOGL* aLayer)
328 : : ThebesLayerBufferOGL(aLayer, aLayer)
329 0 : , ThebesLayerBuffer(SizedToVisibleBounds)
330 : {
331 0 : }
332 0 : virtual ~SurfaceBufferOGL() {}
333 :
334 : // ThebesLayerBufferOGL interface
335 0 : virtual PaintState BeginPaint(ContentType aContentType,
336 : PRUint32 aFlags)
337 : {
338 : // Let ThebesLayerBuffer do all the hard work for us! :D
339 : return ThebesLayerBuffer::BeginPaint(mLayer,
340 : aContentType,
341 0 : aFlags);
342 : }
343 :
344 : // ThebesLayerBuffer interface
345 : virtual already_AddRefed<gfxASurface>
346 0 : CreateBuffer(ContentType aType, const nsIntSize& aSize, PRUint32 aFlags)
347 : {
348 0 : NS_ASSERTION(gfxASurface::CONTENT_ALPHA != aType,"ThebesBuffer has color");
349 :
350 0 : mTexImage = CreateClampOrRepeatTextureImage(gl(), aSize, aType, aFlags);
351 0 : return mTexImage ? mTexImage->GetBackingSurface() : nsnull;
352 : }
353 :
354 : protected:
355 0 : virtual nsIntPoint GetOriginOffset() {
356 0 : return BufferRect().TopLeft() - BufferRotation();
357 : }
358 : };
359 :
360 :
361 : // This implementation is (currently) the slow-path for when we can't
362 : // implement pixel retaining using thebes. This implementation and
363 : // the above could be unified by abstracting buffer-copy operations
364 : // and implementing them here using GL hacketry.
365 : class BasicBufferOGL : public ThebesLayerBufferOGL
366 : {
367 : public:
368 0 : BasicBufferOGL(ThebesLayerOGL* aLayer)
369 : : ThebesLayerBufferOGL(aLayer, aLayer)
370 : , mBufferRect(0,0,0,0)
371 0 : , mBufferRotation(0,0)
372 0 : {}
373 0 : virtual ~BasicBufferOGL() {}
374 :
375 : virtual PaintState BeginPaint(ContentType aContentType,
376 : PRUint32 aFlags);
377 :
378 : protected:
379 : enum XSide {
380 : LEFT, RIGHT
381 : };
382 : enum YSide {
383 : TOP, BOTTOM
384 : };
385 : nsIntRect GetQuadrantRectangle(XSide aXSide, YSide aYSide);
386 :
387 0 : virtual nsIntPoint GetOriginOffset() {
388 0 : return mBufferRect.TopLeft() - mBufferRotation;
389 : }
390 :
391 : private:
392 : nsIntRect mBufferRect;
393 : nsIntPoint mBufferRotation;
394 : };
395 :
396 : static void
397 0 : WrapRotationAxis(PRInt32* aRotationPoint, PRInt32 aSize)
398 : {
399 0 : if (*aRotationPoint < 0) {
400 0 : *aRotationPoint += aSize;
401 0 : } else if (*aRotationPoint >= aSize) {
402 0 : *aRotationPoint -= aSize;
403 : }
404 0 : }
405 :
406 : nsIntRect
407 0 : BasicBufferOGL::GetQuadrantRectangle(XSide aXSide, YSide aYSide)
408 : {
409 : // quadrantTranslation is the amount we translate the top-left
410 : // of the quadrant by to get coordinates relative to the layer
411 0 : nsIntPoint quadrantTranslation = -mBufferRotation;
412 0 : quadrantTranslation.x += aXSide == LEFT ? mBufferRect.width : 0;
413 0 : quadrantTranslation.y += aYSide == TOP ? mBufferRect.height : 0;
414 0 : return mBufferRect + quadrantTranslation;
415 : }
416 :
417 : static void
418 0 : FillSurface(gfxASurface* aSurface, const nsIntRegion& aRegion,
419 : const nsIntPoint& aOffset, const gfxRGBA& aColor)
420 : {
421 0 : nsRefPtr<gfxContext> ctx = new gfxContext(aSurface);
422 0 : ctx->Translate(-gfxPoint(aOffset.x, aOffset.y));
423 0 : gfxUtils::ClipToRegion(ctx, aRegion);
424 0 : ctx->SetColor(aColor);
425 0 : ctx->Paint();
426 0 : }
427 :
428 : BasicBufferOGL::PaintState
429 0 : BasicBufferOGL::BeginPaint(ContentType aContentType,
430 : PRUint32 aFlags)
431 : {
432 0 : PaintState result;
433 : // We need to disable rotation if we're going to be resampled when
434 : // drawing, because we might sample across the rotation boundary.
435 0 : bool canHaveRotation = !(aFlags & PAINT_WILL_RESAMPLE);
436 :
437 0 : nsIntRegion validRegion = mLayer->GetValidRegion();
438 :
439 : Layer::SurfaceMode mode;
440 : ContentType contentType;
441 0 : nsIntRegion neededRegion;
442 : bool canReuseBuffer;
443 0 : nsIntRect destBufferRect;
444 :
445 0 : while (true) {
446 0 : mode = mLayer->GetSurfaceMode();
447 0 : contentType = aContentType;
448 0 : neededRegion = mLayer->GetVisibleRegion();
449 : // If we're going to resample, we need a buffer that's in clamp mode.
450 0 : canReuseBuffer = neededRegion.GetBounds().Size() <= mBufferRect.Size() &&
451 0 : mTexImage &&
452 0 : (!(aFlags & PAINT_WILL_RESAMPLE) ||
453 0 : mTexImage->GetWrapMode() == LOCAL_GL_CLAMP_TO_EDGE);
454 :
455 0 : if (canReuseBuffer) {
456 0 : if (mBufferRect.Contains(neededRegion.GetBounds())) {
457 : // We don't need to adjust mBufferRect.
458 0 : destBufferRect = mBufferRect;
459 : } else {
460 : // The buffer's big enough but doesn't contain everything that's
461 : // going to be visible. We'll move it.
462 0 : destBufferRect = nsIntRect(neededRegion.GetBounds().TopLeft(), mBufferRect.Size());
463 : }
464 : } else {
465 0 : destBufferRect = neededRegion.GetBounds();
466 : }
467 :
468 0 : if (mode == Layer::SURFACE_COMPONENT_ALPHA) {
469 : #ifdef MOZ_GFX_OPTIMIZE_MOBILE
470 : mode = Layer::SURFACE_SINGLE_CHANNEL_ALPHA;
471 : #else
472 0 : if (!mLayer->GetParent() || !mLayer->GetParent()->SupportsComponentAlphaChildren()) {
473 0 : mode = Layer::SURFACE_SINGLE_CHANNEL_ALPHA;
474 : } else {
475 0 : contentType = gfxASurface::CONTENT_COLOR;
476 : }
477 : #endif
478 : }
479 :
480 0 : if ((aFlags & PAINT_WILL_RESAMPLE) &&
481 0 : (!neededRegion.GetBounds().IsEqualInterior(destBufferRect) ||
482 0 : neededRegion.GetNumRects() > 1)) {
483 : // The area we add to neededRegion might not be painted opaquely
484 0 : if (mode == Layer::SURFACE_OPAQUE) {
485 0 : contentType = gfxASurface::CONTENT_COLOR_ALPHA;
486 0 : mode = Layer::SURFACE_SINGLE_CHANNEL_ALPHA;
487 : }
488 : // For component alpha layers, we leave contentType as CONTENT_COLOR.
489 :
490 : // We need to validate the entire buffer, to make sure that only valid
491 : // pixels are sampled
492 0 : neededRegion = destBufferRect;
493 : }
494 :
495 0 : if (mTexImage &&
496 0 : (mTexImage->GetContentType() != contentType ||
497 0 : (mode == Layer::SURFACE_COMPONENT_ALPHA) != (mTexImageOnWhite != nsnull))) {
498 : // We're effectively clearing the valid region, so we need to draw
499 : // the entire needed region now.
500 0 : result.mRegionToInvalidate = mLayer->GetValidRegion();
501 0 : validRegion.SetEmpty();
502 0 : mTexImage = nsnull;
503 0 : mTexImageOnWhite = nsnull;
504 0 : mBufferRect.SetRect(0, 0, 0, 0);
505 0 : mBufferRotation.MoveTo(0, 0);
506 : // Restart decision process with the cleared buffer. We can only go
507 : // around the loop one more iteration, since mTexImage is null now.
508 0 : continue;
509 : }
510 :
511 : break;
512 : }
513 :
514 0 : result.mRegionToDraw.Sub(neededRegion, validRegion);
515 0 : if (result.mRegionToDraw.IsEmpty())
516 : return result;
517 :
518 0 : if (destBufferRect.width > gl()->GetMaxTextureImageSize() ||
519 0 : destBufferRect.height > gl()->GetMaxTextureImageSize()) {
520 : return result;
521 : }
522 :
523 0 : nsIntRect drawBounds = result.mRegionToDraw.GetBounds();
524 0 : nsRefPtr<TextureImage> destBuffer;
525 0 : nsRefPtr<TextureImage> destBufferOnWhite;
526 :
527 0 : PRUint32 bufferFlags = canHaveRotation ? ALLOW_REPEAT : 0;
528 0 : if (canReuseBuffer) {
529 0 : nsIntRect keepArea;
530 0 : if (keepArea.IntersectRect(destBufferRect, mBufferRect)) {
531 : // Set mBufferRotation so that the pixels currently in mBuffer
532 : // will still be rendered in the right place when mBufferRect
533 : // changes to destBufferRect.
534 : nsIntPoint newRotation = mBufferRotation +
535 0 : (destBufferRect.TopLeft() - mBufferRect.TopLeft());
536 0 : WrapRotationAxis(&newRotation.x, mBufferRect.width);
537 0 : WrapRotationAxis(&newRotation.y, mBufferRect.height);
538 0 : NS_ASSERTION(nsIntRect(nsIntPoint(0,0), mBufferRect.Size()).Contains(newRotation),
539 : "newRotation out of bounds");
540 0 : PRInt32 xBoundary = destBufferRect.XMost() - newRotation.x;
541 0 : PRInt32 yBoundary = destBufferRect.YMost() - newRotation.y;
542 0 : if ((drawBounds.x < xBoundary && xBoundary < drawBounds.XMost()) ||
543 0 : (drawBounds.y < yBoundary && yBoundary < drawBounds.YMost()) ||
544 0 : (newRotation != nsIntPoint(0,0) && !canHaveRotation)) {
545 : // The stuff we need to redraw will wrap around an edge of the
546 : // buffer, so we will need to do a self-copy
547 : // If mBufferRotation == nsIntPoint(0,0) we could do a real
548 : // self-copy but we're not going to do that in GL yet.
549 : // We can't do a real self-copy because the buffer is rotated.
550 : // So allocate a new buffer for the destination.
551 0 : destBufferRect = neededRegion.GetBounds();
552 0 : destBuffer = CreateClampOrRepeatTextureImage(gl(), destBufferRect.Size(), contentType, bufferFlags);
553 0 : if (!destBuffer)
554 : return result;
555 0 : if (mode == Layer::SURFACE_COMPONENT_ALPHA) {
556 : destBufferOnWhite =
557 0 : CreateClampOrRepeatTextureImage(gl(), destBufferRect.Size(), contentType, bufferFlags);
558 0 : if (!destBufferOnWhite)
559 : return result;
560 : }
561 : } else {
562 0 : mBufferRect = destBufferRect;
563 0 : mBufferRotation = newRotation;
564 : }
565 : } else {
566 : // No pixels are going to be kept. The whole visible region
567 : // will be redrawn, so we don't need to copy anything, so we don't
568 : // set destBuffer.
569 0 : mBufferRect = destBufferRect;
570 0 : mBufferRotation = nsIntPoint(0,0);
571 : }
572 : } else {
573 : // The buffer's not big enough, so allocate a new one
574 0 : destBuffer = CreateClampOrRepeatTextureImage(gl(), destBufferRect.Size(), contentType, bufferFlags);
575 0 : if (!destBuffer)
576 : return result;
577 :
578 0 : if (mode == Layer::SURFACE_COMPONENT_ALPHA) {
579 : destBufferOnWhite =
580 0 : CreateClampOrRepeatTextureImage(gl(), destBufferRect.Size(), contentType, bufferFlags);
581 0 : if (!destBufferOnWhite)
582 : return result;
583 : }
584 : }
585 0 : NS_ASSERTION(!(aFlags & PAINT_WILL_RESAMPLE) || destBufferRect == neededRegion.GetBounds(),
586 : "If we're resampling, we need to validate the entire buffer");
587 :
588 0 : if (!destBuffer && !mTexImage) {
589 : return result;
590 : }
591 :
592 0 : if (destBuffer) {
593 0 : if (mTexImage && (mode != Layer::SURFACE_COMPONENT_ALPHA || mTexImageOnWhite)) {
594 : // BlitTextureImage depends on the FBO texture target being
595 : // TEXTURE_2D. This isn't the case on some older X1600-era Radeons.
596 0 : if (mOGLLayer->OGLManager()->FBOTextureTarget() == LOCAL_GL_TEXTURE_2D) {
597 0 : nsIntRect overlap;
598 0 : overlap.IntersectRect(mBufferRect, destBufferRect);
599 :
600 0 : nsIntRect srcRect(overlap), dstRect(overlap);
601 0 : srcRect.MoveBy(- mBufferRect.TopLeft() + mBufferRotation);
602 0 : dstRect.MoveBy(- destBufferRect.TopLeft());
603 :
604 0 : if (mBufferRotation != nsIntPoint(0, 0)) {
605 : // If mBuffer is rotated, then BlitTextureImage will only be copying the bottom-right section
606 : // of the buffer. We need to invalidate the remaining sections so that they get redrawn too.
607 : // Alternatively we could teach BlitTextureImage to rearrange the rotated segments onto
608 : // the new buffer.
609 :
610 : // When the rotated buffer is reorganised, the bottom-right section will be drawn in the top left.
611 : // Find the point where this content ends.
612 : nsIntPoint rotationPoint(mBufferRect.x + mBufferRect.width - mBufferRotation.x,
613 0 : mBufferRect.y + mBufferRect.height - mBufferRotation.y);
614 :
615 : // The buffer looks like:
616 : // ______
617 : // |1 |2 | Where the center point is offset by mBufferRotation from the top-left corner.
618 : // |___|__|
619 : // |3 |4 |
620 : // |___|__|
621 : //
622 : // This is drawn to the screen as:
623 : // ______
624 : // |4 |3 | Where the center point is { width - mBufferRotation.x, height - mBufferRotation.y } from
625 : // |___|__| from the top left corner - rotationPoint. Since only quadrant 4 will actually be copied,
626 : // |2 |1 | we need to invalidate the others.
627 : // |___|__|
628 : //
629 : // Quadrants 2 and 1
630 0 : nsIntRect bottom(mBufferRect.x, rotationPoint.y, mBufferRect.width, mBufferRotation.y);
631 : // Quadrant 3
632 0 : nsIntRect topright(rotationPoint.x, mBufferRect.y, mBufferRotation.x, rotationPoint.y - mBufferRect.y);
633 :
634 0 : if (!bottom.IsEmpty()) {
635 0 : nsIntRegion temp;
636 0 : temp.And(destBufferRect, bottom);
637 0 : result.mRegionToDraw.Or(result.mRegionToDraw, temp);
638 : }
639 0 : if (!topright.IsEmpty()) {
640 0 : nsIntRegion temp;
641 0 : temp.And(destBufferRect, topright);
642 0 : result.mRegionToDraw.Or(result.mRegionToDraw, temp);
643 : }
644 : }
645 :
646 0 : destBuffer->Resize(destBufferRect.Size());
647 :
648 : gl()->BlitTextureImage(mTexImage, srcRect,
649 0 : destBuffer, dstRect);
650 0 : destBuffer->MarkValid();
651 0 : if (mode == Layer::SURFACE_COMPONENT_ALPHA) {
652 0 : destBufferOnWhite->Resize(destBufferRect.Size());
653 : gl()->BlitTextureImage(mTexImageOnWhite, srcRect,
654 0 : destBufferOnWhite, dstRect);
655 0 : destBufferOnWhite->MarkValid();
656 : }
657 : } else {
658 : // can't blit, just draw everything
659 0 : destBuffer = CreateClampOrRepeatTextureImage(gl(), destBufferRect.Size(), contentType, bufferFlags);
660 0 : if (mode == Layer::SURFACE_COMPONENT_ALPHA) {
661 : destBufferOnWhite =
662 0 : CreateClampOrRepeatTextureImage(gl(), destBufferRect.Size(), contentType, bufferFlags);
663 : }
664 : }
665 : }
666 :
667 0 : mTexImage = destBuffer.forget();
668 0 : if (mode == Layer::SURFACE_COMPONENT_ALPHA) {
669 0 : mTexImageOnWhite = destBufferOnWhite.forget();
670 : }
671 0 : mBufferRect = destBufferRect;
672 0 : mBufferRotation = nsIntPoint(0,0);
673 : }
674 0 : NS_ASSERTION(canHaveRotation || mBufferRotation == nsIntPoint(0,0),
675 : "Rotation disabled, but we have nonzero rotation?");
676 :
677 0 : nsIntRegion invalidate;
678 0 : invalidate.Sub(mLayer->GetValidRegion(), destBufferRect);
679 0 : result.mRegionToInvalidate.Or(result.mRegionToInvalidate, invalidate);
680 :
681 : // Figure out which quadrant to draw in
682 0 : PRInt32 xBoundary = mBufferRect.XMost() - mBufferRotation.x;
683 0 : PRInt32 yBoundary = mBufferRect.YMost() - mBufferRotation.y;
684 0 : XSide sideX = drawBounds.XMost() <= xBoundary ? RIGHT : LEFT;
685 0 : YSide sideY = drawBounds.YMost() <= yBoundary ? BOTTOM : TOP;
686 0 : nsIntRect quadrantRect = GetQuadrantRectangle(sideX, sideY);
687 0 : NS_ASSERTION(quadrantRect.Contains(drawBounds), "Messed up quadrants");
688 :
689 0 : nsIntPoint offset = -nsIntPoint(quadrantRect.x, quadrantRect.y);
690 :
691 : // Make the region to draw relative to the buffer, before
692 : // passing to BeginUpdate.
693 0 : result.mRegionToDraw.MoveBy(offset);
694 : // BeginUpdate is allowed to modify the given region,
695 : // if it wants more to be repainted than we request.
696 0 : if (mode == Layer::SURFACE_COMPONENT_ALPHA) {
697 0 : nsIntRegion drawRegionCopy = result.mRegionToDraw;
698 0 : gfxASurface *onBlack = mTexImage->BeginUpdate(drawRegionCopy);
699 0 : gfxASurface *onWhite = mTexImageOnWhite->BeginUpdate(result.mRegionToDraw);
700 0 : NS_ASSERTION(result.mRegionToDraw == drawRegionCopy,
701 : "BeginUpdate should always modify the draw region in the same way!");
702 0 : FillSurface(onBlack, result.mRegionToDraw, nsIntPoint(0,0), gfxRGBA(0.0, 0.0, 0.0, 1.0));
703 0 : FillSurface(onWhite, result.mRegionToDraw, nsIntPoint(0,0), gfxRGBA(1.0, 1.0, 1.0, 1.0));
704 0 : gfxASurface* surfaces[2] = { onBlack, onWhite };
705 0 : nsRefPtr<gfxTeeSurface> surf = new gfxTeeSurface(surfaces, ArrayLength(surfaces));
706 :
707 : // XXX If the device offset is set on the individual surfaces instead of on
708 : // the tee surface, we render in the wrong place. Why?
709 0 : gfxPoint deviceOffset = onBlack->GetDeviceOffset();
710 0 : onBlack->SetDeviceOffset(gfxPoint(0, 0));
711 0 : onWhite->SetDeviceOffset(gfxPoint(0, 0));
712 0 : surf->SetDeviceOffset(deviceOffset);
713 :
714 : // Using this surface as a source will likely go horribly wrong, since
715 : // only the onBlack surface will really be used, so alpha information will
716 : // be incorrect.
717 0 : surf->SetAllowUseAsSource(false);
718 0 : result.mContext = new gfxContext(surf);
719 : } else {
720 0 : result.mContext = new gfxContext(mTexImage->BeginUpdate(result.mRegionToDraw));
721 0 : if (mTexImage->GetContentType() == gfxASurface::CONTENT_COLOR_ALPHA) {
722 0 : gfxUtils::ClipToRegion(result.mContext, result.mRegionToDraw);
723 0 : result.mContext->SetOperator(gfxContext::OPERATOR_CLEAR);
724 0 : result.mContext->Paint();
725 0 : result.mContext->SetOperator(gfxContext::OPERATOR_OVER);
726 : }
727 : }
728 0 : if (!result.mContext) {
729 0 : NS_WARNING("unable to get context for update");
730 : return result;
731 : }
732 0 : result.mContext->Translate(-gfxPoint(quadrantRect.x, quadrantRect.y));
733 : // Move rgnToPaint back into position so that the thebes callback
734 : // gets the right coordintes.
735 0 : result.mRegionToDraw.MoveBy(-offset);
736 :
737 : // If we do partial updates, we have to clip drawing to the regionToDraw.
738 : // If we don't clip, background images will be fillrect'd to the region correctly,
739 : // while text or lines will paint outside of the regionToDraw. This becomes apparent
740 : // with concave regions. Right now the scrollbars invalidate a narrow strip of the awesomebar
741 : // although they never cover it. This leads to two draw rects, the narow strip and the actually
742 : // newly exposed area. It would be wise to fix this glitch in any way to have simpler
743 : // clip and draw regions.
744 0 : gfxUtils::ClipToRegion(result.mContext, result.mRegionToDraw);
745 :
746 : return result;
747 : }
748 :
749 0 : ThebesLayerOGL::ThebesLayerOGL(LayerManagerOGL *aManager)
750 : : ThebesLayer(aManager, nsnull)
751 : , LayerOGL(aManager)
752 0 : , mBuffer(nsnull)
753 : {
754 0 : mImplData = static_cast<LayerOGL*>(this);
755 0 : }
756 :
757 0 : ThebesLayerOGL::~ThebesLayerOGL()
758 : {
759 0 : Destroy();
760 0 : }
761 :
762 : void
763 0 : ThebesLayerOGL::Destroy()
764 : {
765 0 : if (!mDestroyed) {
766 0 : mBuffer = nsnull;
767 0 : mDestroyed = true;
768 : }
769 0 : }
770 :
771 : bool
772 0 : ThebesLayerOGL::CreateSurface()
773 : {
774 0 : NS_ASSERTION(!mBuffer, "buffer already created?");
775 :
776 0 : if (mVisibleRegion.IsEmpty()) {
777 0 : return false;
778 : }
779 :
780 0 : if (gl()->TextureImageSupportsGetBackingSurface()) {
781 : // use the ThebesLayerBuffer fast-path
782 0 : mBuffer = new SurfaceBufferOGL(this);
783 : } else {
784 0 : mBuffer = new BasicBufferOGL(this);
785 : }
786 0 : return true;
787 : }
788 :
789 : void
790 0 : ThebesLayerOGL::SetVisibleRegion(const nsIntRegion &aRegion)
791 : {
792 0 : if (aRegion.IsEqual(mVisibleRegion))
793 0 : return;
794 0 : ThebesLayer::SetVisibleRegion(aRegion);
795 : }
796 :
797 : void
798 0 : ThebesLayerOGL::InvalidateRegion(const nsIntRegion &aRegion)
799 : {
800 0 : mValidRegion.Sub(mValidRegion, aRegion);
801 0 : }
802 :
803 : void
804 0 : ThebesLayerOGL::RenderLayer(int aPreviousFrameBuffer,
805 : const nsIntPoint& aOffset)
806 : {
807 0 : if (!mBuffer && !CreateSurface()) {
808 0 : return;
809 : }
810 0 : NS_ABORT_IF_FALSE(mBuffer, "should have a buffer here");
811 :
812 0 : mOGLManager->MakeCurrent();
813 0 : gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
814 :
815 : TextureImage::ContentType contentType =
816 0 : CanUseOpaqueSurface() ? gfxASurface::CONTENT_COLOR :
817 0 : gfxASurface::CONTENT_COLOR_ALPHA;
818 :
819 0 : PRUint32 flags = 0;
820 : #ifndef MOZ_GFX_OPTIMIZE_MOBILE
821 0 : gfxMatrix transform2d;
822 0 : if (GetEffectiveTransform().Is2D(&transform2d)) {
823 0 : if (transform2d.HasNonIntegerTranslation()) {
824 0 : flags |= ThebesLayerBufferOGL::PAINT_WILL_RESAMPLE;
825 : }
826 : } else {
827 0 : flags |= ThebesLayerBufferOGL::PAINT_WILL_RESAMPLE;
828 : }
829 : #endif
830 :
831 0 : Buffer::PaintState state = mBuffer->BeginPaint(contentType, flags);
832 0 : mValidRegion.Sub(mValidRegion, state.mRegionToInvalidate);
833 :
834 0 : if (state.mContext) {
835 0 : state.mRegionToInvalidate.And(state.mRegionToInvalidate, mVisibleRegion);
836 :
837 : LayerManager::DrawThebesLayerCallback callback =
838 0 : mOGLManager->GetThebesLayerCallback();
839 0 : if (!callback) {
840 0 : NS_ERROR("GL should never need to update ThebesLayers in an empty transaction");
841 : } else {
842 0 : void* callbackData = mOGLManager->GetThebesLayerCallbackData();
843 0 : SetAntialiasingFlags(this, state.mContext);
844 : callback(this, state.mContext, state.mRegionToDraw,
845 0 : state.mRegionToInvalidate, callbackData);
846 : // Everything that's visible has been validated. Do this instead of just
847 : // OR-ing with aRegionToDraw, since that can lead to a very complex region
848 : // here (OR doesn't automatically simplify to the simplest possible
849 : // representation of a region.)
850 0 : nsIntRegion tmp;
851 0 : tmp.Or(mVisibleRegion, state.mRegionToDraw);
852 0 : mValidRegion.Or(mValidRegion, tmp);
853 : }
854 : }
855 :
856 : // Drawing thebes layers can change the current context, reset it.
857 0 : gl()->MakeCurrent();
858 :
859 0 : gl()->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, aPreviousFrameBuffer);
860 0 : mBuffer->RenderTo(aOffset, mOGLManager, flags);
861 : }
862 :
863 : Layer*
864 0 : ThebesLayerOGL::GetLayer()
865 : {
866 0 : return this;
867 : }
868 :
869 : bool
870 0 : ThebesLayerOGL::IsEmpty()
871 : {
872 0 : return !mBuffer;
873 : }
874 :
875 : void
876 0 : ThebesLayerOGL::CleanupResources()
877 : {
878 0 : mBuffer = nsnull;
879 0 : }
880 :
881 : class ShadowBufferOGL : public ThebesLayerBufferOGL
882 0 : {
883 : public:
884 0 : ShadowBufferOGL(ShadowThebesLayerOGL* aLayer)
885 0 : : ThebesLayerBufferOGL(aLayer, aLayer)
886 0 : {}
887 :
888 0 : virtual PaintState BeginPaint(ContentType aContentType, PRUint32) {
889 0 : NS_RUNTIMEABORT("can't BeginPaint for a shadow layer");
890 0 : return PaintState();
891 : }
892 :
893 : void Upload(gfxASurface* aUpdate, const nsIntRegion& aUpdated,
894 : const nsIntRect& aRect, const nsIntPoint& aRotation);
895 :
896 : protected:
897 0 : virtual nsIntPoint GetOriginOffset() {
898 0 : return mBufferRect.TopLeft() - mBufferRotation;
899 : }
900 :
901 : private:
902 : nsIntRect mBufferRect;
903 : nsIntPoint mBufferRotation;
904 : };
905 :
906 : void
907 0 : ShadowBufferOGL::Upload(gfxASurface* aUpdate, const nsIntRegion& aUpdated,
908 : const nsIntRect& aRect, const nsIntPoint& aRotation)
909 : {
910 0 : gfxIntSize size = aUpdate->GetSize();
911 0 : if (!mTexImage ||
912 0 : GetSize() != nsIntSize(size.width, size.height) ||
913 0 : mTexImage->GetContentType() != aUpdate->GetContentType()) {
914 : // XXX we should do something here to decide whether to use REPEAT or not,
915 : // but I'm not sure what
916 : mTexImage = CreateClampOrRepeatTextureImage(gl(),
917 0 : nsIntSize(size.width, size.height), aUpdate->GetContentType(), ALLOW_REPEAT);
918 : }
919 :
920 0 : nsIntRegion destRegion(aUpdated);
921 : // aUpdated is in screen coordinates. Move it so that the layer's
922 : // top-left is 0,0
923 0 : nsIntPoint visTopLeft = mLayer->GetVisibleRegion().GetBounds().TopLeft();
924 0 : destRegion.MoveBy(-visTopLeft);
925 :
926 : // Correct for rotation
927 0 : destRegion.MoveBy(aRotation);
928 0 : nsIntRect destBounds = destRegion.GetBounds();
929 : destRegion.MoveBy((destBounds.x >= size.width) ? -size.width : 0,
930 0 : (destBounds.y >= size.height) ? -size.height : 0);
931 :
932 : // There's code to make sure that updated regions don't cross rotation
933 : // boundaries, so assert here that this is the case
934 0 : NS_ASSERTION(((destBounds.x % size.width) + destBounds.width <= size.width) &&
935 : ((destBounds.y % size.height) + destBounds.height <= size.height),
936 : "Updated region lies across rotation boundaries!");
937 :
938 : // NB: this gfxContext must not escape EndUpdate() below
939 0 : mTexImage->DirectUpdate(aUpdate, destRegion);
940 :
941 0 : mBufferRect = aRect;
942 0 : mBufferRotation = aRotation;
943 0 : }
944 :
945 0 : ShadowThebesLayerOGL::ShadowThebesLayerOGL(LayerManagerOGL *aManager)
946 : : ShadowThebesLayer(aManager, nsnull)
947 0 : , LayerOGL(aManager)
948 : {
949 0 : mImplData = static_cast<LayerOGL*>(this);
950 0 : }
951 :
952 0 : ShadowThebesLayerOGL::~ShadowThebesLayerOGL()
953 0 : {}
954 :
955 : void
956 0 : ShadowThebesLayerOGL::Swap(const ThebesBuffer& aNewFront,
957 : const nsIntRegion& aUpdatedRegion,
958 : OptionalThebesBuffer* aNewBack,
959 : nsIntRegion* aNewBackValidRegion,
960 : OptionalThebesBuffer* aReadOnlyFront,
961 : nsIntRegion* aFrontUpdatedRegion)
962 : {
963 0 : if (!mDestroyed) {
964 0 : if (!mBuffer) {
965 0 : mBuffer = new ShadowBufferOGL(this);
966 : }
967 0 : nsRefPtr<gfxASurface> surf = ShadowLayerForwarder::OpenDescriptor(aNewFront.buffer());
968 0 : mBuffer->Upload(surf, aUpdatedRegion, aNewFront.rect(), aNewFront.rotation());
969 : }
970 :
971 0 : *aNewBack = aNewFront;
972 0 : *aNewBackValidRegion = mValidRegion;
973 0 : *aReadOnlyFront = null_t();
974 0 : aFrontUpdatedRegion->SetEmpty();
975 0 : }
976 :
977 : void
978 0 : ShadowThebesLayerOGL::DestroyFrontBuffer()
979 : {
980 0 : mBuffer = nsnull;
981 0 : }
982 :
983 : void
984 0 : ShadowThebesLayerOGL::Disconnect()
985 : {
986 0 : Destroy();
987 0 : }
988 :
989 : void
990 0 : ShadowThebesLayerOGL::Destroy()
991 : {
992 0 : if (!mDestroyed) {
993 0 : mDestroyed = true;
994 0 : mBuffer = nsnull;
995 : }
996 0 : }
997 :
998 : Layer*
999 0 : ShadowThebesLayerOGL::GetLayer()
1000 : {
1001 0 : return this;
1002 : }
1003 :
1004 : bool
1005 0 : ShadowThebesLayerOGL::IsEmpty()
1006 : {
1007 0 : return !mBuffer;
1008 : }
1009 :
1010 : void
1011 0 : ShadowThebesLayerOGL::RenderLayer(int aPreviousFrameBuffer,
1012 : const nsIntPoint& aOffset)
1013 : {
1014 0 : if (!mBuffer) {
1015 0 : return;
1016 : }
1017 0 : NS_ABORT_IF_FALSE(mBuffer, "should have a buffer here");
1018 :
1019 0 : mOGLManager->MakeCurrent();
1020 0 : gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
1021 :
1022 0 : gl()->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, aPreviousFrameBuffer);
1023 0 : mBuffer->RenderTo(aOffset, mOGLManager, 0);
1024 : }
1025 :
1026 : void
1027 0 : ShadowThebesLayerOGL::CleanupResources()
1028 : {
1029 0 : DestroyFrontBuffer();
1030 0 : }
1031 :
1032 : } /* layers */
1033 : } /* mozilla */
|