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.
This commit is contained in:
Chris Lord 2011-08-24 15:09:59 +01:00
parent 085e4aff4d
commit 9f3d985c52
3 changed files with 89 additions and 22 deletions

View File

@ -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());
}

View File

@ -901,6 +901,11 @@ void TiledTextureImage::Resize(const nsIntSize& aSize)
mTextureState = Allocated;
}
PRUint32 TiledTextureImage::GetTileCount()
{
return mImages.Length();
}
PRBool
GLContext::ResizeOffscreenFBO(const gfxIntSize& aSize)
{

View File

@ -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();