michael@0: /* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "GLTextureImage.h" michael@0: #include "GLContext.h" michael@0: #include "gfxContext.h" michael@0: #include "gfxPlatform.h" michael@0: #include "gfxUtils.h" michael@0: #include "gfx2DGlue.h" michael@0: #include "ScopedGLHelpers.h" michael@0: #include "GLUploadHelpers.h" michael@0: michael@0: #include "TextureImageEGL.h" michael@0: #ifdef XP_MACOSX michael@0: #include "TextureImageCGL.h" michael@0: #endif michael@0: michael@0: namespace mozilla { michael@0: namespace gl { michael@0: michael@0: already_AddRefed michael@0: CreateTextureImage(GLContext* gl, michael@0: const gfx::IntSize& aSize, michael@0: TextureImage::ContentType aContentType, michael@0: GLenum aWrapMode, michael@0: TextureImage::Flags aFlags, michael@0: TextureImage::ImageFormat aImageFormat) michael@0: { michael@0: switch (gl->GetContextType()) { michael@0: #ifdef XP_MACOSX michael@0: case GLContextType::CGL: michael@0: return CreateTextureImageCGL(gl, aSize, aContentType, aWrapMode, aFlags, aImageFormat); michael@0: #endif michael@0: case GLContextType::EGL: michael@0: return CreateTextureImageEGL(gl, aSize, aContentType, aWrapMode, aFlags, aImageFormat); michael@0: default: michael@0: return CreateBasicTextureImage(gl, aSize, aContentType, aWrapMode, aFlags, aImageFormat); michael@0: } michael@0: } michael@0: michael@0: michael@0: static already_AddRefed michael@0: TileGenFunc(GLContext* gl, michael@0: const nsIntSize& aSize, michael@0: TextureImage::ContentType aContentType, michael@0: TextureImage::Flags aFlags, michael@0: TextureImage::ImageFormat aImageFormat) michael@0: { michael@0: switch (gl->GetContextType()) { michael@0: #ifdef XP_MACOSX michael@0: case GLContextType::CGL: michael@0: return TileGenFuncCGL(gl, aSize, aContentType, aFlags, aImageFormat); michael@0: #endif michael@0: case GLContextType::EGL: michael@0: return TileGenFuncEGL(gl, aSize, aContentType, aFlags, aImageFormat); michael@0: default: michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: already_AddRefed michael@0: TextureImage::Create(GLContext* gl, michael@0: const nsIntSize& size, michael@0: TextureImage::ContentType contentType, michael@0: GLenum wrapMode, michael@0: TextureImage::Flags flags) michael@0: { michael@0: return Create(gl, size.ToIntSize(), contentType, wrapMode, flags); michael@0: } michael@0: michael@0: // Moz2D equivalent... michael@0: already_AddRefed michael@0: TextureImage::Create(GLContext* gl, michael@0: const gfx::IntSize& size, michael@0: TextureImage::ContentType contentType, michael@0: GLenum wrapMode, michael@0: TextureImage::Flags flags) michael@0: { michael@0: return CreateTextureImage(gl, size, contentType, wrapMode, flags); michael@0: } michael@0: michael@0: bool michael@0: TextureImage::UpdateFromDataSource(gfx::DataSourceSurface *aSurface, michael@0: const nsIntRegion* aDestRegion, michael@0: const gfx::IntPoint* aSrcPoint) michael@0: { michael@0: nsIntRegion destRegion = aDestRegion ? *aDestRegion michael@0: : nsIntRect(0, 0, michael@0: aSurface->GetSize().width, michael@0: aSurface->GetSize().height); michael@0: gfx::IntPoint srcPoint = aSrcPoint ? *aSrcPoint michael@0: : gfx::IntPoint(0, 0); michael@0: return DirectUpdate(aSurface, destRegion, srcPoint); michael@0: } michael@0: michael@0: gfx::IntRect TextureImage::GetTileRect() { michael@0: return gfx::IntRect(gfx::IntPoint(0,0), mSize); michael@0: } michael@0: michael@0: gfx::IntRect TextureImage::GetSrcTileRect() { michael@0: return GetTileRect(); michael@0: } michael@0: michael@0: BasicTextureImage::~BasicTextureImage() michael@0: { michael@0: GLContext *ctx = mGLContext; michael@0: if (ctx->IsDestroyed() || !ctx->IsOwningThreadCurrent()) { michael@0: ctx = ctx->GetSharedContext(); michael@0: } michael@0: michael@0: // If we have a context, then we need to delete the texture; michael@0: // if we don't have a context (either real or shared), michael@0: // then they went away when the contex was deleted, because it michael@0: // was the only one that had access to it. michael@0: if (ctx && ctx->MakeCurrent()) { michael@0: ctx->fDeleteTextures(1, &mTexture); michael@0: } michael@0: } michael@0: michael@0: gfx::DrawTarget* michael@0: BasicTextureImage::BeginUpdate(nsIntRegion& aRegion) michael@0: { michael@0: NS_ASSERTION(!mUpdateDrawTarget, "BeginUpdate() without EndUpdate()?"); michael@0: michael@0: // determine the region the client will need to repaint michael@0: if (CanUploadSubTextures(mGLContext)) { michael@0: GetUpdateRegion(aRegion); michael@0: } else { michael@0: aRegion = nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize)); michael@0: } michael@0: michael@0: mUpdateRegion = aRegion; michael@0: michael@0: nsIntRect rgnSize = mUpdateRegion.GetBounds(); michael@0: if (!nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize)).Contains(rgnSize)) { michael@0: NS_ERROR("update outside of image"); michael@0: return nullptr; michael@0: } michael@0: michael@0: gfx::SurfaceFormat format = michael@0: (GetContentType() == gfxContentType::COLOR) ? michael@0: gfx::SurfaceFormat::B8G8R8X8 : gfx::SurfaceFormat::B8G8R8A8; michael@0: mUpdateDrawTarget = michael@0: GetDrawTargetForUpdate(gfx::IntSize(rgnSize.width, rgnSize.height), format); michael@0: michael@0: return mUpdateDrawTarget; michael@0: } michael@0: michael@0: void michael@0: BasicTextureImage::GetUpdateRegion(nsIntRegion& aForRegion) michael@0: { michael@0: // if the texture hasn't been initialized yet, or something important michael@0: // changed, we need to recreate our backing surface and force the michael@0: // client to paint everything michael@0: if (mTextureState != Valid) michael@0: aForRegion = nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize)); michael@0: } michael@0: michael@0: void michael@0: BasicTextureImage::EndUpdate() michael@0: { michael@0: NS_ASSERTION(!!mUpdateDrawTarget, "EndUpdate() without BeginUpdate()?"); michael@0: michael@0: // FIXME: this is the slow boat. Make me fast (with GLXPixmap?). michael@0: michael@0: RefPtr updateSnapshot = mUpdateDrawTarget->Snapshot(); michael@0: RefPtr updateData = updateSnapshot->GetDataSurface(); michael@0: michael@0: bool relative = FinishedSurfaceUpdate(); michael@0: michael@0: mTextureFormat = michael@0: UploadSurfaceToTexture(mGLContext, michael@0: updateData, michael@0: mUpdateRegion, michael@0: mTexture, michael@0: mTextureState == Created, michael@0: mUpdateOffset, michael@0: relative); michael@0: FinishedSurfaceUpload(); michael@0: michael@0: mUpdateDrawTarget = nullptr; michael@0: mTextureState = Valid; michael@0: } michael@0: michael@0: void michael@0: BasicTextureImage::BindTexture(GLenum aTextureUnit) michael@0: { michael@0: mGLContext->fActiveTexture(aTextureUnit); michael@0: mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture); michael@0: mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0); michael@0: } michael@0: michael@0: TemporaryRef michael@0: BasicTextureImage::GetDrawTargetForUpdate(const gfx::IntSize& aSize, gfx::SurfaceFormat aFmt) michael@0: { michael@0: return gfx::Factory::CreateDrawTarget(gfx::BackendType::CAIRO, aSize, aFmt); michael@0: } michael@0: michael@0: bool michael@0: BasicTextureImage::FinishedSurfaceUpdate() michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: BasicTextureImage::FinishedSurfaceUpload() michael@0: { michael@0: } michael@0: michael@0: bool michael@0: BasicTextureImage::DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion, const gfx::IntPoint& aFrom /* = gfx::IntPoint(0, 0) */) michael@0: { michael@0: nsIntRect bounds = aRegion.GetBounds(); michael@0: nsIntRegion region; michael@0: if (mTextureState != Valid) { michael@0: bounds = nsIntRect(0, 0, mSize.width, mSize.height); michael@0: region = nsIntRegion(bounds); michael@0: } else { michael@0: region = aRegion; michael@0: } michael@0: michael@0: mTextureFormat = michael@0: UploadSurfaceToTexture(mGLContext, michael@0: aSurf, michael@0: region, michael@0: mTexture, michael@0: mTextureState == Created, michael@0: bounds.TopLeft() + nsIntPoint(aFrom.x, aFrom.y), michael@0: false); michael@0: mTextureState = Valid; michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: BasicTextureImage::Resize(const gfx::IntSize& aSize) michael@0: { michael@0: NS_ASSERTION(!mUpdateDrawTarget, "Resize() while in update?"); michael@0: michael@0: mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture); michael@0: michael@0: mGLContext->fTexImage2D(LOCAL_GL_TEXTURE_2D, michael@0: 0, michael@0: LOCAL_GL_RGBA, michael@0: aSize.width, michael@0: aSize.height, michael@0: 0, michael@0: LOCAL_GL_RGBA, michael@0: LOCAL_GL_UNSIGNED_BYTE, michael@0: nullptr); michael@0: michael@0: mTextureState = Allocated; michael@0: mSize = aSize; michael@0: } michael@0: michael@0: gfx::IntSize TextureImage::GetSize() const { michael@0: return mSize; michael@0: } michael@0: michael@0: TextureImage::TextureImage(const gfx::IntSize& aSize, michael@0: GLenum aWrapMode, ContentType aContentType, michael@0: Flags aFlags) michael@0: : mSize(aSize) michael@0: , mWrapMode(aWrapMode) michael@0: , mContentType(aContentType) michael@0: , mFilter(GraphicsFilter::FILTER_GOOD) michael@0: , mFlags(aFlags) michael@0: {} michael@0: michael@0: BasicTextureImage::BasicTextureImage(GLuint aTexture, michael@0: const nsIntSize& aSize, michael@0: GLenum aWrapMode, michael@0: ContentType aContentType, michael@0: GLContext* aContext, michael@0: TextureImage::Flags aFlags /* = TextureImage::NoFlags */, michael@0: TextureImage::ImageFormat aImageFormat /* = gfxImageFormat::Unknown */) michael@0: : TextureImage(aSize, aWrapMode, aContentType, aFlags, aImageFormat) michael@0: , mTexture(aTexture) michael@0: , mTextureState(Created) michael@0: , mGLContext(aContext) michael@0: , mUpdateOffset(0, 0) michael@0: { michael@0: } michael@0: michael@0: BasicTextureImage::BasicTextureImage(GLuint aTexture, michael@0: const gfx::IntSize& aSize, michael@0: GLenum aWrapMode, michael@0: ContentType aContentType, michael@0: GLContext* aContext, michael@0: TextureImage::Flags aFlags, michael@0: TextureImage::ImageFormat aImageFormat) michael@0: : TextureImage(ThebesIntSize(aSize), aWrapMode, aContentType, aFlags, aImageFormat) michael@0: , mTexture(aTexture) michael@0: , mTextureState(Created) michael@0: , mGLContext(aContext) michael@0: , mUpdateOffset(0, 0) michael@0: {} michael@0: michael@0: static bool michael@0: WantsSmallTiles(GLContext* gl) michael@0: { michael@0: // We must use small tiles for good performance if we can't use michael@0: // glTexSubImage2D() for some reason. michael@0: if (!CanUploadSubTextures(gl)) michael@0: return true; michael@0: michael@0: // We can't use small tiles on the SGX 540, because of races in texture upload. michael@0: if (gl->WorkAroundDriverBugs() && michael@0: gl->Renderer() == GLRenderer::SGX540) michael@0: return false; michael@0: michael@0: // Don't use small tiles otherwise. (If we implement incremental texture upload, michael@0: // then we will want to revisit this.) michael@0: return false; michael@0: } michael@0: michael@0: TiledTextureImage::TiledTextureImage(GLContext* aGL, michael@0: gfx::IntSize aSize, michael@0: TextureImage::ContentType aContentType, michael@0: TextureImage::Flags aFlags, michael@0: TextureImage::ImageFormat aImageFormat) michael@0: : TextureImage(aSize, LOCAL_GL_CLAMP_TO_EDGE, aContentType, aFlags) michael@0: , mCurrentImage(0) michael@0: , mIterationCallback(nullptr) michael@0: , mInUpdate(false) michael@0: , mRows(0) michael@0: , mColumns(0) michael@0: , mGL(aGL) michael@0: , mTextureState(Created) michael@0: , mImageFormat(aImageFormat) michael@0: { michael@0: if (!(aFlags & TextureImage::DisallowBigImage) && WantsSmallTiles(mGL)) { michael@0: mTileSize = 256; michael@0: } else { michael@0: mGL->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, (GLint*) &mTileSize); michael@0: } michael@0: if (aSize.width != 0 && aSize.height != 0) { michael@0: Resize(aSize); michael@0: } michael@0: } michael@0: michael@0: TiledTextureImage::~TiledTextureImage() michael@0: { michael@0: } michael@0: michael@0: bool michael@0: TiledTextureImage::DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion, const gfx::IntPoint& aFrom /* = gfx::IntPoint(0, 0) */) michael@0: { michael@0: if (mSize.width == 0 || mSize.height == 0) { michael@0: return true; michael@0: } michael@0: michael@0: nsIntRegion region; michael@0: michael@0: if (mTextureState != Valid) { michael@0: nsIntRect bounds = nsIntRect(0, 0, mSize.width, mSize.height); michael@0: region = nsIntRegion(bounds); michael@0: } else { michael@0: region = aRegion; michael@0: } michael@0: michael@0: bool result = true; michael@0: int oldCurrentImage = mCurrentImage; michael@0: BeginTileIteration(); michael@0: do { michael@0: nsIntRect tileRect = ThebesIntRect(GetSrcTileRect()); michael@0: int xPos = tileRect.x; michael@0: int yPos = tileRect.y; michael@0: michael@0: nsIntRegion tileRegion; michael@0: tileRegion.And(region, tileRect); // intersect with tile michael@0: michael@0: if (tileRegion.IsEmpty()) michael@0: continue; michael@0: michael@0: if (CanUploadSubTextures(mGL)) { michael@0: tileRegion.MoveBy(-xPos, -yPos); // translate into tile local space michael@0: } else { michael@0: // If sub-textures are unsupported, expand to tile boundaries michael@0: tileRect.x = tileRect.y = 0; michael@0: tileRegion = nsIntRegion(tileRect); michael@0: } michael@0: michael@0: result &= mImages[mCurrentImage]-> michael@0: DirectUpdate(aSurf, tileRegion, aFrom + gfx::IntPoint(xPos, yPos)); michael@0: michael@0: if (mCurrentImage == mImages.Length() - 1) { michael@0: // We know we're done, but we still need to ensure that the callback michael@0: // gets called (e.g. to update the uploaded region). michael@0: NextTile(); michael@0: break; michael@0: } michael@0: // Override a callback cancelling iteration if the texture wasn't valid. michael@0: // We need to force the update in that situation, or we may end up michael@0: // showing invalid/out-of-date texture data. michael@0: } while (NextTile() || (mTextureState != Valid)); michael@0: mCurrentImage = oldCurrentImage; michael@0: michael@0: mTextureFormat = mImages[0]->GetTextureFormat(); michael@0: mTextureState = Valid; michael@0: return result; michael@0: } michael@0: michael@0: void michael@0: TiledTextureImage::GetUpdateRegion(nsIntRegion& aForRegion) michael@0: { michael@0: if (mTextureState != Valid) { michael@0: // if the texture hasn't been initialized yet, or something important michael@0: // changed, we need to recreate our backing surface and force the michael@0: // client to paint everything michael@0: aForRegion = nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize)); michael@0: return; michael@0: } michael@0: michael@0: nsIntRegion newRegion; michael@0: michael@0: // We need to query each texture with the region it will be drawing and michael@0: // set aForRegion to be the combination of all of these regions michael@0: for (unsigned i = 0; i < mImages.Length(); i++) { michael@0: int xPos = (i % mColumns) * mTileSize; michael@0: int yPos = (i / mColumns) * mTileSize; michael@0: nsIntRect imageRect = nsIntRect(nsIntPoint(xPos,yPos), michael@0: ThebesIntSize(mImages[i]->GetSize())); michael@0: michael@0: if (aForRegion.Intersects(imageRect)) { michael@0: // Make a copy of the region michael@0: nsIntRegion subRegion; michael@0: subRegion.And(aForRegion, imageRect); michael@0: // Translate it into tile-space michael@0: subRegion.MoveBy(-xPos, -yPos); michael@0: // Query region michael@0: mImages[i]->GetUpdateRegion(subRegion); michael@0: // Translate back michael@0: subRegion.MoveBy(xPos, yPos); michael@0: // Add to the accumulated region michael@0: newRegion.Or(newRegion, subRegion); michael@0: } michael@0: } michael@0: michael@0: aForRegion = newRegion; michael@0: } michael@0: michael@0: gfx::DrawTarget* michael@0: TiledTextureImage::BeginUpdate(nsIntRegion& aRegion) michael@0: { michael@0: NS_ASSERTION(!mInUpdate, "nested update"); michael@0: mInUpdate = true; michael@0: michael@0: // Note, we don't call GetUpdateRegion here as if the updated region is michael@0: // fully contained in a single tile, we get to avoid iterating through michael@0: // the tiles again (and a little copying). michael@0: if (mTextureState != Valid) michael@0: { michael@0: // if the texture hasn't been initialized yet, or something important michael@0: // changed, we need to recreate our backing surface and force the michael@0: // client to paint everything michael@0: aRegion = nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize)); michael@0: } michael@0: michael@0: nsIntRect bounds = aRegion.GetBounds(); michael@0: michael@0: for (unsigned i = 0; i < mImages.Length(); i++) { michael@0: int xPos = (i % mColumns) * mTileSize; michael@0: int yPos = (i / mColumns) * mTileSize; michael@0: nsIntRegion imageRegion = michael@0: nsIntRegion(nsIntRect(nsIntPoint(xPos,yPos), michael@0: ThebesIntSize(mImages[i]->GetSize()))); michael@0: michael@0: // a single Image can handle this update request michael@0: if (imageRegion.Contains(aRegion)) { michael@0: // adjust for tile offset michael@0: aRegion.MoveBy(-xPos, -yPos); michael@0: // forward the actual call michael@0: RefPtr drawTarget = mImages[i]->BeginUpdate(aRegion); michael@0: // caller expects container space michael@0: aRegion.MoveBy(xPos, yPos); michael@0: // we don't have a temp surface michael@0: mUpdateDrawTarget = nullptr; michael@0: // remember which image to EndUpdate michael@0: mCurrentImage = i; michael@0: return drawTarget.get(); michael@0: } michael@0: } michael@0: michael@0: // Get the real updated region, taking into account the capabilities of michael@0: // each TextureImage tile michael@0: GetUpdateRegion(aRegion); michael@0: mUpdateRegion = aRegion; michael@0: bounds = aRegion.GetBounds(); michael@0: michael@0: // update covers multiple Images - create a temp surface to paint in michael@0: gfx::SurfaceFormat format = michael@0: (GetContentType() == gfxContentType::COLOR) ? michael@0: gfx::SurfaceFormat::B8G8R8X8: gfx::SurfaceFormat::B8G8R8A8; michael@0: mUpdateDrawTarget = gfx::Factory::CreateDrawTarget(gfx::BackendType::CAIRO, michael@0: bounds.Size().ToIntSize(), michael@0: format); michael@0: michael@0: return mUpdateDrawTarget;; michael@0: } michael@0: michael@0: void michael@0: TiledTextureImage::EndUpdate() michael@0: { michael@0: NS_ASSERTION(mInUpdate, "EndUpdate not in update"); michael@0: if (!mUpdateDrawTarget) { // update was to a single TextureImage michael@0: mImages[mCurrentImage]->EndUpdate(); michael@0: mInUpdate = false; michael@0: mTextureState = Valid; michael@0: mTextureFormat = mImages[mCurrentImage]->GetTextureFormat(); michael@0: return; michael@0: } michael@0: michael@0: RefPtr updateSnapshot = mUpdateDrawTarget->Snapshot(); michael@0: RefPtr updateData = updateSnapshot->GetDataSurface(); michael@0: nsRefPtr updateSurface = new gfxImageSurface(updateData->GetData(), michael@0: gfx::ThebesIntSize(updateData->GetSize()), michael@0: updateData->Stride(), michael@0: gfx::SurfaceFormatToImageFormat(updateData->GetFormat())); michael@0: michael@0: // upload tiles from temp surface michael@0: for (unsigned i = 0; i < mImages.Length(); i++) { michael@0: int xPos = (i % mColumns) * mTileSize; michael@0: int yPos = (i / mColumns) * mTileSize; michael@0: nsIntRect imageRect = nsIntRect(nsIntPoint(xPos,yPos), michael@0: ThebesIntSize(mImages[i]->GetSize())); michael@0: michael@0: nsIntRegion subregion; michael@0: subregion.And(mUpdateRegion, imageRect); michael@0: if (subregion.IsEmpty()) michael@0: continue; michael@0: subregion.MoveBy(-xPos, -yPos); // Tile-local space michael@0: // copy tile from temp target michael@0: gfx::DrawTarget* drawTarget = mImages[i]->BeginUpdate(subregion); michael@0: nsRefPtr ctx = new gfxContext(drawTarget); michael@0: gfxUtils::ClipToRegion(ctx, subregion); michael@0: ctx->SetOperator(gfxContext::OPERATOR_SOURCE); michael@0: ctx->SetSource(updateSurface, gfxPoint(-xPos, -yPos)); michael@0: ctx->Paint(); michael@0: mImages[i]->EndUpdate(); michael@0: } michael@0: michael@0: mUpdateDrawTarget = nullptr; michael@0: mInUpdate = false; michael@0: mTextureFormat = mImages[0]->GetTextureFormat(); michael@0: mTextureState = Valid; michael@0: } michael@0: michael@0: void TiledTextureImage::BeginTileIteration() michael@0: { michael@0: mCurrentImage = 0; michael@0: } michael@0: michael@0: bool TiledTextureImage::NextTile() michael@0: { michael@0: bool continueIteration = true; michael@0: michael@0: if (mIterationCallback) michael@0: continueIteration = mIterationCallback(this, mCurrentImage, michael@0: mIterationCallbackData); michael@0: michael@0: if (mCurrentImage + 1 < mImages.Length()) { michael@0: mCurrentImage++; michael@0: return continueIteration; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: void TiledTextureImage::SetIterationCallback(TileIterationCallback aCallback, michael@0: void* aCallbackData) michael@0: { michael@0: mIterationCallback = aCallback; michael@0: mIterationCallbackData = aCallbackData; michael@0: } michael@0: michael@0: gfx::IntRect TiledTextureImage::GetTileRect() michael@0: { michael@0: if (!GetTileCount()) { michael@0: return gfx::IntRect(); michael@0: } michael@0: gfx::IntRect rect = mImages[mCurrentImage]->GetTileRect(); michael@0: unsigned int xPos = (mCurrentImage % mColumns) * mTileSize; michael@0: unsigned int yPos = (mCurrentImage / mColumns) * mTileSize; michael@0: rect.MoveBy(xPos, yPos); michael@0: return rect; michael@0: } michael@0: michael@0: gfx::IntRect TiledTextureImage::GetSrcTileRect() michael@0: { michael@0: gfx::IntRect rect = GetTileRect(); michael@0: unsigned int srcY = mFlags & NeedsYFlip michael@0: ? mSize.height - rect.height - rect.y michael@0: : rect.y; michael@0: return gfx::IntRect(rect.x, srcY, rect.width, rect.height); michael@0: } michael@0: michael@0: void michael@0: TiledTextureImage::BindTexture(GLenum aTextureUnit) michael@0: { michael@0: if (!GetTileCount()) { michael@0: return; michael@0: } michael@0: mImages[mCurrentImage]->BindTexture(aTextureUnit); michael@0: } michael@0: michael@0: /* michael@0: * Resize, trying to reuse tiles. The reuse strategy is to decide on reuse per michael@0: * column. A tile on a column is reused if it hasn't changed size, otherwise it michael@0: * is discarded/replaced. Extra tiles on a column are pruned after iterating michael@0: * each column, and extra rows are pruned after iteration over the entire image michael@0: * finishes. michael@0: */ michael@0: void TiledTextureImage::Resize(const gfx::IntSize& aSize) michael@0: { michael@0: if (mSize == aSize && mTextureState != Created) { michael@0: return; michael@0: } michael@0: michael@0: // calculate rows and columns, rounding up michael@0: unsigned int columns = (aSize.width + mTileSize - 1) / mTileSize; michael@0: unsigned int rows = (aSize.height + mTileSize - 1) / mTileSize; michael@0: michael@0: // Iterate over old tile-store and insert/remove tiles as necessary michael@0: int row; michael@0: unsigned int i = 0; michael@0: for (row = 0; row < (int)rows; row++) { michael@0: // If we've gone beyond how many rows there were before, set mColumns to michael@0: // zero so that we only create new tiles. michael@0: if (row >= (int)mRows) michael@0: mColumns = 0; michael@0: michael@0: // Similarly, if we're on the last row of old tiles and the height has michael@0: // changed, discard all tiles in that row. michael@0: // This will cause the pruning of columns not to work, but we don't need michael@0: // to worry about that, as no more tiles will be reused past this point michael@0: // anyway. michael@0: if ((row == (int)mRows - 1) && (aSize.height != mSize.height)) michael@0: mColumns = 0; michael@0: michael@0: int col; michael@0: for (col = 0; col < (int)columns; col++) { michael@0: nsIntSize size( // use tilesize first, then the remainder michael@0: (col+1) * mTileSize > (unsigned int)aSize.width ? aSize.width % mTileSize : mTileSize, michael@0: (row+1) * mTileSize > (unsigned int)aSize.height ? aSize.height % mTileSize : mTileSize); michael@0: michael@0: bool replace = false; michael@0: michael@0: // Check if we can re-use old tiles. michael@0: if (col < (int)mColumns) { michael@0: // Reuse an existing tile. If the tile is an end-tile and the michael@0: // width differs, replace it instead. michael@0: if (mSize.width != aSize.width) { michael@0: if (col == (int)mColumns - 1) { michael@0: // Tile at the end of the old column, replace it with michael@0: // a new one. michael@0: replace = true; michael@0: } else if (col == (int)columns - 1) { michael@0: // Tile at the end of the new column, create a new one. michael@0: } else { michael@0: // Before the last column on both the old and new sizes, michael@0: // reuse existing tile. michael@0: i++; michael@0: continue; michael@0: } michael@0: } else { michael@0: // Width hasn't changed, reuse existing tile. michael@0: i++; michael@0: continue; michael@0: } michael@0: } michael@0: michael@0: // Create a new tile. michael@0: nsRefPtr teximg = michael@0: TileGenFunc(mGL, size, mContentType, mFlags, mImageFormat); michael@0: if (replace) michael@0: mImages.ReplaceElementAt(i, teximg); michael@0: else michael@0: mImages.InsertElementAt(i, teximg); michael@0: i++; michael@0: } michael@0: michael@0: // Prune any unused tiles on the end of the column. michael@0: if (row < (int)mRows) { michael@0: for (col = (int)mColumns - col; col > 0; col--) { michael@0: mImages.RemoveElementAt(i); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Prune any unused tiles at the end of the store. michael@0: unsigned int length = mImages.Length(); michael@0: for (; i < length; i++) michael@0: mImages.RemoveElementAt(mImages.Length()-1); michael@0: michael@0: // Reset tile-store properties. michael@0: mRows = rows; michael@0: mColumns = columns; michael@0: mSize = aSize; michael@0: mTextureState = Allocated; michael@0: mCurrentImage = 0; michael@0: } michael@0: michael@0: uint32_t TiledTextureImage::GetTileCount() michael@0: { michael@0: return mImages.Length(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: CreateBasicTextureImage(GLContext* aGL, michael@0: const gfx::IntSize& aSize, michael@0: TextureImage::ContentType aContentType, michael@0: GLenum aWrapMode, michael@0: TextureImage::Flags aFlags, michael@0: TextureImage::ImageFormat aImageFormat) michael@0: { michael@0: bool useNearestFilter = aFlags & TextureImage::UseNearestFilter; michael@0: if (!aGL->MakeCurrent()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: GLuint texture = 0; michael@0: aGL->fGenTextures(1, &texture); michael@0: michael@0: ScopedBindTexture bind(aGL, texture); michael@0: michael@0: GLint texfilter = useNearestFilter ? LOCAL_GL_NEAREST : LOCAL_GL_LINEAR; michael@0: aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, texfilter); michael@0: aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, texfilter); michael@0: aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, aWrapMode); michael@0: aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, aWrapMode); michael@0: michael@0: nsRefPtr texImage = michael@0: new BasicTextureImage(texture, aSize, aWrapMode, aContentType, michael@0: aGL, aFlags, aImageFormat); michael@0: return texImage.forget(); michael@0: } michael@0: michael@0: } // namespace michael@0: } // namespace