Fix drawing of GL Thebes layers with rotation when using tiled textures
authorChris Lord <chrislord.net@gmail.com>
Wed, 24 Aug 2011 15:09:59 +0100
changeset 75821 50575fae9b1d3d2297793410321cc6ed6860abaa
parent 75820 7099a6d4c871b63baf0826d5a9bfa5861d864aa6
child 75822 01225860326563a71ab2d1cc6e92e9b6b60bf256
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
milestone9.0a1
Fix drawing of GL Thebes layers with rotation when using tiled textures Drawing of rotated buffers relies on texture-wrapping, but in the case of tiled textures, this would cause each individual tile to wrap instead of wrapping the compound texture. Add a special case for tiled textures that manually does the wrapping.
gfx/layers/opengl/ThebesLayerOGL.cpp
gfx/thebes/GLContext.cpp
gfx/thebes/GLContext.h
--- a/gfx/layers/opengl/ThebesLayerOGL.cpp
+++ b/gfx/layers/opengl/ThebesLayerOGL.cpp
@@ -186,50 +186,106 @@ ThebesLayerBufferOGL::RenderTo(const nsI
       // entire visible region's bounds, and we should draw it all in one quad
       // to avoid unexpected aliasing.
       tmpRegion = visibleRegion.GetBounds();
       renderRegion = &tmpRegion;
     } else {
       renderRegion = &visibleRegion;
     }
 
-    mTexImage->BeginTileIteration();
-    if (mTexImageOnWhite) {
-      mTexImageOnWhite->BeginTileIteration();
-      NS_ASSERTION(mTexImageOnWhite->GetTileRect() == mTexImage->GetTileRect(), "component alpha textures should be the same size.");
-    }
     nsIntRegion region(*renderRegion);
     nsIntPoint origin = GetOriginOffset();
     region.MoveBy(-origin);           // translate into TexImage space, buffer origin might not be at texture (0,0)
 
+    // Figure out the intersecting draw region
+    nsIntSize texSize = mTexImage->GetSize();
+    nsIntRect textureRect = nsIntRect(0, 0, texSize.width, texSize.height);
+    textureRect.MoveBy(region.GetBounds().TopLeft());
+    nsIntRegion subregion;
+    subregion.And(region, textureRect);
+    if (subregion.IsEmpty())  // Region is empty, nothing to draw
+      return;
+
+    nsIntRegion screenRects;
+    nsIntRegion regionRects;
+
+    // Collect texture/screen coordinates for drawing
+    nsIntRegionRectIterator iter(subregion);
+    while (const nsIntRect* iterRect = iter.Next()) {
+        nsIntRect regionRect = *iterRect;
+        nsIntRect screenRect = regionRect;
+        screenRect.MoveBy(origin);
+
+        screenRects.Or(screenRects, screenRect);
+        regionRects.Or(regionRects, regionRect);
+    }
+
+    mTexImage->BeginTileIteration();
+    if (mTexImageOnWhite) {
+      NS_ASSERTION(mTexImage->GetTileCount() == mTexImageOnWhite->GetTileCount(),
+                   "Tile count mismatch on component alpha texture");
+      mTexImageOnWhite->BeginTileIteration();
+    }
+
+    bool usingTiles = (mTexImage->GetTileCount() > 1);
     do {
-      nsIntRect textureRect = mTexImage->GetTileRect();
-      textureRect.MoveBy(region.GetBounds().x, region.GetBounds().y);
-      nsIntRegion subregion(region);
-      subregion.And(region, textureRect); // region this texture is visible in
-      if (subregion.IsEmpty()) {
-        continue;
+      if (mTexImageOnWhite) {
+        NS_ASSERTION(mTexImageOnWhite->GetTileRect() == mTexImage->GetTileRect(), "component alpha textures should be the same size.");
       }
+
+      nsIntRect tileRect = mTexImage->GetTileRect();
+
       // Bind textures.
       TextureImage::ScopedBindTexture texBind(mTexImage, LOCAL_GL_TEXTURE0);
       TextureImage::ScopedBindTexture texOnWhiteBind(mTexImageOnWhite, LOCAL_GL_TEXTURE1);
 
-      nsIntRegionRectIterator iter(subregion);
-      while (const nsIntRect *iterRect = iter.Next()) {
-        nsIntRect regionRect = *iterRect;  // one rectangle of this texture's region
-        // translate into the correct place for this texture sub-region
-        nsIntRect screenRect = regionRect;
-        screenRect.MoveBy(origin);
-        program->SetLayerQuadRect(screenRect);
+      // Draw texture. If we're using tiles, we do repeating manually, as texture
+      // repeat would cause each individual tile to repeat instead of the
+      // compound texture as a whole. This involves drawing at most 4 sections,
+      // 2 for each axis that has texture repeat.
+      for (int y = 0; y < (usingTiles ? 2 : 1); y++) {
+        for (int x = 0; x < (usingTiles ? 2 : 1); x++) {
+          nsIntRect currentTileRect(tileRect);
+          currentTileRect.MoveBy(x * texSize.width, y * texSize.height);
+
+          nsIntRegionRectIterator screenIter(screenRects);
+          nsIntRegionRectIterator regionIter(regionRects);
+
+          const nsIntRect* screenRect;
+          const nsIntRect* regionRect;
+          while ((screenRect = screenIter.Next()) &&
+                 (regionRect = regionIter.Next())) {
+              nsIntRect tileScreenRect(*screenRect);
+              nsIntRect tileRegionRect(*regionRect);
 
-        regionRect.MoveBy(-mTexImage->GetTileRect().TopLeft()); // get region of tile
-        aManager->BindAndDrawQuadWithTextureRect(program, regionRect,
-                                                 textureRect.Size(),
-                                                 mTexImage->GetWrapMode());
+              // When we're using tiles, find the intersection between the tile
+              // rect and this region rect. Tiling is then handled by the
+              // outer for-loops and modifying the tile rect.
+              if (usingTiles) {
+                  tileScreenRect.MoveBy(-origin);
+                  tileScreenRect = tileScreenRect.Intersect(currentTileRect);
+                  tileScreenRect.MoveBy(origin);
+
+                  if (tileScreenRect.IsEmpty())
+                    continue;
+
+                  tileRegionRect = regionRect->Intersect(currentTileRect);
+                  tileRegionRect.MoveBy(-currentTileRect.TopLeft());
+              }
+
+              program->SetLayerQuadRect(tileScreenRect);
+              aManager->BindAndDrawQuadWithTextureRect(program, tileRegionRect,
+                                                       tileRect.Size(),
+                                                       mTexImage->GetWrapMode());
+          }
+        }
       }
+
+      if (mTexImageOnWhite)
+          mTexImageOnWhite->NextTile();
     } while (mTexImage->NextTile());
   }
 
   if (mTexImageOnWhite) {
     // Restore defaults
     gl()->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
                              LOCAL_GL_ONE, LOCAL_GL_ONE);
   }
