From 9f3d985c52aa91cf386cc2c4876d47ef2d6cd43d Mon Sep 17 00:00:00 2001 From: Chris Lord Date: Wed, 24 Aug 2011 15:09:59 +0100 Subject: [PATCH] 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 | 100 +++++++++++++++++++++------ gfx/thebes/GLContext.cpp | 5 ++ gfx/thebes/GLContext.h | 6 ++ 3 files changed, 89 insertions(+), 22 deletions(-) diff --git a/gfx/layers/opengl/ThebesLayerOGL.cpp b/gfx/layers/opengl/ThebesLayerOGL.cpp index 91fd36c40ee..83fe201c04b 100644 --- a/gfx/layers/opengl/ThebesLayerOGL.cpp +++ b/gfx/layers/opengl/ThebesLayerOGL.cpp @@ -191,40 +191,96 @@ ThebesLayerBufferOGL::RenderTo(const nsIntPoint& aOffset, 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); - regionRect.MoveBy(-mTexImage->GetTileRect().TopLeft()); // get region of tile - aManager->BindAndDrawQuadWithTextureRect(program, regionRect, - textureRect.Size(), - mTexImage->GetWrapMode()); + 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); + + // 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()); } diff --git a/gfx/thebes/GLContext.cpp b/gfx/thebes/GLContext.cpp index fcf78f30db3..f5e636f8284 100644 --- a/gfx/thebes/GLContext.cpp +++ b/gfx/thebes/GLContext.cpp @@ -901,6 +901,11 @@ void TiledTextureImage::Resize(const nsIntSize& aSize) mTextureState = Allocated; } +PRUint32 TiledTextureImage::GetTileCount() +{ + return mImages.Length(); +} + PRBool GLContext::ResizeOffscreenFBO(const gfxIntSize& aSize) { diff --git a/gfx/thebes/GLContext.h b/gfx/thebes/GLContext.h index 863f3f444a4..7c7bd57c33e 100644 --- a/gfx/thebes/GLContext.h +++ b/gfx/thebes/GLContext.h @@ -218,6 +218,11 @@ public: }; 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. @@ -394,6 +399,7 @@ public: 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();