mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 00:01:50 +00:00
Bug 811958 - Move TextureImage to its own files - r=bgirard
This commit is contained in:
parent
f8b6d7d465
commit
e744903408
@ -23,6 +23,8 @@
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Util.h" // for DebugOnly
|
||||
|
||||
#include "GLTextureImage.h"
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
namespace mozilla {
|
||||
@ -738,6 +740,19 @@ GLContext::CreateTextureImage(const nsIntSize& aSize,
|
||||
return CreateBasicTextureImage(texture, aSize, aWrapMode, aContentType, this, aFlags);
|
||||
}
|
||||
|
||||
already_AddRefed<TextureImage>
|
||||
GLContext::CreateBasicTextureImage(GLuint aTexture,
|
||||
const nsIntSize& aSize,
|
||||
GLenum aWrapMode,
|
||||
TextureImage::ContentType aContentType,
|
||||
GLContext* aContext,
|
||||
TextureImage::Flags aFlags)
|
||||
{
|
||||
nsRefPtr<BasicTextureImage> teximage(
|
||||
new BasicTextureImage(aTexture, aSize, aWrapMode, aContentType, aContext, aFlags));
|
||||
return teximage.forget();
|
||||
}
|
||||
|
||||
void GLContext::ApplyFilterToBoundTexture(gfxPattern::GraphicsFilter aFilter)
|
||||
{
|
||||
ApplyFilterToBoundTexture(LOCAL_GL_TEXTURE_2D, aFilter);
|
||||
@ -755,546 +770,6 @@ void GLContext::ApplyFilterToBoundTexture(GLuint aTarget,
|
||||
}
|
||||
}
|
||||
|
||||
BasicTextureImage::~BasicTextureImage()
|
||||
{
|
||||
GLContext *ctx = mGLContext;
|
||||
if (ctx->IsDestroyed() || !ctx->IsOwningThreadCurrent()) {
|
||||
ctx = ctx->GetSharedContext();
|
||||
}
|
||||
|
||||
// If we have a context, then we need to delete the texture;
|
||||
// if we don't have a context (either real or shared),
|
||||
// then they went away when the contex was deleted, because it
|
||||
// was the only one that had access to it.
|
||||
if (ctx && !ctx->IsDestroyed()) {
|
||||
mGLContext->MakeCurrent();
|
||||
mGLContext->fDeleteTextures(1, &mTexture);
|
||||
}
|
||||
}
|
||||
|
||||
gfxASurface*
|
||||
BasicTextureImage::BeginUpdate(nsIntRegion& aRegion)
|
||||
{
|
||||
NS_ASSERTION(!mUpdateSurface, "BeginUpdate() without EndUpdate()?");
|
||||
|
||||
// determine the region the client will need to repaint
|
||||
if (mGLContext->CanUploadSubTextures()) {
|
||||
GetUpdateRegion(aRegion);
|
||||
} else {
|
||||
aRegion = nsIntRect(nsIntPoint(0, 0), mSize);
|
||||
}
|
||||
|
||||
mUpdateRegion = aRegion;
|
||||
|
||||
nsIntRect rgnSize = mUpdateRegion.GetBounds();
|
||||
if (!nsIntRect(nsIntPoint(0, 0), mSize).Contains(rgnSize)) {
|
||||
NS_ERROR("update outside of image");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ImageFormat format =
|
||||
(GetContentType() == gfxASurface::CONTENT_COLOR) ?
|
||||
gfxASurface::ImageFormatRGB24 : gfxASurface::ImageFormatARGB32;
|
||||
mUpdateSurface =
|
||||
GetSurfaceForUpdate(gfxIntSize(rgnSize.width, rgnSize.height), format);
|
||||
|
||||
if (!mUpdateSurface || mUpdateSurface->CairoStatus()) {
|
||||
mUpdateSurface = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mUpdateSurface->SetDeviceOffset(gfxPoint(-rgnSize.x, -rgnSize.y));
|
||||
|
||||
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()
|
||||
{
|
||||
NS_ASSERTION(!!mUpdateSurface, "EndUpdate() without BeginUpdate()?");
|
||||
|
||||
// FIXME: this is the slow boat. Make me fast (with GLXPixmap?).
|
||||
|
||||
// Undo the device offset that BeginUpdate set; doesn't much matter for us here,
|
||||
// but important if we ever do anything directly with the surface.
|
||||
mUpdateSurface->SetDeviceOffset(gfxPoint(0, 0));
|
||||
|
||||
bool relative = FinishedSurfaceUpdate();
|
||||
|
||||
mShaderType =
|
||||
mGLContext->UploadSurfaceToTexture(mUpdateSurface,
|
||||
mUpdateRegion,
|
||||
mTexture,
|
||||
mTextureState == Created,
|
||||
mUpdateOffset,
|
||||
relative);
|
||||
FinishedSurfaceUpload();
|
||||
|
||||
mUpdateSurface = nullptr;
|
||||
mTextureState = Valid;
|
||||
}
|
||||
|
||||
void
|
||||
BasicTextureImage::BindTexture(GLenum aTextureUnit)
|
||||
{
|
||||
mGLContext->fActiveTexture(aTextureUnit);
|
||||
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
|
||||
mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
|
||||
}
|
||||
|
||||
void
|
||||
BasicTextureImage::ApplyFilter()
|
||||
{
|
||||
mGLContext->ApplyFilterToBoundTexture(mFilter);
|
||||
}
|
||||
|
||||
|
||||
already_AddRefed<gfxASurface>
|
||||
BasicTextureImage::GetSurfaceForUpdate(const gfxIntSize& aSize, ImageFormat aFmt)
|
||||
{
|
||||
return gfxPlatform::GetPlatform()->
|
||||
CreateOffscreenSurface(aSize, gfxASurface::ContentFromFormat(aFmt));
|
||||
}
|
||||
|
||||
bool
|
||||
BasicTextureImage::FinishedSurfaceUpdate()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
BasicTextureImage::FinishedSurfaceUpload()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
BasicTextureImage::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);
|
||||
region = nsIntRegion(bounds);
|
||||
} else {
|
||||
region = aRegion;
|
||||
}
|
||||
|
||||
mShaderType =
|
||||
mGLContext->UploadSurfaceToTexture(aSurf,
|
||||
region,
|
||||
mTexture,
|
||||
mTextureState == Created,
|
||||
bounds.TopLeft() + aFrom,
|
||||
false);
|
||||
mTextureState = Valid;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
BasicTextureImage::Resize(const nsIntSize& aSize)
|
||||
{
|
||||
NS_ASSERTION(!mUpdateSurface, "Resize() while in update?");
|
||||
|
||||
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
|
||||
|
||||
mGLContext->fTexImage2D(LOCAL_GL_TEXTURE_2D,
|
||||
0,
|
||||
LOCAL_GL_RGBA,
|
||||
aSize.width,
|
||||
aSize.height,
|
||||
0,
|
||||
LOCAL_GL_RGBA,
|
||||
LOCAL_GL_UNSIGNED_BYTE,
|
||||
NULL);
|
||||
|
||||
mTextureState = Allocated;
|
||||
mSize = aSize;
|
||||
}
|
||||
|
||||
TiledTextureImage::TiledTextureImage(GLContext* aGL,
|
||||
nsIntSize aSize,
|
||||
TextureImage::ContentType aContentType,
|
||||
TextureImage::Flags aFlags)
|
||||
: TextureImage(aSize, LOCAL_GL_CLAMP_TO_EDGE, aContentType, aFlags)
|
||||
, mCurrentImage(0)
|
||||
, mIterationCallback(nullptr)
|
||||
, mInUpdate(false)
|
||||
, mRows(0)
|
||||
, mColumns(0)
|
||||
, mGL(aGL)
|
||||
, mTextureState(Created)
|
||||
{
|
||||
mTileSize = (!(aFlags & TextureImage::ForceSingleTile) && mGL->WantsSmallTiles())
|
||||
? 256 : mGL->GetMaxTextureSize();
|
||||
if (aSize != nsIntSize(0,0)) {
|
||||
Resize(aSize);
|
||||
}
|
||||
}
|
||||
|
||||
TiledTextureImage::~TiledTextureImage()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
TiledTextureImage::DirectUpdate(gfxASurface* aSurf, const nsIntRegion& aRegion, const nsIntPoint& aFrom /* = nsIntPoint(0, 0) */)
|
||||
{
|
||||
nsIntRegion region;
|
||||
|
||||
if (mTextureState != Valid) {
|
||||
nsIntRect bounds = nsIntRect(0, 0, mSize.width, mSize.height);
|
||||
region = nsIntRegion(bounds);
|
||||
} else {
|
||||
region = aRegion;
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
int oldCurrentImage = mCurrentImage;
|
||||
BeginTileIteration();
|
||||
do {
|
||||
nsIntRect tileRect = GetSrcTileRect();
|
||||
int xPos = tileRect.x;
|
||||
int yPos = tileRect.y;
|
||||
|
||||
nsIntRegion tileRegion;
|
||||
tileRegion.And(region, tileRect); // intersect with tile
|
||||
|
||||
if (tileRegion.IsEmpty())
|
||||
continue;
|
||||
|
||||
if (mGL->CanUploadSubTextures()) {
|
||||
tileRegion.MoveBy(-xPos, -yPos); // translate into tile local space
|
||||
} else {
|
||||
// If sub-textures are unsupported, expand to tile boundaries
|
||||
tileRect.x = tileRect.y = 0;
|
||||
tileRegion = nsIntRegion(tileRect);
|
||||
}
|
||||
|
||||
result &= mImages[mCurrentImage]->
|
||||
DirectUpdate(aSurf, tileRegion, aFrom + nsIntPoint(xPos, yPos));
|
||||
|
||||
if (mCurrentImage == mImages.Length() - 1) {
|
||||
// We know we're done, but we still need to ensure that the callback
|
||||
// gets called (e.g. to update the uploaded region).
|
||||
NextTile();
|
||||
break;
|
||||
}
|
||||
// Override a callback cancelling iteration if the texture wasn't valid.
|
||||
// We need to force the update in that situation, or we may end up
|
||||
// showing invalid/out-of-date texture data.
|
||||
} while (NextTile() || (mTextureState != Valid));
|
||||
mCurrentImage = oldCurrentImage;
|
||||
|
||||
mShaderType = mImages[0]->GetShaderProgramType();
|
||||
mTextureState = Valid;
|
||||
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 = 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
|
||||
aRegion = nsIntRect(nsIntPoint(0, 0), mSize);
|
||||
}
|
||||
|
||||
nsIntRect bounds = aRegion.GetBounds();
|
||||
|
||||
for (unsigned i = 0; i < mImages.Length(); i++) {
|
||||
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
|
||||
if (imageRegion.Contains(aRegion)) {
|
||||
// adjust for tile offset
|
||||
aRegion.MoveBy(-xPos, -yPos);
|
||||
// forward the actual call
|
||||
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 = nullptr;
|
||||
// remember which image to EndUpdate
|
||||
mCurrentImage = i;
|
||||
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;
|
||||
mUpdateSurface = gfxPlatform::GetPlatform()->
|
||||
CreateOffscreenSurface(gfxIntSize(bounds.width, bounds.height), gfxASurface::ContentFromFormat(format));
|
||||
mUpdateSurface->SetDeviceOffset(gfxPoint(-bounds.x, -bounds.y));
|
||||
|
||||
return mUpdateSurface;
|
||||
}
|
||||
|
||||
void
|
||||
TiledTextureImage::EndUpdate()
|
||||
{
|
||||
NS_ASSERTION(mInUpdate, "EndUpdate not in update");
|
||||
if (!mUpdateSurface) { // update was to a single TextureImage
|
||||
mImages[mCurrentImage]->EndUpdate();
|
||||
mInUpdate = false;
|
||||
mTextureState = Valid;
|
||||
mShaderType = mImages[mCurrentImage]->GetShaderProgramType();
|
||||
return;
|
||||
}
|
||||
|
||||
// upload tiles from temp surface
|
||||
for (unsigned i = 0; i < mImages.Length(); i++) {
|
||||
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())
|
||||
continue;
|
||||
subregion.MoveBy(-xPos, -yPos); // Tile-local space
|
||||
// copy tile from temp surface
|
||||
gfxASurface* surf = mImages[i]->BeginUpdate(subregion);
|
||||
nsRefPtr<gfxContext> ctx = new gfxContext(surf);
|
||||
gfxUtils::ClipToRegion(ctx, subregion);
|
||||
ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
|
||||
ctx->SetSource(mUpdateSurface, gfxPoint(-xPos, -yPos));
|
||||
ctx->Paint();
|
||||
mImages[i]->EndUpdate();
|
||||
}
|
||||
|
||||
mUpdateSurface = nullptr;
|
||||
mInUpdate = false;
|
||||
mShaderType = mImages[0]->GetShaderProgramType();
|
||||
mTextureState = Valid;
|
||||
}
|
||||
|
||||
void TiledTextureImage::BeginTileIteration()
|
||||
{
|
||||
mCurrentImage = 0;
|
||||
}
|
||||
|
||||
bool TiledTextureImage::NextTile()
|
||||
{
|
||||
bool continueIteration = true;
|
||||
|
||||
if (mIterationCallback)
|
||||
continueIteration = mIterationCallback(this, mCurrentImage,
|
||||
mIterationCallbackData);
|
||||
|
||||
if (mCurrentImage + 1 < mImages.Length()) {
|
||||
mCurrentImage++;
|
||||
return continueIteration;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TiledTextureImage::SetIterationCallback(TileIterationCallback aCallback,
|
||||
void* aCallbackData)
|
||||
{
|
||||
mIterationCallback = aCallback;
|
||||
mIterationCallbackData = aCallbackData;
|
||||
}
|
||||
|
||||
nsIntRect TiledTextureImage::GetTileRect()
|
||||
{
|
||||
nsIntRect rect = mImages[mCurrentImage]->GetTileRect();
|
||||
unsigned int xPos = (mCurrentImage % mColumns) * mTileSize;
|
||||
unsigned int yPos = (mCurrentImage / mColumns) * mTileSize;
|
||||
rect.MoveBy(xPos, yPos);
|
||||
return rect;
|
||||
}
|
||||
|
||||
nsIntRect TiledTextureImage::GetSrcTileRect()
|
||||
{
|
||||
nsIntRect rect = GetTileRect();
|
||||
unsigned int srcY = mFlags & NeedsYFlip
|
||||
? mSize.height - rect.height - rect.y
|
||||
: rect.y;
|
||||
return nsIntRect(rect.x, srcY, rect.width, rect.height);
|
||||
}
|
||||
|
||||
void
|
||||
TiledTextureImage::BindTexture(GLenum aTextureUnit)
|
||||
{
|
||||
mImages[mCurrentImage]->BindTexture(aTextureUnit);
|
||||
}
|
||||
|
||||
void
|
||||
TiledTextureImage::ApplyFilter()
|
||||
{
|
||||
mGL->ApplyFilterToBoundTexture(mFilter);
|
||||
}
|
||||
|
||||
/*
|
||||
* Resize, trying to reuse tiles. The reuse strategy is to decide on reuse per
|
||||
* column. A tile on a column is reused if it hasn't changed size, otherwise it
|
||||
* is discarded/replaced. Extra tiles on a column are pruned after iterating
|
||||
* each column, and extra rows are pruned after iteration over the entire image
|
||||
* finishes.
|
||||
*/
|
||||
void TiledTextureImage::Resize(const nsIntSize& aSize)
|
||||
{
|
||||
if (mSize == aSize && mTextureState != Created) {
|
||||
return;
|
||||
}
|
||||
|
||||
// calculate rows and columns, rounding up
|
||||
unsigned int columns = (aSize.width + mTileSize - 1) / mTileSize;
|
||||
unsigned int rows = (aSize.height + mTileSize - 1) / mTileSize;
|
||||
|
||||
// Iterate over old tile-store and insert/remove tiles as necessary
|
||||
int row;
|
||||
unsigned int i = 0;
|
||||
for (row = 0; row < (int)rows; row++) {
|
||||
// If we've gone beyond how many rows there were before, set mColumns to
|
||||
// zero so that we only create new tiles.
|
||||
if (row >= (int)mRows)
|
||||
mColumns = 0;
|
||||
|
||||
// Similarly, if we're on the last row of old tiles and the height has
|
||||
// changed, discard all tiles in that row.
|
||||
// This will cause the pruning of columns not to work, but we don't need
|
||||
// to worry about that, as no more tiles will be reused past this point
|
||||
// anyway.
|
||||
if ((row == (int)mRows - 1) && (aSize.height != mSize.height))
|
||||
mColumns = 0;
|
||||
|
||||
int col;
|
||||
for (col = 0; col < (int)columns; col++) {
|
||||
nsIntSize size( // use tilesize first, then the remainder
|
||||
(col+1) * mTileSize > (unsigned int)aSize.width ? aSize.width % mTileSize : mTileSize,
|
||||
(row+1) * mTileSize > (unsigned int)aSize.height ? aSize.height % mTileSize : mTileSize);
|
||||
|
||||
bool replace = false;
|
||||
|
||||
// Check if we can re-use old tiles.
|
||||
if (col < (int)mColumns) {
|
||||
// Reuse an existing tile. If the tile is an end-tile and the
|
||||
// width differs, replace it instead.
|
||||
if (mSize.width != aSize.width) {
|
||||
if (col == (int)mColumns - 1) {
|
||||
// Tile at the end of the old column, replace it with
|
||||
// a new one.
|
||||
replace = true;
|
||||
} else if (col == (int)columns - 1) {
|
||||
// Tile at the end of the new column, create a new one.
|
||||
} else {
|
||||
// Before the last column on both the old and new sizes,
|
||||
// reuse existing tile.
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// Width hasn't changed, reuse existing tile.
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new tile.
|
||||
nsRefPtr<TextureImage> teximg =
|
||||
mGL->TileGenFunc(size, mContentType, mFlags);
|
||||
if (replace)
|
||||
mImages.ReplaceElementAt(i, teximg.forget());
|
||||
else
|
||||
mImages.InsertElementAt(i, teximg.forget());
|
||||
i++;
|
||||
}
|
||||
|
||||
// Prune any unused tiles on the end of the column.
|
||||
if (row < (int)mRows) {
|
||||
for (col = (int)mColumns - col; col > 0; col--) {
|
||||
mImages.RemoveElementAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prune any unused tiles at the end of the store.
|
||||
unsigned int length = mImages.Length();
|
||||
for (; i < length; i++)
|
||||
mImages.RemoveElementAt(mImages.Length()-1);
|
||||
|
||||
// Reset tile-store properties.
|
||||
mRows = rows;
|
||||
mColumns = columns;
|
||||
mSize = aSize;
|
||||
mTextureState = Allocated;
|
||||
mCurrentImage = 0;
|
||||
}
|
||||
|
||||
uint32_t TiledTextureImage::GetTileCount()
|
||||
{
|
||||
return mImages.Length();
|
||||
}
|
||||
|
||||
GLContext::GLFormats
|
||||
GLContext::ChooseGLFormats(ContextFormat& aCF, ColorByteOrder aByteOrder)
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "GLContextTypes.h"
|
||||
#include "GLTextureImage.h"
|
||||
|
||||
typedef char realGLboolean;
|
||||
|
||||
@ -56,368 +57,6 @@ class GLContext;
|
||||
|
||||
typedef uintptr_t SharedTextureHandle;
|
||||
|
||||
/**
|
||||
* A TextureImage encapsulates a surface that can be drawn to by a
|
||||
* Thebes gfxContext and (hopefully efficiently!) synchronized to a
|
||||
* texture in the server. TextureImages are associated with one and
|
||||
* only one GLContext.
|
||||
*
|
||||
* Implementation note: TextureImages attempt to unify two categories
|
||||
* of backends
|
||||
*
|
||||
* (1) proxy to server-side object that can be bound to a texture;
|
||||
* e.g. Pixmap on X11.
|
||||
*
|
||||
* (2) efficient manager of texture memory; e.g. by having clients draw
|
||||
* into a scratch buffer which is then uploaded with
|
||||
* glTexSubImage2D().
|
||||
*/
|
||||
class TextureImage
|
||||
{
|
||||
NS_INLINE_DECL_REFCOUNTING(TextureImage)
|
||||
public:
|
||||
enum TextureState
|
||||
{
|
||||
Created, // Texture created, but has not had glTexImage called to initialize it.
|
||||
Allocated, // Texture memory exists, but contents are invalid.
|
||||
Valid // Texture fully ready to use.
|
||||
};
|
||||
|
||||
enum Flags {
|
||||
NoFlags = 0x0,
|
||||
UseNearestFilter = 0x1,
|
||||
NeedsYFlip = 0x2,
|
||||
ForceSingleTile = 0x4
|
||||
};
|
||||
|
||||
typedef gfxASurface::gfxContentType ContentType;
|
||||
|
||||
virtual ~TextureImage() {}
|
||||
|
||||
/**
|
||||
* Returns a gfxASurface for updating |aRegion| of the client's
|
||||
* image if successul, NULL if not. |aRegion|'s bounds must fit
|
||||
* within Size(); its coordinate space (if any) is ignored. If
|
||||
* the update begins successfully, the returned gfxASurface is
|
||||
* owned by this. Otherwise, NULL is returned.
|
||||
*
|
||||
* |aRegion| is an inout param: the returned region is what the
|
||||
* client must repaint. Category (1) regions above can
|
||||
* efficiently handle repaints to "scattered" regions, while (2)
|
||||
* can only efficiently handle repaints to rects.
|
||||
*
|
||||
* Painting the returned surface outside of |aRegion| results
|
||||
* in undefined behavior.
|
||||
*
|
||||
* BeginUpdate() calls cannot be "nested", and each successful
|
||||
* BeginUpdate() must be followed by exactly one EndUpdate() (see
|
||||
* below). Failure to do so can leave this in a possibly
|
||||
* inconsistent state. Unsuccessful BeginUpdate()s must not be
|
||||
* 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.
|
||||
*
|
||||
* BeginUpdate() must have been called exactly once before
|
||||
* EndUpdate().
|
||||
*/
|
||||
virtual void EndUpdate() = 0;
|
||||
|
||||
/**
|
||||
* The Image may contain several textures for different regions (tiles).
|
||||
* These functions iterate over each sub texture image tile.
|
||||
*/
|
||||
virtual void BeginTileIteration() {
|
||||
}
|
||||
|
||||
virtual bool NextTile() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Function prototype for a tile iteration callback. Returning false will
|
||||
// cause iteration to be interrupted (i.e. the corresponding NextTile call
|
||||
// will return false).
|
||||
typedef bool (* TileIterationCallback)(TextureImage* aImage,
|
||||
int aTileNumber,
|
||||
void* aCallbackData);
|
||||
|
||||
// Sets a callback to be called every time NextTile is called.
|
||||
virtual void SetIterationCallback(TileIterationCallback aCallback,
|
||||
void* aCallbackData) {
|
||||
}
|
||||
|
||||
virtual nsIntRect GetTileRect() {
|
||||
return nsIntRect(nsIntPoint(0,0), mSize);
|
||||
}
|
||||
|
||||
virtual GLuint GetTextureID() = 0;
|
||||
|
||||
virtual uint32_t 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.
|
||||
*/
|
||||
virtual void Resize(const nsIntSize& aSize) {
|
||||
mSize = aSize;
|
||||
nsIntRegion r(nsIntRect(0, 0, aSize.width, aSize.height));
|
||||
BeginUpdate(r);
|
||||
EndUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this texture as having valid contents. Call this after modifying
|
||||
* the texture contents externally.
|
||||
*/
|
||||
virtual void MarkValid() {}
|
||||
|
||||
/**
|
||||
* aSurf - the source surface to update from
|
||||
* aRegion - the region in this image to update
|
||||
* aFrom - offset in the source to update from
|
||||
*/
|
||||
virtual bool DirectUpdate(gfxASurface *aSurf, const nsIntRegion& aRegion, const nsIntPoint& aFrom = nsIntPoint(0,0)) = 0;
|
||||
|
||||
virtual void BindTexture(GLenum aTextureUnit) = 0;
|
||||
virtual void ReleaseTexture() {}
|
||||
|
||||
void BindTextureAndApplyFilter(GLenum aTextureUnit) {
|
||||
BindTexture(aTextureUnit);
|
||||
ApplyFilter();
|
||||
}
|
||||
|
||||
class ScopedBindTexture
|
||||
{
|
||||
public:
|
||||
ScopedBindTexture(TextureImage *aTexture, GLenum aTextureUnit) :
|
||||
mTexture(aTexture)
|
||||
{
|
||||
if (mTexture) {
|
||||
mTexture->BindTexture(aTextureUnit);
|
||||
}
|
||||
}
|
||||
|
||||
~ScopedBindTexture()
|
||||
{
|
||||
if (mTexture) {
|
||||
mTexture->ReleaseTexture();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
TextureImage *mTexture;
|
||||
};
|
||||
|
||||
class ScopedBindTextureAndApplyFilter
|
||||
: public ScopedBindTexture
|
||||
{
|
||||
public:
|
||||
ScopedBindTextureAndApplyFilter(TextureImage *aTexture, GLenum aTextureUnit) :
|
||||
ScopedBindTexture(aTexture, aTextureUnit)
|
||||
{
|
||||
if (mTexture) {
|
||||
mTexture->ApplyFilter();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the shader program type that should be used to render
|
||||
* this texture. Only valid after a matching BeginUpdate/EndUpdate
|
||||
* pair have been called.
|
||||
*/
|
||||
virtual ShaderProgramType GetShaderProgramType()
|
||||
{
|
||||
return mShaderType;
|
||||
}
|
||||
|
||||
/** Can be called safely at any time. */
|
||||
|
||||
/**
|
||||
* If this TextureImage has a permanent gfxASurface backing,
|
||||
* return it. Otherwise return NULL.
|
||||
*/
|
||||
virtual already_AddRefed<gfxASurface> GetBackingSurface()
|
||||
{ return NULL; }
|
||||
|
||||
const nsIntSize& GetSize() const { return mSize; }
|
||||
ContentType GetContentType() const { return mContentType; }
|
||||
virtual bool InUpdate() const = 0;
|
||||
GLenum GetWrapMode() const { return mWrapMode; }
|
||||
|
||||
void SetFilter(gfxPattern::GraphicsFilter aFilter) { mFilter = aFilter; }
|
||||
|
||||
/**
|
||||
* Applies this TextureImage's filter, assuming that its texture is
|
||||
* the currently bound texture.
|
||||
*/
|
||||
virtual void ApplyFilter() = 0;
|
||||
|
||||
protected:
|
||||
friend class GLContext;
|
||||
|
||||
/**
|
||||
* After the ctor, the TextureImage is invalid. Implementations
|
||||
* must allocate resources successfully before returning the new
|
||||
* TextureImage from GLContext::CreateTextureImage(). That is,
|
||||
* clients must not be given partially-constructed TextureImages.
|
||||
*/
|
||||
TextureImage(const nsIntSize& aSize,
|
||||
GLenum aWrapMode, ContentType aContentType,
|
||||
Flags aFlags = NoFlags)
|
||||
: mSize(aSize)
|
||||
, mWrapMode(aWrapMode)
|
||||
, mContentType(aContentType)
|
||||
, mFilter(gfxPattern::FILTER_GOOD)
|
||||
, mFlags(aFlags)
|
||||
{}
|
||||
|
||||
virtual nsIntRect GetSrcTileRect() {
|
||||
return nsIntRect(nsIntPoint(0,0), mSize);
|
||||
}
|
||||
|
||||
nsIntSize mSize;
|
||||
GLenum mWrapMode;
|
||||
ContentType mContentType;
|
||||
ShaderProgramType mShaderType;
|
||||
gfxPattern::GraphicsFilter mFilter;
|
||||
Flags mFlags;
|
||||
};
|
||||
|
||||
/**
|
||||
* BasicTextureImage is the baseline TextureImage implementation ---
|
||||
* it updates its texture by allocating a scratch buffer for the
|
||||
* client to draw into, then using glTexSubImage2D() to upload the new
|
||||
* pixels. Platforms must provide the code to create a new surface
|
||||
* into which the updated pixels will be drawn, and the code to
|
||||
* convert the update surface's pixels into an image on which we can
|
||||
* glTexSubImage2D().
|
||||
*/
|
||||
class BasicTextureImage
|
||||
: public TextureImage
|
||||
{
|
||||
public:
|
||||
typedef gfxASurface::gfxImageFormat ImageFormat;
|
||||
virtual ~BasicTextureImage();
|
||||
|
||||
BasicTextureImage(GLuint aTexture,
|
||||
const nsIntSize& aSize,
|
||||
GLenum aWrapMode,
|
||||
ContentType aContentType,
|
||||
GLContext* aContext,
|
||||
TextureImage::Flags aFlags = TextureImage::NoFlags)
|
||||
: TextureImage(aSize, aWrapMode, aContentType, aFlags)
|
||||
, mTexture(aTexture)
|
||||
, mTextureState(Created)
|
||||
, mGLContext(aContext)
|
||||
, mUpdateOffset(0, 0)
|
||||
{}
|
||||
|
||||
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; }
|
||||
// Returns a surface to draw into
|
||||
virtual already_AddRefed<gfxASurface>
|
||||
GetSurfaceForUpdate(const gfxIntSize& aSize, ImageFormat aFmt);
|
||||
|
||||
virtual void MarkValid() { mTextureState = Valid; }
|
||||
|
||||
// Call when drawing into the update surface is complete.
|
||||
// Returns true if textures should be upload with a relative
|
||||
// offset - See UploadSurfaceToTexture.
|
||||
virtual bool FinishedSurfaceUpdate();
|
||||
|
||||
// Call after surface data has been uploaded to a texture.
|
||||
virtual void FinishedSurfaceUpload();
|
||||
|
||||
virtual bool InUpdate() const { return !!mUpdateSurface; }
|
||||
|
||||
virtual void Resize(const nsIntSize& aSize);
|
||||
|
||||
virtual void ApplyFilter();
|
||||
protected:
|
||||
|
||||
GLuint mTexture;
|
||||
TextureState mTextureState;
|
||||
GLContext* mGLContext;
|
||||
nsRefPtr<gfxASurface> mUpdateSurface;
|
||||
nsIntRegion mUpdateRegion;
|
||||
|
||||
// The offset into the update surface at which the update rect is located.
|
||||
nsIntPoint mUpdateOffset;
|
||||
};
|
||||
|
||||
/**
|
||||
* A container class that complements many sub TextureImages into a big TextureImage.
|
||||
* Aims to behave just like the real thing.
|
||||
*/
|
||||
|
||||
class TiledTextureImage
|
||||
: public TextureImage
|
||||
{
|
||||
public:
|
||||
TiledTextureImage(GLContext* aGL, nsIntSize aSize,
|
||||
TextureImage::ContentType, TextureImage::Flags aFlags = TextureImage::NoFlags);
|
||||
~TiledTextureImage();
|
||||
void DumpDiv();
|
||||
virtual gfxASurface* BeginUpdate(nsIntRegion& aRegion);
|
||||
virtual void GetUpdateRegion(nsIntRegion& aForRegion);
|
||||
virtual void EndUpdate();
|
||||
virtual void Resize(const nsIntSize& aSize);
|
||||
virtual uint32_t GetTileCount();
|
||||
virtual void BeginTileIteration();
|
||||
virtual bool NextTile();
|
||||
virtual void SetIterationCallback(TileIterationCallback aCallback,
|
||||
void* aCallbackData);
|
||||
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 bool InUpdate() const { return mInUpdate; }
|
||||
virtual void BindTexture(GLenum);
|
||||
virtual void ApplyFilter();
|
||||
|
||||
protected:
|
||||
virtual nsIntRect GetSrcTileRect();
|
||||
|
||||
unsigned int mCurrentImage;
|
||||
TileIterationCallback mIterationCallback;
|
||||
void* mIterationCallbackData;
|
||||
nsTArray< nsRefPtr<TextureImage> > mImages;
|
||||
bool mInUpdate;
|
||||
nsIntSize mSize;
|
||||
unsigned int mTileSize;
|
||||
unsigned int mRows, mColumns;
|
||||
GLContext* mGL;
|
||||
// A temporary surface to faciliate cross-tile updates.
|
||||
nsRefPtr<gfxASurface> mUpdateSurface;
|
||||
// The region of update requested
|
||||
nsIntRegion mUpdateRegion;
|
||||
TextureState mTextureState;
|
||||
};
|
||||
|
||||
struct THEBES_API ContextFormat
|
||||
{
|
||||
static const ContextFormat BasicRGBA32Format;
|
||||
@ -1927,12 +1566,7 @@ protected:
|
||||
GLenum aWrapMode,
|
||||
TextureImage::ContentType aContentType,
|
||||
GLContext* aContext,
|
||||
TextureImage::Flags aFlags = TextureImage::NoFlags)
|
||||
{
|
||||
nsRefPtr<BasicTextureImage> teximage(
|
||||
new BasicTextureImage(aTexture, aSize, aWrapMode, aContentType, aContext, aFlags));
|
||||
return teximage.forget();
|
||||
}
|
||||
TextureImage::Flags aFlags = TextureImage::NoFlags);
|
||||
|
||||
bool IsOffscreenSizeAllowed(const gfxIntSize& aSize) const {
|
||||
int32_t biggerDimension = NS_MAX(aSize.width, aSize.height);
|
||||
|
@ -1072,6 +1072,7 @@ bool GLContextEGL::AttachSharedHandle(SharedTextureShareType shareType,
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
GLContextEGL::BindTex2DOffscreen(GLContext *aOffscreen)
|
||||
{
|
||||
|
573
gfx/gl/GLTextureImage.cpp
Normal file
573
gfx/gl/GLTextureImage.cpp
Normal file
@ -0,0 +1,573 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "GLTextureImage.h"
|
||||
#include "GLContext.h"
|
||||
#include "gfxContext.h"
|
||||
#include "gfxPlatform.h"
|
||||
#include "gfxUtils.h"
|
||||
|
||||
using namespace mozilla::gl;
|
||||
|
||||
already_AddRefed<TextureImage>
|
||||
TextureImage::Create(GLContext* gl,
|
||||
const nsIntSize& size,
|
||||
TextureImage::ContentType contentType,
|
||||
GLenum wrapMode,
|
||||
TextureImage::Flags flags)
|
||||
{
|
||||
return gl->CreateTextureImage(size, contentType, wrapMode, flags);
|
||||
}
|
||||
|
||||
BasicTextureImage::~BasicTextureImage()
|
||||
{
|
||||
GLContext *ctx = mGLContext;
|
||||
if (ctx->IsDestroyed() || !ctx->IsOwningThreadCurrent()) {
|
||||
ctx = ctx->GetSharedContext();
|
||||
}
|
||||
|
||||
// If we have a context, then we need to delete the texture;
|
||||
// if we don't have a context (either real or shared),
|
||||
// then they went away when the contex was deleted, because it
|
||||
// was the only one that had access to it.
|
||||
if (ctx && !ctx->IsDestroyed()) {
|
||||
mGLContext->MakeCurrent();
|
||||
mGLContext->fDeleteTextures(1, &mTexture);
|
||||
}
|
||||
}
|
||||
|
||||
gfxASurface*
|
||||
BasicTextureImage::BeginUpdate(nsIntRegion& aRegion)
|
||||
{
|
||||
NS_ASSERTION(!mUpdateSurface, "BeginUpdate() without EndUpdate()?");
|
||||
|
||||
// determine the region the client will need to repaint
|
||||
if (mGLContext->CanUploadSubTextures()) {
|
||||
GetUpdateRegion(aRegion);
|
||||
} else {
|
||||
aRegion = nsIntRect(nsIntPoint(0, 0), mSize);
|
||||
}
|
||||
|
||||
mUpdateRegion = aRegion;
|
||||
|
||||
nsIntRect rgnSize = mUpdateRegion.GetBounds();
|
||||
if (!nsIntRect(nsIntPoint(0, 0), mSize).Contains(rgnSize)) {
|
||||
NS_ERROR("update outside of image");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ImageFormat format =
|
||||
(GetContentType() == gfxASurface::CONTENT_COLOR) ?
|
||||
gfxASurface::ImageFormatRGB24 : gfxASurface::ImageFormatARGB32;
|
||||
mUpdateSurface =
|
||||
GetSurfaceForUpdate(gfxIntSize(rgnSize.width, rgnSize.height), format);
|
||||
|
||||
if (!mUpdateSurface || mUpdateSurface->CairoStatus()) {
|
||||
mUpdateSurface = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mUpdateSurface->SetDeviceOffset(gfxPoint(-rgnSize.x, -rgnSize.y));
|
||||
|
||||
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()
|
||||
{
|
||||
NS_ASSERTION(!!mUpdateSurface, "EndUpdate() without BeginUpdate()?");
|
||||
|
||||
// FIXME: this is the slow boat. Make me fast (with GLXPixmap?).
|
||||
|
||||
// Undo the device offset that BeginUpdate set; doesn't much matter for us here,
|
||||
// but important if we ever do anything directly with the surface.
|
||||
mUpdateSurface->SetDeviceOffset(gfxPoint(0, 0));
|
||||
|
||||
bool relative = FinishedSurfaceUpdate();
|
||||
|
||||
mShaderType =
|
||||
mGLContext->UploadSurfaceToTexture(mUpdateSurface,
|
||||
mUpdateRegion,
|
||||
mTexture,
|
||||
mTextureState == Created,
|
||||
mUpdateOffset,
|
||||
relative);
|
||||
FinishedSurfaceUpload();
|
||||
|
||||
mUpdateSurface = nullptr;
|
||||
mTextureState = Valid;
|
||||
}
|
||||
|
||||
void
|
||||
BasicTextureImage::BindTexture(GLenum aTextureUnit)
|
||||
{
|
||||
mGLContext->fActiveTexture(aTextureUnit);
|
||||
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
|
||||
mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
|
||||
}
|
||||
|
||||
void
|
||||
BasicTextureImage::ApplyFilter()
|
||||
{
|
||||
mGLContext->ApplyFilterToBoundTexture(mFilter);
|
||||
}
|
||||
|
||||
|
||||
already_AddRefed<gfxASurface>
|
||||
BasicTextureImage::GetSurfaceForUpdate(const gfxIntSize& aSize, ImageFormat aFmt)
|
||||
{
|
||||
return gfxPlatform::GetPlatform()->
|
||||
CreateOffscreenSurface(aSize, gfxASurface::ContentFromFormat(aFmt));
|
||||
}
|
||||
|
||||
bool
|
||||
BasicTextureImage::FinishedSurfaceUpdate()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
BasicTextureImage::FinishedSurfaceUpload()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
BasicTextureImage::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);
|
||||
region = nsIntRegion(bounds);
|
||||
} else {
|
||||
region = aRegion;
|
||||
}
|
||||
|
||||
mShaderType =
|
||||
mGLContext->UploadSurfaceToTexture(aSurf,
|
||||
region,
|
||||
mTexture,
|
||||
mTextureState == Created,
|
||||
bounds.TopLeft() + aFrom,
|
||||
false);
|
||||
mTextureState = Valid;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
BasicTextureImage::Resize(const nsIntSize& aSize)
|
||||
{
|
||||
NS_ASSERTION(!mUpdateSurface, "Resize() while in update?");
|
||||
|
||||
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
|
||||
|
||||
mGLContext->fTexImage2D(LOCAL_GL_TEXTURE_2D,
|
||||
0,
|
||||
LOCAL_GL_RGBA,
|
||||
aSize.width,
|
||||
aSize.height,
|
||||
0,
|
||||
LOCAL_GL_RGBA,
|
||||
LOCAL_GL_UNSIGNED_BYTE,
|
||||
NULL);
|
||||
|
||||
mTextureState = Allocated;
|
||||
mSize = aSize;
|
||||
}
|
||||
|
||||
TiledTextureImage::TiledTextureImage(GLContext* aGL,
|
||||
nsIntSize aSize,
|
||||
TextureImage::ContentType aContentType,
|
||||
TextureImage::Flags aFlags)
|
||||
: TextureImage(aSize, LOCAL_GL_CLAMP_TO_EDGE, aContentType, aFlags)
|
||||
, mCurrentImage(0)
|
||||
, mIterationCallback(nullptr)
|
||||
, mInUpdate(false)
|
||||
, mRows(0)
|
||||
, mColumns(0)
|
||||
, mGL(aGL)
|
||||
, mTextureState(Created)
|
||||
{
|
||||
mTileSize = (!(aFlags & TextureImage::ForceSingleTile) && mGL->WantsSmallTiles())
|
||||
? 256 : mGL->GetMaxTextureSize();
|
||||
if (aSize != nsIntSize(0,0)) {
|
||||
Resize(aSize);
|
||||
}
|
||||
}
|
||||
|
||||
TiledTextureImage::~TiledTextureImage()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
TiledTextureImage::DirectUpdate(gfxASurface* aSurf, const nsIntRegion& aRegion, const nsIntPoint& aFrom /* = nsIntPoint(0, 0) */)
|
||||
{
|
||||
nsIntRegion region;
|
||||
|
||||
if (mTextureState != Valid) {
|
||||
nsIntRect bounds = nsIntRect(0, 0, mSize.width, mSize.height);
|
||||
region = nsIntRegion(bounds);
|
||||
} else {
|
||||
region = aRegion;
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
int oldCurrentImage = mCurrentImage;
|
||||
BeginTileIteration();
|
||||
do {
|
||||
nsIntRect tileRect = GetSrcTileRect();
|
||||
int xPos = tileRect.x;
|
||||
int yPos = tileRect.y;
|
||||
|
||||
nsIntRegion tileRegion;
|
||||
tileRegion.And(region, tileRect); // intersect with tile
|
||||
|
||||
if (tileRegion.IsEmpty())
|
||||
continue;
|
||||
|
||||
if (mGL->CanUploadSubTextures()) {
|
||||
tileRegion.MoveBy(-xPos, -yPos); // translate into tile local space
|
||||
} else {
|
||||
// If sub-textures are unsupported, expand to tile boundaries
|
||||
tileRect.x = tileRect.y = 0;
|
||||
tileRegion = nsIntRegion(tileRect);
|
||||
}
|
||||
|
||||
result &= mImages[mCurrentImage]->
|
||||
DirectUpdate(aSurf, tileRegion, aFrom + nsIntPoint(xPos, yPos));
|
||||
|
||||
if (mCurrentImage == mImages.Length() - 1) {
|
||||
// We know we're done, but we still need to ensure that the callback
|
||||
// gets called (e.g. to update the uploaded region).
|
||||
NextTile();
|
||||
break;
|
||||
}
|
||||
// Override a callback cancelling iteration if the texture wasn't valid.
|
||||
// We need to force the update in that situation, or we may end up
|
||||
// showing invalid/out-of-date texture data.
|
||||
} while (NextTile() || (mTextureState != Valid));
|
||||
mCurrentImage = oldCurrentImage;
|
||||
|
||||
mShaderType = mImages[0]->GetShaderProgramType();
|
||||
mTextureState = Valid;
|
||||
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 = 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
|
||||
aRegion = nsIntRect(nsIntPoint(0, 0), mSize);
|
||||
}
|
||||
|
||||
nsIntRect bounds = aRegion.GetBounds();
|
||||
|
||||
for (unsigned i = 0; i < mImages.Length(); i++) {
|
||||
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
|
||||
if (imageRegion.Contains(aRegion)) {
|
||||
// adjust for tile offset
|
||||
aRegion.MoveBy(-xPos, -yPos);
|
||||
// forward the actual call
|
||||
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 = nullptr;
|
||||
// remember which image to EndUpdate
|
||||
mCurrentImage = i;
|
||||
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;
|
||||
mUpdateSurface = gfxPlatform::GetPlatform()->
|
||||
CreateOffscreenSurface(gfxIntSize(bounds.width, bounds.height), gfxASurface::ContentFromFormat(format));
|
||||
mUpdateSurface->SetDeviceOffset(gfxPoint(-bounds.x, -bounds.y));
|
||||
|
||||
return mUpdateSurface;
|
||||
}
|
||||
|
||||
void
|
||||
TiledTextureImage::EndUpdate()
|
||||
{
|
||||
NS_ASSERTION(mInUpdate, "EndUpdate not in update");
|
||||
if (!mUpdateSurface) { // update was to a single TextureImage
|
||||
mImages[mCurrentImage]->EndUpdate();
|
||||
mInUpdate = false;
|
||||
mTextureState = Valid;
|
||||
mShaderType = mImages[mCurrentImage]->GetShaderProgramType();
|
||||
return;
|
||||
}
|
||||
|
||||
// upload tiles from temp surface
|
||||
for (unsigned i = 0; i < mImages.Length(); i++) {
|
||||
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())
|
||||
continue;
|
||||
subregion.MoveBy(-xPos, -yPos); // Tile-local space
|
||||
// copy tile from temp surface
|
||||
gfxASurface* surf = mImages[i]->BeginUpdate(subregion);
|
||||
nsRefPtr<gfxContext> ctx = new gfxContext(surf);
|
||||
gfxUtils::ClipToRegion(ctx, subregion);
|
||||
ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
|
||||
ctx->SetSource(mUpdateSurface, gfxPoint(-xPos, -yPos));
|
||||
ctx->Paint();
|
||||
mImages[i]->EndUpdate();
|
||||
}
|
||||
|
||||
mUpdateSurface = nullptr;
|
||||
mInUpdate = false;
|
||||
mShaderType = mImages[0]->GetShaderProgramType();
|
||||
mTextureState = Valid;
|
||||
}
|
||||
|
||||
void TiledTextureImage::BeginTileIteration()
|
||||
{
|
||||
mCurrentImage = 0;
|
||||
}
|
||||
|
||||
bool TiledTextureImage::NextTile()
|
||||
{
|
||||
bool continueIteration = true;
|
||||
|
||||
if (mIterationCallback)
|
||||
continueIteration = mIterationCallback(this, mCurrentImage,
|
||||
mIterationCallbackData);
|
||||
|
||||
if (mCurrentImage + 1 < mImages.Length()) {
|
||||
mCurrentImage++;
|
||||
return continueIteration;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TiledTextureImage::SetIterationCallback(TileIterationCallback aCallback,
|
||||
void* aCallbackData)
|
||||
{
|
||||
mIterationCallback = aCallback;
|
||||
mIterationCallbackData = aCallbackData;
|
||||
}
|
||||
|
||||
nsIntRect TiledTextureImage::GetTileRect()
|
||||
{
|
||||
nsIntRect rect = mImages[mCurrentImage]->GetTileRect();
|
||||
unsigned int xPos = (mCurrentImage % mColumns) * mTileSize;
|
||||
unsigned int yPos = (mCurrentImage / mColumns) * mTileSize;
|
||||
rect.MoveBy(xPos, yPos);
|
||||
return rect;
|
||||
}
|
||||
|
||||
nsIntRect TiledTextureImage::GetSrcTileRect()
|
||||
{
|
||||
nsIntRect rect = GetTileRect();
|
||||
unsigned int srcY = mFlags & NeedsYFlip
|
||||
? mSize.height - rect.height - rect.y
|
||||
: rect.y;
|
||||
return nsIntRect(rect.x, srcY, rect.width, rect.height);
|
||||
}
|
||||
|
||||
void
|
||||
TiledTextureImage::BindTexture(GLenum aTextureUnit)
|
||||
{
|
||||
mImages[mCurrentImage]->BindTexture(aTextureUnit);
|
||||
}
|
||||
|
||||
void
|
||||
TiledTextureImage::ApplyFilter()
|
||||
{
|
||||
mGL->ApplyFilterToBoundTexture(mFilter);
|
||||
}
|
||||
|
||||
/*
|
||||
* Resize, trying to reuse tiles. The reuse strategy is to decide on reuse per
|
||||
* column. A tile on a column is reused if it hasn't changed size, otherwise it
|
||||
* is discarded/replaced. Extra tiles on a column are pruned after iterating
|
||||
* each column, and extra rows are pruned after iteration over the entire image
|
||||
* finishes.
|
||||
*/
|
||||
void TiledTextureImage::Resize(const nsIntSize& aSize)
|
||||
{
|
||||
if (mSize == aSize && mTextureState != Created) {
|
||||
return;
|
||||
}
|
||||
|
||||
// calculate rows and columns, rounding up
|
||||
unsigned int columns = (aSize.width + mTileSize - 1) / mTileSize;
|
||||
unsigned int rows = (aSize.height + mTileSize - 1) / mTileSize;
|
||||
|
||||
// Iterate over old tile-store and insert/remove tiles as necessary
|
||||
int row;
|
||||
unsigned int i = 0;
|
||||
for (row = 0; row < (int)rows; row++) {
|
||||
// If we've gone beyond how many rows there were before, set mColumns to
|
||||
// zero so that we only create new tiles.
|
||||
if (row >= (int)mRows)
|
||||
mColumns = 0;
|
||||
|
||||
// Similarly, if we're on the last row of old tiles and the height has
|
||||
// changed, discard all tiles in that row.
|
||||
// This will cause the pruning of columns not to work, but we don't need
|
||||
// to worry about that, as no more tiles will be reused past this point
|
||||
// anyway.
|
||||
if ((row == (int)mRows - 1) && (aSize.height != mSize.height))
|
||||
mColumns = 0;
|
||||
|
||||
int col;
|
||||
for (col = 0; col < (int)columns; col++) {
|
||||
nsIntSize size( // use tilesize first, then the remainder
|
||||
(col+1) * mTileSize > (unsigned int)aSize.width ? aSize.width % mTileSize : mTileSize,
|
||||
(row+1) * mTileSize > (unsigned int)aSize.height ? aSize.height % mTileSize : mTileSize);
|
||||
|
||||
bool replace = false;
|
||||
|
||||
// Check if we can re-use old tiles.
|
||||
if (col < (int)mColumns) {
|
||||
// Reuse an existing tile. If the tile is an end-tile and the
|
||||
// width differs, replace it instead.
|
||||
if (mSize.width != aSize.width) {
|
||||
if (col == (int)mColumns - 1) {
|
||||
// Tile at the end of the old column, replace it with
|
||||
// a new one.
|
||||
replace = true;
|
||||
} else if (col == (int)columns - 1) {
|
||||
// Tile at the end of the new column, create a new one.
|
||||
} else {
|
||||
// Before the last column on both the old and new sizes,
|
||||
// reuse existing tile.
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// Width hasn't changed, reuse existing tile.
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new tile.
|
||||
nsRefPtr<TextureImage> teximg =
|
||||
mGL->TileGenFunc(size, mContentType, mFlags);
|
||||
if (replace)
|
||||
mImages.ReplaceElementAt(i, teximg.forget());
|
||||
else
|
||||
mImages.InsertElementAt(i, teximg.forget());
|
||||
i++;
|
||||
}
|
||||
|
||||
// Prune any unused tiles on the end of the column.
|
||||
if (row < (int)mRows) {
|
||||
for (col = (int)mColumns - col; col > 0; col--) {
|
||||
mImages.RemoveElementAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prune any unused tiles at the end of the store.
|
||||
unsigned int length = mImages.Length();
|
||||
for (; i < length; i++)
|
||||
mImages.RemoveElementAt(mImages.Length()-1);
|
||||
|
||||
// Reset tile-store properties.
|
||||
mRows = rows;
|
||||
mColumns = columns;
|
||||
mSize = aSize;
|
||||
mTextureState = Allocated;
|
||||
mCurrentImage = 0;
|
||||
}
|
||||
|
||||
uint32_t TiledTextureImage::GetTileCount()
|
||||
{
|
||||
return mImages.Length();
|
||||
}
|
||||
|
||||
TextureImage::ScopedBindTexture::ScopedBindTexture(TextureImage* aTexture,
|
||||
GLenum aTextureUnit)
|
||||
: mTexture(aTexture)
|
||||
{
|
||||
if (mTexture) {
|
||||
MOZ_ASSERT(aTextureUnit >= LOCAL_GL_TEXTURE0);
|
||||
mTexture->BindTexture(aTextureUnit);
|
||||
}
|
||||
}
|
385
gfx/gl/GLTextureImage.h
Normal file
385
gfx/gl/GLTextureImage.h
Normal file
@ -0,0 +1,385 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef GLTEXTUREIMAGE_H_
|
||||
#define GLTEXTUREIMAGE_H_
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsRegion.h"
|
||||
#include "gfxASurface.h"
|
||||
#include "GLContextTypes.h"
|
||||
#include "gfxPattern.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gl {
|
||||
class GLContext;
|
||||
|
||||
/**
|
||||
* A TextureImage encapsulates a surface that can be drawn to by a
|
||||
* Thebes gfxContext and (hopefully efficiently!) synchronized to a
|
||||
* texture in the server. TextureImages are associated with one and
|
||||
* only one GLContext.
|
||||
*
|
||||
* Implementation note: TextureImages attempt to unify two categories
|
||||
* of backends
|
||||
*
|
||||
* (1) proxy to server-side object that can be bound to a texture;
|
||||
* e.g. Pixmap on X11.
|
||||
*
|
||||
* (2) efficient manager of texture memory; e.g. by having clients draw
|
||||
* into a scratch buffer which is then uploaded with
|
||||
* glTexSubImage2D().
|
||||
*/
|
||||
class TextureImage
|
||||
{
|
||||
NS_INLINE_DECL_REFCOUNTING(TextureImage)
|
||||
public:
|
||||
enum TextureState
|
||||
{
|
||||
Created, // Texture created, but has not had glTexImage called to initialize it.
|
||||
Allocated, // Texture memory exists, but contents are invalid.
|
||||
Valid // Texture fully ready to use.
|
||||
};
|
||||
|
||||
enum Flags {
|
||||
NoFlags = 0x0,
|
||||
UseNearestFilter = 0x1,
|
||||
NeedsYFlip = 0x2,
|
||||
ForceSingleTile = 0x4
|
||||
};
|
||||
|
||||
typedef gfxASurface::gfxContentType ContentType;
|
||||
|
||||
static already_AddRefed<TextureImage> Create(
|
||||
GLContext* gl,
|
||||
const nsIntSize& aSize,
|
||||
TextureImage::ContentType aContentType,
|
||||
GLenum aWrapMode,
|
||||
TextureImage::Flags aFlags = TextureImage::NoFlags);
|
||||
|
||||
virtual ~TextureImage() {}
|
||||
|
||||
/**
|
||||
* Returns a gfxASurface for updating |aRegion| of the client's
|
||||
* image if successul, NULL if not. |aRegion|'s bounds must fit
|
||||
* within Size(); its coordinate space (if any) is ignored. If
|
||||
* the update begins successfully, the returned gfxASurface is
|
||||
* owned by this. Otherwise, NULL is returned.
|
||||
*
|
||||
* |aRegion| is an inout param: the returned region is what the
|
||||
* client must repaint. Category (1) regions above can
|
||||
* efficiently handle repaints to "scattered" regions, while (2)
|
||||
* can only efficiently handle repaints to rects.
|
||||
*
|
||||
* Painting the returned surface outside of |aRegion| results
|
||||
* in undefined behavior.
|
||||
*
|
||||
* BeginUpdate() calls cannot be "nested", and each successful
|
||||
* BeginUpdate() must be followed by exactly one EndUpdate() (see
|
||||
* below). Failure to do so can leave this in a possibly
|
||||
* inconsistent state. Unsuccessful BeginUpdate()s must not be
|
||||
* 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.
|
||||
*
|
||||
* BeginUpdate() must have been called exactly once before
|
||||
* EndUpdate().
|
||||
*/
|
||||
virtual void EndUpdate() = 0;
|
||||
|
||||
/**
|
||||
* The Image may contain several textures for different regions (tiles).
|
||||
* These functions iterate over each sub texture image tile.
|
||||
*/
|
||||
virtual void BeginTileIteration() {
|
||||
}
|
||||
|
||||
virtual bool NextTile() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Function prototype for a tile iteration callback. Returning false will
|
||||
// cause iteration to be interrupted (i.e. the corresponding NextTile call
|
||||
// will return false).
|
||||
typedef bool (* TileIterationCallback)(TextureImage* aImage,
|
||||
int aTileNumber,
|
||||
void* aCallbackData);
|
||||
|
||||
// Sets a callback to be called every time NextTile is called.
|
||||
virtual void SetIterationCallback(TileIterationCallback aCallback,
|
||||
void* aCallbackData) {
|
||||
}
|
||||
|
||||
virtual nsIntRect GetTileRect() {
|
||||
return nsIntRect(nsIntPoint(0,0), mSize);
|
||||
}
|
||||
|
||||
virtual GLuint GetTextureID() = 0;
|
||||
|
||||
virtual uint32_t 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.
|
||||
*/
|
||||
virtual void Resize(const nsIntSize& aSize) {
|
||||
mSize = aSize;
|
||||
nsIntRegion r(nsIntRect(0, 0, aSize.width, aSize.height));
|
||||
BeginUpdate(r);
|
||||
EndUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this texture as having valid contents. Call this after modifying
|
||||
* the texture contents externally.
|
||||
*/
|
||||
virtual void MarkValid() {}
|
||||
|
||||
/**
|
||||
* aSurf - the source surface to update from
|
||||
* aRegion - the region in this image to update
|
||||
* aFrom - offset in the source to update from
|
||||
*/
|
||||
virtual bool DirectUpdate(gfxASurface *aSurf, const nsIntRegion& aRegion, const nsIntPoint& aFrom = nsIntPoint(0,0)) = 0;
|
||||
|
||||
virtual void BindTexture(GLenum aTextureUnit) = 0;
|
||||
virtual void ReleaseTexture() {}
|
||||
|
||||
void BindTextureAndApplyFilter(GLenum aTextureUnit) {
|
||||
BindTexture(aTextureUnit);
|
||||
ApplyFilter();
|
||||
}
|
||||
|
||||
class ScopedBindTexture
|
||||
{
|
||||
public:
|
||||
ScopedBindTexture(TextureImage *aTexture, GLenum aTextureUnit);
|
||||
|
||||
~ScopedBindTexture()
|
||||
{
|
||||
if (mTexture) {
|
||||
mTexture->ReleaseTexture();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
TextureImage *mTexture;
|
||||
};
|
||||
|
||||
class ScopedBindTextureAndApplyFilter
|
||||
: public ScopedBindTexture
|
||||
{
|
||||
public:
|
||||
ScopedBindTextureAndApplyFilter(TextureImage *aTexture, GLenum aTextureUnit) :
|
||||
ScopedBindTexture(aTexture, aTextureUnit)
|
||||
{
|
||||
if (mTexture) {
|
||||
mTexture->ApplyFilter();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the shader program type that should be used to render
|
||||
* this texture. Only valid after a matching BeginUpdate/EndUpdate
|
||||
* pair have been called.
|
||||
*/
|
||||
virtual ShaderProgramType GetShaderProgramType()
|
||||
{
|
||||
return mShaderType;
|
||||
}
|
||||
|
||||
/** Can be called safely at any time. */
|
||||
|
||||
/**
|
||||
* If this TextureImage has a permanent gfxASurface backing,
|
||||
* return it. Otherwise return NULL.
|
||||
*/
|
||||
virtual already_AddRefed<gfxASurface> GetBackingSurface()
|
||||
{ return NULL; }
|
||||
|
||||
const nsIntSize& GetSize() const { return mSize; }
|
||||
ContentType GetContentType() const { return mContentType; }
|
||||
virtual bool InUpdate() const = 0;
|
||||
GLenum GetWrapMode() const { return mWrapMode; }
|
||||
|
||||
void SetFilter(gfxPattern::GraphicsFilter aFilter) { mFilter = aFilter; }
|
||||
|
||||
/**
|
||||
* Applies this TextureImage's filter, assuming that its texture is
|
||||
* the currently bound texture.
|
||||
*/
|
||||
virtual void ApplyFilter() = 0;
|
||||
|
||||
protected:
|
||||
friend class GLContext;
|
||||
|
||||
/**
|
||||
* After the ctor, the TextureImage is invalid. Implementations
|
||||
* must allocate resources successfully before returning the new
|
||||
* TextureImage from GLContext::CreateTextureImage(). That is,
|
||||
* clients must not be given partially-constructed TextureImages.
|
||||
*/
|
||||
TextureImage(const nsIntSize& aSize,
|
||||
GLenum aWrapMode, ContentType aContentType,
|
||||
Flags aFlags = NoFlags)
|
||||
: mSize(aSize)
|
||||
, mWrapMode(aWrapMode)
|
||||
, mContentType(aContentType)
|
||||
, mFilter(gfxPattern::FILTER_GOOD)
|
||||
, mFlags(aFlags)
|
||||
{}
|
||||
|
||||
virtual nsIntRect GetSrcTileRect() {
|
||||
return nsIntRect(nsIntPoint(0,0), mSize);
|
||||
}
|
||||
|
||||
nsIntSize mSize;
|
||||
GLenum mWrapMode;
|
||||
ContentType mContentType;
|
||||
ShaderProgramType mShaderType;
|
||||
gfxPattern::GraphicsFilter mFilter;
|
||||
Flags mFlags;
|
||||
};
|
||||
|
||||
/**
|
||||
* BasicTextureImage is the baseline TextureImage implementation ---
|
||||
* it updates its texture by allocating a scratch buffer for the
|
||||
* client to draw into, then using glTexSubImage2D() to upload the new
|
||||
* pixels. Platforms must provide the code to create a new surface
|
||||
* into which the updated pixels will be drawn, and the code to
|
||||
* convert the update surface's pixels into an image on which we can
|
||||
* glTexSubImage2D().
|
||||
*/
|
||||
class BasicTextureImage
|
||||
: public TextureImage
|
||||
{
|
||||
public:
|
||||
typedef gfxASurface::gfxImageFormat ImageFormat;
|
||||
virtual ~BasicTextureImage();
|
||||
|
||||
BasicTextureImage(GLuint aTexture,
|
||||
const nsIntSize& aSize,
|
||||
GLenum aWrapMode,
|
||||
ContentType aContentType,
|
||||
GLContext* aContext,
|
||||
TextureImage::Flags aFlags = TextureImage::NoFlags)
|
||||
: TextureImage(aSize, aWrapMode, aContentType, aFlags)
|
||||
, mTexture(aTexture)
|
||||
, mTextureState(Created)
|
||||
, mGLContext(aContext)
|
||||
, mUpdateOffset(0, 0)
|
||||
{}
|
||||
|
||||
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; }
|
||||
// Returns a surface to draw into
|
||||
virtual already_AddRefed<gfxASurface>
|
||||
GetSurfaceForUpdate(const gfxIntSize& aSize, ImageFormat aFmt);
|
||||
|
||||
virtual void MarkValid() { mTextureState = Valid; }
|
||||
|
||||
// Call when drawing into the update surface is complete.
|
||||
// Returns true if textures should be upload with a relative
|
||||
// offset - See UploadSurfaceToTexture.
|
||||
virtual bool FinishedSurfaceUpdate();
|
||||
|
||||
// Call after surface data has been uploaded to a texture.
|
||||
virtual void FinishedSurfaceUpload();
|
||||
|
||||
virtual bool InUpdate() const { return !!mUpdateSurface; }
|
||||
|
||||
virtual void Resize(const nsIntSize& aSize);
|
||||
|
||||
virtual void ApplyFilter();
|
||||
protected:
|
||||
|
||||
GLuint mTexture;
|
||||
TextureState mTextureState;
|
||||
GLContext* mGLContext;
|
||||
nsRefPtr<gfxASurface> mUpdateSurface;
|
||||
nsIntRegion mUpdateRegion;
|
||||
|
||||
// The offset into the update surface at which the update rect is located.
|
||||
nsIntPoint mUpdateOffset;
|
||||
};
|
||||
|
||||
/**
|
||||
* A container class that complements many sub TextureImages into a big TextureImage.
|
||||
* Aims to behave just like the real thing.
|
||||
*/
|
||||
|
||||
class TiledTextureImage
|
||||
: public TextureImage
|
||||
{
|
||||
public:
|
||||
TiledTextureImage(GLContext* aGL, nsIntSize aSize,
|
||||
TextureImage::ContentType, TextureImage::Flags aFlags = TextureImage::NoFlags);
|
||||
~TiledTextureImage();
|
||||
void DumpDiv();
|
||||
virtual gfxASurface* BeginUpdate(nsIntRegion& aRegion);
|
||||
virtual void GetUpdateRegion(nsIntRegion& aForRegion);
|
||||
virtual void EndUpdate();
|
||||
virtual void Resize(const nsIntSize& aSize);
|
||||
virtual uint32_t GetTileCount();
|
||||
virtual void BeginTileIteration();
|
||||
virtual bool NextTile();
|
||||
virtual void SetIterationCallback(TileIterationCallback aCallback,
|
||||
void* aCallbackData);
|
||||
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 bool InUpdate() const { return mInUpdate; }
|
||||
virtual void BindTexture(GLenum);
|
||||
virtual void ApplyFilter();
|
||||
|
||||
protected:
|
||||
virtual nsIntRect GetSrcTileRect();
|
||||
|
||||
unsigned int mCurrentImage;
|
||||
TileIterationCallback mIterationCallback;
|
||||
void* mIterationCallbackData;
|
||||
nsTArray< nsRefPtr<TextureImage> > mImages;
|
||||
bool mInUpdate;
|
||||
nsIntSize mSize;
|
||||
unsigned int mTileSize;
|
||||
unsigned int mRows, mColumns;
|
||||
GLContext* mGL;
|
||||
// A temporary surface to faciliate cross-tile updates.
|
||||
nsRefPtr<gfxASurface> mUpdateSurface;
|
||||
// The region of update requested
|
||||
nsIntRegion mUpdateRegion;
|
||||
TextureState mTextureState;
|
||||
};
|
||||
|
||||
} // namespace gl
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* GLTEXTUREIMAGE_H_ */
|
@ -24,6 +24,7 @@ EXPORTS = \
|
||||
GLContextProviderImpl.h \
|
||||
GLLibraryLoader.h \
|
||||
ForceDiscreteGPUHelperCGL.h \
|
||||
GLTextureImage.h \
|
||||
$(NULL)
|
||||
|
||||
ifdef MOZ_X11
|
||||
@ -48,6 +49,7 @@ CPPSRCS = \
|
||||
GLContext.cpp \
|
||||
GLContextUtils.cpp \
|
||||
GLLibraryLoader.cpp \
|
||||
GLTextureImage.cpp \
|
||||
$(NULL)
|
||||
|
||||
GL_PROVIDER = Null
|
||||
|
@ -51,6 +51,7 @@
|
||||
#include "nsRegion.h"
|
||||
#include "Layers.h"
|
||||
#include "LayerManagerOGL.h"
|
||||
#include "GLTextureImage.h"
|
||||
#include "mozilla/layers/CompositorCocoaWidgetHelper.h"
|
||||
#ifdef ACCESSIBILITY
|
||||
#include "nsAccessibilityService.h"
|
||||
@ -1758,10 +1759,11 @@ nsChildView::DrawWindowOverlay(LayerManager* aManager, nsIntRect aRect)
|
||||
}
|
||||
|
||||
if (!mResizerImage) {
|
||||
mResizerImage = manager->gl()->CreateTextureImage(nsIntSize(15, 15),
|
||||
gfxASurface::CONTENT_COLOR_ALPHA,
|
||||
LOCAL_GL_CLAMP_TO_EDGE,
|
||||
TextureImage::UseNearestFilter);
|
||||
mResizerImage = TextureImage::Create(manager->gl(),
|
||||
nsIntSize(15, 15),
|
||||
gfxASurface::CONTENT_COLOR_ALPHA,
|
||||
LOCAL_GL_CLAMP_TO_EDGE,
|
||||
TextureImage::UseNearestFilter);
|
||||
|
||||
// Creation of texture images can fail.
|
||||
if (!mResizerImage)
|
||||
|
Loading…
Reference in New Issue
Block a user