--- a/gfx/thebes/GLContext.cpp
+++ b/gfx/thebes/GLContext.cpp
@@ -896,16 +896,21 @@ void TiledTextureImage::Resize(const nsI
           nsRefPtr<TextureImage> teximg =
                   mGL->TileGenFunc(size, mContentType, mUseNearestFilter);
           mImages.AppendElement(teximg.forget());
       }
     }
     mTextureState = Allocated;
 }
 
+PRUint32 TiledTextureImage::GetTileCount()
+{
+    return mImages.Length();
+}
+
 PRBool
 GLContext::ResizeOffscreenFBO(const gfxIntSize& aSize)
 {
     if (!IsOffscreenSizeAllowed(aSize))
         return PR_FALSE;
 
     MakeCurrent();
 
--- a/gfx/thebes/GLContext.h
+++ b/gfx/thebes/GLContext.h
@@ -213,16 +213,21 @@ public:
         return PR_FALSE;
     };
 
     virtual nsIntRect GetTileRect() {
         return nsIntRect(nsIntPoint(0,0), mSize);
     };
 
     virtual GLuint GetTextureID() = 0;
+
+    virtual PRUint32 GetTileCount() {
+        return 1;
+    };
+
     /**
      * Set this TextureImage's size, and ensure a texture has been
      * allocated.  Must not be called between BeginUpdate and EndUpdate.
      * After a resize, the contents are undefined.
      *
      * If this isn't implemented by a subclass, it will just perform
      * a dummy BeginUpdate/EndUpdate pair.
      */
@@ -389,16 +394,17 @@ class TiledTextureImage
 public:
     TiledTextureImage(GLContext* aGL, nsIntSize aSize,
         TextureImage::ContentType, PRBool aUseNearestFilter = PR_FALSE);
     ~TiledTextureImage();
     void DumpDiv();
     virtual gfxASurface* BeginUpdate(nsIntRegion& aRegion);
     virtual void EndUpdate();
     virtual void Resize(const nsIntSize& aSize);
+    virtual PRUint32 GetTileCount();
     virtual void BeginTileIteration();
     virtual PRBool NextTile();
     virtual nsIntRect GetTileRect();
     virtual GLuint GetTextureID() {
         return mImages[mCurrentImage]->GetTextureID();
     };
     virtual bool DirectUpdate(gfxASurface* aSurf, const nsIntRegion& aRegion, const nsIntPoint& aFrom = nsIntPoint(0,0));
     virtual PRBool InUpdate() const { return mInUpdate; };