Bug 675908 - Fix TiledTextureImage updates. r=joe,romaxa

TiledTextureImage breaks BeginUpdate/EndUpdate in these cases:

- The update is encompassed by more than one tile
- The update is encompassed by a single tile that isn't the first tile
- The update is a non-rectangular region that covers more than one tile

Fixed by using signed instead of unsigned integers in tile loops, correcting
an incorrect device offset and correcting the region returned by BeginUpdate.
This commit is contained in:
Chris Lord 2011-08-24 20:21:53 +01:00
parent 378ef8cf62
commit 7ad668b25a
3 changed files with 109 additions and 44 deletions

View File

@ -575,20 +575,8 @@ BasicTextureImage::BeginUpdate(nsIntRegion& aRegion)
NS_ASSERTION(!mUpdateSurface, "BeginUpdate() without EndUpdate()?");
// determine the region the client will need to repaint
ImageFormat format =
(GetContentType() == gfxASurface::CONTENT_COLOR) ?
gfxASurface::ImageFormatRGB24 : gfxASurface::ImageFormatARGB32;
if (mTextureState != Valid)
{
// if the texture hasn't been initialized yet, or something important
// changed, we need to recreate our backing surface and force the
// client to paint everything
mUpdateRegion = nsIntRect(nsIntPoint(0, 0), mSize);
} else {
mUpdateRegion = aRegion;
}
aRegion = mUpdateRegion;
GetUpdateRegion(aRegion);
mUpdateRegion = aRegion;
nsIntRect rgnSize = mUpdateRegion.GetBounds();
if (!nsIntRect(nsIntPoint(0, 0), mSize).Contains(rgnSize)) {
@ -596,7 +584,10 @@ BasicTextureImage::BeginUpdate(nsIntRegion& aRegion)
return NULL;
}
mUpdateSurface =
ImageFormat format =
(GetContentType() == gfxASurface::CONTENT_COLOR) ?
gfxASurface::ImageFormatRGB24 : gfxASurface::ImageFormatARGB32;
mUpdateSurface =
GetSurfaceForUpdate(gfxIntSize(rgnSize.width, rgnSize.height), format);
if (!mUpdateSurface || mUpdateSurface->CairoStatus()) {
@ -609,6 +600,16 @@ BasicTextureImage::BeginUpdate(nsIntRegion& aRegion)
return mUpdateSurface;
}
void
BasicTextureImage::GetUpdateRegion(nsIntRegion& aForRegion)
{
// if the texture hasn't been initialized yet, or something important
// changed, we need to recreate our backing surface and force the
// client to paint everything
if (mTextureState != Valid)
aForRegion = nsIntRect(nsIntPoint(0, 0), mSize);
}
void
BasicTextureImage::EndUpdate()
{
@ -729,10 +730,9 @@ TiledTextureImage::~TiledTextureImage()
bool
TiledTextureImage::DirectUpdate(gfxASurface* aSurf, const nsIntRegion& aRegion, const nsIntPoint& aFrom /* = nsIntPoint(0, 0) */)
{
nsIntRect bounds = aRegion.GetBounds();
nsIntRegion region;
if (mTextureState != Valid) {
bounds = nsIntRect(0, 0, mSize.width, mSize.height);
nsIntRect bounds = nsIntRect(0, 0, mSize.width, mSize.height);
region = nsIntRegion(bounds);
} else {
region = aRegion;
@ -740,8 +740,8 @@ TiledTextureImage::DirectUpdate(gfxASurface* aSurf, const nsIntRegion& aRegion,
PRBool result = PR_TRUE;
for (unsigned i = 0; i < mImages.Length(); i++) {
unsigned int xPos = (i % mColumns) * mTileSize;
unsigned int yPos = (i / mColumns) * mTileSize;
int xPos = (i % mColumns) * mTileSize;
int yPos = (i / mColumns) * mTileSize;
nsIntRegion tileRegion;
tileRegion.And(region, nsIntRect(nsIntPoint(xPos,yPos), mImages[i]->GetSize())); // intersect with tile
if (tileRegion.IsEmpty())
@ -757,25 +757,66 @@ TiledTextureImage::DirectUpdate(gfxASurface* aSurf, const nsIntRegion& aRegion,
return result;
}
void
TiledTextureImage::GetUpdateRegion(nsIntRegion& aForRegion)
{
if (mTextureState != Valid) {
// if the texture hasn't been initialized yet, or something important
// changed, we need to recreate our backing surface and force the
// client to paint everything
aForRegion = nsIntRect(nsIntPoint(0, 0), mSize);
return;
}
nsIntRegion newRegion;
// We need to query each texture with the region it will be drawing and
// set aForRegion to be the combination of all of these regions
for (unsigned i = 0; i < mImages.Length(); i++) {
int xPos = (i % mColumns) * mTileSize;
int yPos = (i / mColumns) * mTileSize;
nsIntRect imageRect = nsIntRect(nsIntRect(nsIntPoint(xPos,yPos), mImages[i]->GetSize()));
if (aForRegion.Intersects(imageRect)) {
// Make a copy of the region
nsIntRegion subRegion;
subRegion.And(aForRegion, imageRect);
// Translate it into tile-space
subRegion.MoveBy(-xPos, -yPos);
// Query region
mImages[i]->GetUpdateRegion(subRegion);
// Translate back
subRegion.MoveBy(xPos, yPos);
// Add to the accumulated region
newRegion.Or(newRegion, subRegion);
}
}
aForRegion = newRegion;
}
gfxASurface*
TiledTextureImage::BeginUpdate(nsIntRegion& aRegion)
{
NS_ASSERTION(!mInUpdate, "nested update");
mInUpdate = PR_TRUE;
// Note, we don't call GetUpdateRegion here as if the updated region is
// fully contained in a single tile, we get to avoid iterating through
// the tiles again (and a little copying).
if (mTextureState != Valid)
{
// if the texture hasn't been initialized yet, or something important
// changed, we need to recreate our backing surface and force the
// client to paint everything
mUpdateRegion = nsIntRect(nsIntPoint(0, 0), mSize);
} else {
mUpdateRegion = aRegion;
aRegion = nsIntRect(nsIntPoint(0, 0), mSize);
}
nsIntRect bounds = aRegion.GetBounds();
for (unsigned i = 0; i < mImages.Length(); i++) {
unsigned int xPos = (i % mColumns) * mTileSize;
unsigned int yPos = (i / mColumns) * mTileSize;
int xPos = (i % mColumns) * mTileSize;
int yPos = (i / mColumns) * mTileSize;
nsIntRegion imageRegion = nsIntRegion(nsIntRect(nsIntPoint(xPos,yPos), mImages[i]->GetSize()));
// a single Image can handle this update request
@ -786,6 +827,10 @@ TiledTextureImage::BeginUpdate(nsIntRegion& aRegion)
nsRefPtr<gfxASurface> surface = mImages[i]->BeginUpdate(aRegion);
// caller expects container space
aRegion.MoveBy(xPos, yPos);
// Correct the device offset
gfxPoint offset = surface->GetDeviceOffset();
surface->SetDeviceOffset(gfxPoint(offset.x - xPos,
offset.y - yPos));
// we don't have a temp surface
mUpdateSurface = nsnull;
// remember which image to EndUpdate
@ -793,15 +838,21 @@ TiledTextureImage::BeginUpdate(nsIntRegion& aRegion)
return surface.get();
}
}
// Get the real updated region, taking into account the capabilities of
// each TextureImage tile
GetUpdateRegion(aRegion);
mUpdateRegion = aRegion;
bounds = aRegion.GetBounds();
// update covers multiple Images - create a temp surface to paint in
gfxASurface::gfxImageFormat format =
(GetContentType() == gfxASurface::CONTENT_COLOR) ?
gfxASurface::ImageFormatRGB24 : gfxASurface::ImageFormatARGB32;
nsIntRect bounds = aRegion.GetBounds();
mUpdateSurface = gfxPlatform::GetPlatform()->
CreateOffscreenSurface(gfxIntSize(bounds.width, bounds.height), gfxASurface::ContentFromFormat(format));
mUpdateSurface->SetDeviceOffset(gfxPoint(-bounds.x, -bounds.y));
return mUpdateSurface;
}
@ -820,9 +871,10 @@ TiledTextureImage::EndUpdate()
// upload tiles from temp surface
for (unsigned i = 0; i < mImages.Length(); i++) {
unsigned int xPos = (i % mColumns) * mTileSize;
unsigned int yPos = (i / mColumns) * mTileSize;
int xPos = (i % mColumns) * mTileSize;
int yPos = (i / mColumns) * mTileSize;
nsIntRect imageRect = nsIntRect(nsIntPoint(xPos,yPos), mImages[i]->GetSize());
nsIntRegion subregion;
subregion.And(mUpdateRegion, imageRect);
if (subregion.IsEmpty())
@ -842,6 +894,7 @@ TiledTextureImage::EndUpdate()
mInUpdate = PR_FALSE;
mShaderType = mImages[0]->GetShaderProgramType();
mIsRGBFormat = mImages[0]->IsRGB();
mTextureState = Valid;
}
void TiledTextureImage::BeginTileIteration()

View File

@ -193,6 +193,15 @@ public:
* followed by EndUpdate().
*/
virtual gfxASurface* BeginUpdate(nsIntRegion& aRegion) = 0;
/**
* Retrieves the region that will require updating, given a
* region that needs to be updated. This can be used for
* making decisions about updating before calling BeginUpdate().
*
* |aRegion| is an inout param.
*/
virtual void GetUpdateRegion(nsIntRegion& aForRegion) {
};
/**
* Finish the active update and synchronize with the server, if
* necessary.
@ -353,6 +362,7 @@ public:
virtual void BindTexture(GLenum aTextureUnit);
virtual gfxASurface* BeginUpdate(nsIntRegion& aRegion);
virtual void GetUpdateRegion(nsIntRegion& aForRegion);
virtual void EndUpdate();
virtual bool DirectUpdate(gfxASurface* aSurf, const nsIntRegion& aRegion, const nsIntPoint& aFrom = nsIntPoint(0,0));
virtual GLuint GetTextureID() { return mTexture; };
@ -397,6 +407,7 @@ public:
~TiledTextureImage();
void DumpDiv();
virtual gfxASurface* BeginUpdate(nsIntRegion& aRegion);
virtual void GetUpdateRegion(nsIntRegion& aForRegion);
virtual void EndUpdate();
virtual void Resize(const nsIntSize& aSize);
virtual PRUint32 GetTileCount();

View File

@ -1221,27 +1221,28 @@ public:
}
}
virtual void GetUpdateRegion(nsIntRegion& aForRegion)
{
if (mTextureState != Valid) {
// if the texture hasn't been initialized yet, force the
// client to paint everything
aForRegion = nsIntRect(nsIntPoint(0, 0), mSize);
} else if (!mBackingSurface) {
// We can only draw a rectangle, not subregions due to
// the way that our texture upload functions work. If
// needed, we /could/ do multiple texture uploads if we have
// non-overlapping rects, but that's a tradeoff.
aForRegion = nsIntRegion(aForRegion.GetBounds());
}
}
virtual gfxASurface* BeginUpdate(nsIntRegion& aRegion)
{
NS_ASSERTION(!mUpdateSurface, "BeginUpdate() without EndUpdate()?");
// determine the region the client will need to repaint
if (mTextureState != Valid) {
// if the texture hasn't been initialized yet, force the
// client to paint everything
mUpdateRect = nsIntRect(nsIntPoint(0, 0), mSize);
//printf_stderr("v Forcing full paint\n");
aRegion = nsIntRegion(mUpdateRect);
} else {
mUpdateRect = aRegion.GetBounds();
if (!mBackingSurface) {
// We can only draw a rectangle, not subregions due to
// the way that our texture upload functions work. If
// needed, we /could/ do multiple texture uploads if we have
// non-overlapping rects, but that's a tradeoff.
aRegion = nsIntRegion(mUpdateRect);
}
}
GetUpdateRegion(aRegion);
mUpdateRect = aRegion.GetBounds();
//printf_stderr("BeginUpdate with updateRect [%d %d %d %d]\n", mUpdateRect.x, mUpdateRect.y, mUpdateRect.width, mUpdateRect.height);
if (!nsIntRect(nsIntPoint(0, 0), mSize).Contains(mUpdateRect)) {