1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/gl/GLTextureImage.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,740 @@ 1.4 +/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "GLTextureImage.h" 1.10 +#include "GLContext.h" 1.11 +#include "gfxContext.h" 1.12 +#include "gfxPlatform.h" 1.13 +#include "gfxUtils.h" 1.14 +#include "gfx2DGlue.h" 1.15 +#include "ScopedGLHelpers.h" 1.16 +#include "GLUploadHelpers.h" 1.17 + 1.18 +#include "TextureImageEGL.h" 1.19 +#ifdef XP_MACOSX 1.20 +#include "TextureImageCGL.h" 1.21 +#endif 1.22 + 1.23 +namespace mozilla { 1.24 +namespace gl { 1.25 + 1.26 +already_AddRefed<TextureImage> 1.27 +CreateTextureImage(GLContext* gl, 1.28 + const gfx::IntSize& aSize, 1.29 + TextureImage::ContentType aContentType, 1.30 + GLenum aWrapMode, 1.31 + TextureImage::Flags aFlags, 1.32 + TextureImage::ImageFormat aImageFormat) 1.33 +{ 1.34 + switch (gl->GetContextType()) { 1.35 +#ifdef XP_MACOSX 1.36 + case GLContextType::CGL: 1.37 + return CreateTextureImageCGL(gl, aSize, aContentType, aWrapMode, aFlags, aImageFormat); 1.38 +#endif 1.39 + case GLContextType::EGL: 1.40 + return CreateTextureImageEGL(gl, aSize, aContentType, aWrapMode, aFlags, aImageFormat); 1.41 + default: 1.42 + return CreateBasicTextureImage(gl, aSize, aContentType, aWrapMode, aFlags, aImageFormat); 1.43 + } 1.44 +} 1.45 + 1.46 + 1.47 +static already_AddRefed<TextureImage> 1.48 +TileGenFunc(GLContext* gl, 1.49 + const nsIntSize& aSize, 1.50 + TextureImage::ContentType aContentType, 1.51 + TextureImage::Flags aFlags, 1.52 + TextureImage::ImageFormat aImageFormat) 1.53 +{ 1.54 + switch (gl->GetContextType()) { 1.55 +#ifdef XP_MACOSX 1.56 + case GLContextType::CGL: 1.57 + return TileGenFuncCGL(gl, aSize, aContentType, aFlags, aImageFormat); 1.58 +#endif 1.59 + case GLContextType::EGL: 1.60 + return TileGenFuncEGL(gl, aSize, aContentType, aFlags, aImageFormat); 1.61 + default: 1.62 + return nullptr; 1.63 + } 1.64 +} 1.65 + 1.66 +already_AddRefed<TextureImage> 1.67 +TextureImage::Create(GLContext* gl, 1.68 + const nsIntSize& size, 1.69 + TextureImage::ContentType contentType, 1.70 + GLenum wrapMode, 1.71 + TextureImage::Flags flags) 1.72 +{ 1.73 + return Create(gl, size.ToIntSize(), contentType, wrapMode, flags); 1.74 +} 1.75 + 1.76 +// Moz2D equivalent... 1.77 +already_AddRefed<TextureImage> 1.78 +TextureImage::Create(GLContext* gl, 1.79 + const gfx::IntSize& size, 1.80 + TextureImage::ContentType contentType, 1.81 + GLenum wrapMode, 1.82 + TextureImage::Flags flags) 1.83 +{ 1.84 + return CreateTextureImage(gl, size, contentType, wrapMode, flags); 1.85 +} 1.86 + 1.87 +bool 1.88 +TextureImage::UpdateFromDataSource(gfx::DataSourceSurface *aSurface, 1.89 + const nsIntRegion* aDestRegion, 1.90 + const gfx::IntPoint* aSrcPoint) 1.91 +{ 1.92 + nsIntRegion destRegion = aDestRegion ? *aDestRegion 1.93 + : nsIntRect(0, 0, 1.94 + aSurface->GetSize().width, 1.95 + aSurface->GetSize().height); 1.96 + gfx::IntPoint srcPoint = aSrcPoint ? *aSrcPoint 1.97 + : gfx::IntPoint(0, 0); 1.98 + return DirectUpdate(aSurface, destRegion, srcPoint); 1.99 +} 1.100 + 1.101 +gfx::IntRect TextureImage::GetTileRect() { 1.102 + return gfx::IntRect(gfx::IntPoint(0,0), mSize); 1.103 +} 1.104 + 1.105 +gfx::IntRect TextureImage::GetSrcTileRect() { 1.106 + return GetTileRect(); 1.107 +} 1.108 + 1.109 +BasicTextureImage::~BasicTextureImage() 1.110 +{ 1.111 + GLContext *ctx = mGLContext; 1.112 + if (ctx->IsDestroyed() || !ctx->IsOwningThreadCurrent()) { 1.113 + ctx = ctx->GetSharedContext(); 1.114 + } 1.115 + 1.116 + // If we have a context, then we need to delete the texture; 1.117 + // if we don't have a context (either real or shared), 1.118 + // then they went away when the contex was deleted, because it 1.119 + // was the only one that had access to it. 1.120 + if (ctx && ctx->MakeCurrent()) { 1.121 + ctx->fDeleteTextures(1, &mTexture); 1.122 + } 1.123 +} 1.124 + 1.125 +gfx::DrawTarget* 1.126 +BasicTextureImage::BeginUpdate(nsIntRegion& aRegion) 1.127 +{ 1.128 + NS_ASSERTION(!mUpdateDrawTarget, "BeginUpdate() without EndUpdate()?"); 1.129 + 1.130 + // determine the region the client will need to repaint 1.131 + if (CanUploadSubTextures(mGLContext)) { 1.132 + GetUpdateRegion(aRegion); 1.133 + } else { 1.134 + aRegion = nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize)); 1.135 + } 1.136 + 1.137 + mUpdateRegion = aRegion; 1.138 + 1.139 + nsIntRect rgnSize = mUpdateRegion.GetBounds(); 1.140 + if (!nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize)).Contains(rgnSize)) { 1.141 + NS_ERROR("update outside of image"); 1.142 + return nullptr; 1.143 + } 1.144 + 1.145 + gfx::SurfaceFormat format = 1.146 + (GetContentType() == gfxContentType::COLOR) ? 1.147 + gfx::SurfaceFormat::B8G8R8X8 : gfx::SurfaceFormat::B8G8R8A8; 1.148 + mUpdateDrawTarget = 1.149 + GetDrawTargetForUpdate(gfx::IntSize(rgnSize.width, rgnSize.height), format); 1.150 + 1.151 + return mUpdateDrawTarget; 1.152 +} 1.153 + 1.154 +void 1.155 +BasicTextureImage::GetUpdateRegion(nsIntRegion& aForRegion) 1.156 +{ 1.157 + // if the texture hasn't been initialized yet, or something important 1.158 + // changed, we need to recreate our backing surface and force the 1.159 + // client to paint everything 1.160 + if (mTextureState != Valid) 1.161 + aForRegion = nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize)); 1.162 +} 1.163 + 1.164 +void 1.165 +BasicTextureImage::EndUpdate() 1.166 +{ 1.167 + NS_ASSERTION(!!mUpdateDrawTarget, "EndUpdate() without BeginUpdate()?"); 1.168 + 1.169 + // FIXME: this is the slow boat. Make me fast (with GLXPixmap?). 1.170 + 1.171 + RefPtr<gfx::SourceSurface> updateSnapshot = mUpdateDrawTarget->Snapshot(); 1.172 + RefPtr<gfx::DataSourceSurface> updateData = updateSnapshot->GetDataSurface(); 1.173 + 1.174 + bool relative = FinishedSurfaceUpdate(); 1.175 + 1.176 + mTextureFormat = 1.177 + UploadSurfaceToTexture(mGLContext, 1.178 + updateData, 1.179 + mUpdateRegion, 1.180 + mTexture, 1.181 + mTextureState == Created, 1.182 + mUpdateOffset, 1.183 + relative); 1.184 + FinishedSurfaceUpload(); 1.185 + 1.186 + mUpdateDrawTarget = nullptr; 1.187 + mTextureState = Valid; 1.188 +} 1.189 + 1.190 +void 1.191 +BasicTextureImage::BindTexture(GLenum aTextureUnit) 1.192 +{ 1.193 + mGLContext->fActiveTexture(aTextureUnit); 1.194 + mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture); 1.195 + mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0); 1.196 +} 1.197 + 1.198 +TemporaryRef<gfx::DrawTarget> 1.199 +BasicTextureImage::GetDrawTargetForUpdate(const gfx::IntSize& aSize, gfx::SurfaceFormat aFmt) 1.200 +{ 1.201 + return gfx::Factory::CreateDrawTarget(gfx::BackendType::CAIRO, aSize, aFmt); 1.202 +} 1.203 + 1.204 +bool 1.205 +BasicTextureImage::FinishedSurfaceUpdate() 1.206 +{ 1.207 + return false; 1.208 +} 1.209 + 1.210 +void 1.211 +BasicTextureImage::FinishedSurfaceUpload() 1.212 +{ 1.213 +} 1.214 + 1.215 +bool 1.216 +BasicTextureImage::DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion, const gfx::IntPoint& aFrom /* = gfx::IntPoint(0, 0) */) 1.217 +{ 1.218 + nsIntRect bounds = aRegion.GetBounds(); 1.219 + nsIntRegion region; 1.220 + if (mTextureState != Valid) { 1.221 + bounds = nsIntRect(0, 0, mSize.width, mSize.height); 1.222 + region = nsIntRegion(bounds); 1.223 + } else { 1.224 + region = aRegion; 1.225 + } 1.226 + 1.227 + mTextureFormat = 1.228 + UploadSurfaceToTexture(mGLContext, 1.229 + aSurf, 1.230 + region, 1.231 + mTexture, 1.232 + mTextureState == Created, 1.233 + bounds.TopLeft() + nsIntPoint(aFrom.x, aFrom.y), 1.234 + false); 1.235 + mTextureState = Valid; 1.236 + return true; 1.237 +} 1.238 + 1.239 +void 1.240 +BasicTextureImage::Resize(const gfx::IntSize& aSize) 1.241 +{ 1.242 + NS_ASSERTION(!mUpdateDrawTarget, "Resize() while in update?"); 1.243 + 1.244 + mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture); 1.245 + 1.246 + mGLContext->fTexImage2D(LOCAL_GL_TEXTURE_2D, 1.247 + 0, 1.248 + LOCAL_GL_RGBA, 1.249 + aSize.width, 1.250 + aSize.height, 1.251 + 0, 1.252 + LOCAL_GL_RGBA, 1.253 + LOCAL_GL_UNSIGNED_BYTE, 1.254 + nullptr); 1.255 + 1.256 + mTextureState = Allocated; 1.257 + mSize = aSize; 1.258 +} 1.259 + 1.260 +gfx::IntSize TextureImage::GetSize() const { 1.261 + return mSize; 1.262 +} 1.263 + 1.264 +TextureImage::TextureImage(const gfx::IntSize& aSize, 1.265 + GLenum aWrapMode, ContentType aContentType, 1.266 + Flags aFlags) 1.267 + : mSize(aSize) 1.268 + , mWrapMode(aWrapMode) 1.269 + , mContentType(aContentType) 1.270 + , mFilter(GraphicsFilter::FILTER_GOOD) 1.271 + , mFlags(aFlags) 1.272 +{} 1.273 + 1.274 +BasicTextureImage::BasicTextureImage(GLuint aTexture, 1.275 + const nsIntSize& aSize, 1.276 + GLenum aWrapMode, 1.277 + ContentType aContentType, 1.278 + GLContext* aContext, 1.279 + TextureImage::Flags aFlags /* = TextureImage::NoFlags */, 1.280 + TextureImage::ImageFormat aImageFormat /* = gfxImageFormat::Unknown */) 1.281 + : TextureImage(aSize, aWrapMode, aContentType, aFlags, aImageFormat) 1.282 + , mTexture(aTexture) 1.283 + , mTextureState(Created) 1.284 + , mGLContext(aContext) 1.285 + , mUpdateOffset(0, 0) 1.286 +{ 1.287 +} 1.288 + 1.289 +BasicTextureImage::BasicTextureImage(GLuint aTexture, 1.290 + const gfx::IntSize& aSize, 1.291 + GLenum aWrapMode, 1.292 + ContentType aContentType, 1.293 + GLContext* aContext, 1.294 + TextureImage::Flags aFlags, 1.295 + TextureImage::ImageFormat aImageFormat) 1.296 + : TextureImage(ThebesIntSize(aSize), aWrapMode, aContentType, aFlags, aImageFormat) 1.297 + , mTexture(aTexture) 1.298 + , mTextureState(Created) 1.299 + , mGLContext(aContext) 1.300 + , mUpdateOffset(0, 0) 1.301 +{} 1.302 + 1.303 +static bool 1.304 +WantsSmallTiles(GLContext* gl) 1.305 +{ 1.306 + // We must use small tiles for good performance if we can't use 1.307 + // glTexSubImage2D() for some reason. 1.308 + if (!CanUploadSubTextures(gl)) 1.309 + return true; 1.310 + 1.311 + // We can't use small tiles on the SGX 540, because of races in texture upload. 1.312 + if (gl->WorkAroundDriverBugs() && 1.313 + gl->Renderer() == GLRenderer::SGX540) 1.314 + return false; 1.315 + 1.316 + // Don't use small tiles otherwise. (If we implement incremental texture upload, 1.317 + // then we will want to revisit this.) 1.318 + return false; 1.319 +} 1.320 + 1.321 +TiledTextureImage::TiledTextureImage(GLContext* aGL, 1.322 + gfx::IntSize aSize, 1.323 + TextureImage::ContentType aContentType, 1.324 + TextureImage::Flags aFlags, 1.325 + TextureImage::ImageFormat aImageFormat) 1.326 + : TextureImage(aSize, LOCAL_GL_CLAMP_TO_EDGE, aContentType, aFlags) 1.327 + , mCurrentImage(0) 1.328 + , mIterationCallback(nullptr) 1.329 + , mInUpdate(false) 1.330 + , mRows(0) 1.331 + , mColumns(0) 1.332 + , mGL(aGL) 1.333 + , mTextureState(Created) 1.334 + , mImageFormat(aImageFormat) 1.335 +{ 1.336 + if (!(aFlags & TextureImage::DisallowBigImage) && WantsSmallTiles(mGL)) { 1.337 + mTileSize = 256; 1.338 + } else { 1.339 + mGL->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, (GLint*) &mTileSize); 1.340 + } 1.341 + if (aSize.width != 0 && aSize.height != 0) { 1.342 + Resize(aSize); 1.343 + } 1.344 +} 1.345 + 1.346 +TiledTextureImage::~TiledTextureImage() 1.347 +{ 1.348 +} 1.349 + 1.350 +bool 1.351 +TiledTextureImage::DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion, const gfx::IntPoint& aFrom /* = gfx::IntPoint(0, 0) */) 1.352 +{ 1.353 + if (mSize.width == 0 || mSize.height == 0) { 1.354 + return true; 1.355 + } 1.356 + 1.357 + nsIntRegion region; 1.358 + 1.359 + if (mTextureState != Valid) { 1.360 + nsIntRect bounds = nsIntRect(0, 0, mSize.width, mSize.height); 1.361 + region = nsIntRegion(bounds); 1.362 + } else { 1.363 + region = aRegion; 1.364 + } 1.365 + 1.366 + bool result = true; 1.367 + int oldCurrentImage = mCurrentImage; 1.368 + BeginTileIteration(); 1.369 + do { 1.370 + nsIntRect tileRect = ThebesIntRect(GetSrcTileRect()); 1.371 + int xPos = tileRect.x; 1.372 + int yPos = tileRect.y; 1.373 + 1.374 + nsIntRegion tileRegion; 1.375 + tileRegion.And(region, tileRect); // intersect with tile 1.376 + 1.377 + if (tileRegion.IsEmpty()) 1.378 + continue; 1.379 + 1.380 + if (CanUploadSubTextures(mGL)) { 1.381 + tileRegion.MoveBy(-xPos, -yPos); // translate into tile local space 1.382 + } else { 1.383 + // If sub-textures are unsupported, expand to tile boundaries 1.384 + tileRect.x = tileRect.y = 0; 1.385 + tileRegion = nsIntRegion(tileRect); 1.386 + } 1.387 + 1.388 + result &= mImages[mCurrentImage]-> 1.389 + DirectUpdate(aSurf, tileRegion, aFrom + gfx::IntPoint(xPos, yPos)); 1.390 + 1.391 + if (mCurrentImage == mImages.Length() - 1) { 1.392 + // We know we're done, but we still need to ensure that the callback 1.393 + // gets called (e.g. to update the uploaded region). 1.394 + NextTile(); 1.395 + break; 1.396 + } 1.397 + // Override a callback cancelling iteration if the texture wasn't valid. 1.398 + // We need to force the update in that situation, or we may end up 1.399 + // showing invalid/out-of-date texture data. 1.400 + } while (NextTile() || (mTextureState != Valid)); 1.401 + mCurrentImage = oldCurrentImage; 1.402 + 1.403 + mTextureFormat = mImages[0]->GetTextureFormat(); 1.404 + mTextureState = Valid; 1.405 + return result; 1.406 +} 1.407 + 1.408 +void 1.409 +TiledTextureImage::GetUpdateRegion(nsIntRegion& aForRegion) 1.410 +{ 1.411 + if (mTextureState != Valid) { 1.412 + // if the texture hasn't been initialized yet, or something important 1.413 + // changed, we need to recreate our backing surface and force the 1.414 + // client to paint everything 1.415 + aForRegion = nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize)); 1.416 + return; 1.417 + } 1.418 + 1.419 + nsIntRegion newRegion; 1.420 + 1.421 + // We need to query each texture with the region it will be drawing and 1.422 + // set aForRegion to be the combination of all of these regions 1.423 + for (unsigned i = 0; i < mImages.Length(); i++) { 1.424 + int xPos = (i % mColumns) * mTileSize; 1.425 + int yPos = (i / mColumns) * mTileSize; 1.426 + nsIntRect imageRect = nsIntRect(nsIntPoint(xPos,yPos), 1.427 + ThebesIntSize(mImages[i]->GetSize())); 1.428 + 1.429 + if (aForRegion.Intersects(imageRect)) { 1.430 + // Make a copy of the region 1.431 + nsIntRegion subRegion; 1.432 + subRegion.And(aForRegion, imageRect); 1.433 + // Translate it into tile-space 1.434 + subRegion.MoveBy(-xPos, -yPos); 1.435 + // Query region 1.436 + mImages[i]->GetUpdateRegion(subRegion); 1.437 + // Translate back 1.438 + subRegion.MoveBy(xPos, yPos); 1.439 + // Add to the accumulated region 1.440 + newRegion.Or(newRegion, subRegion); 1.441 + } 1.442 + } 1.443 + 1.444 + aForRegion = newRegion; 1.445 +} 1.446 + 1.447 +gfx::DrawTarget* 1.448 +TiledTextureImage::BeginUpdate(nsIntRegion& aRegion) 1.449 +{ 1.450 + NS_ASSERTION(!mInUpdate, "nested update"); 1.451 + mInUpdate = true; 1.452 + 1.453 + // Note, we don't call GetUpdateRegion here as if the updated region is 1.454 + // fully contained in a single tile, we get to avoid iterating through 1.455 + // the tiles again (and a little copying). 1.456 + if (mTextureState != Valid) 1.457 + { 1.458 + // if the texture hasn't been initialized yet, or something important 1.459 + // changed, we need to recreate our backing surface and force the 1.460 + // client to paint everything 1.461 + aRegion = nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize)); 1.462 + } 1.463 + 1.464 + nsIntRect bounds = aRegion.GetBounds(); 1.465 + 1.466 + for (unsigned i = 0; i < mImages.Length(); i++) { 1.467 + int xPos = (i % mColumns) * mTileSize; 1.468 + int yPos = (i / mColumns) * mTileSize; 1.469 + nsIntRegion imageRegion = 1.470 + nsIntRegion(nsIntRect(nsIntPoint(xPos,yPos), 1.471 + ThebesIntSize(mImages[i]->GetSize()))); 1.472 + 1.473 + // a single Image can handle this update request 1.474 + if (imageRegion.Contains(aRegion)) { 1.475 + // adjust for tile offset 1.476 + aRegion.MoveBy(-xPos, -yPos); 1.477 + // forward the actual call 1.478 + RefPtr<gfx::DrawTarget> drawTarget = mImages[i]->BeginUpdate(aRegion); 1.479 + // caller expects container space 1.480 + aRegion.MoveBy(xPos, yPos); 1.481 + // we don't have a temp surface 1.482 + mUpdateDrawTarget = nullptr; 1.483 + // remember which image to EndUpdate 1.484 + mCurrentImage = i; 1.485 + return drawTarget.get(); 1.486 + } 1.487 + } 1.488 + 1.489 + // Get the real updated region, taking into account the capabilities of 1.490 + // each TextureImage tile 1.491 + GetUpdateRegion(aRegion); 1.492 + mUpdateRegion = aRegion; 1.493 + bounds = aRegion.GetBounds(); 1.494 + 1.495 + // update covers multiple Images - create a temp surface to paint in 1.496 + gfx::SurfaceFormat format = 1.497 + (GetContentType() == gfxContentType::COLOR) ? 1.498 + gfx::SurfaceFormat::B8G8R8X8: gfx::SurfaceFormat::B8G8R8A8; 1.499 + mUpdateDrawTarget = gfx::Factory::CreateDrawTarget(gfx::BackendType::CAIRO, 1.500 + bounds.Size().ToIntSize(), 1.501 + format); 1.502 + 1.503 + return mUpdateDrawTarget;; 1.504 +} 1.505 + 1.506 +void 1.507 +TiledTextureImage::EndUpdate() 1.508 +{ 1.509 + NS_ASSERTION(mInUpdate, "EndUpdate not in update"); 1.510 + if (!mUpdateDrawTarget) { // update was to a single TextureImage 1.511 + mImages[mCurrentImage]->EndUpdate(); 1.512 + mInUpdate = false; 1.513 + mTextureState = Valid; 1.514 + mTextureFormat = mImages[mCurrentImage]->GetTextureFormat(); 1.515 + return; 1.516 + } 1.517 + 1.518 + RefPtr<gfx::SourceSurface> updateSnapshot = mUpdateDrawTarget->Snapshot(); 1.519 + RefPtr<gfx::DataSourceSurface> updateData = updateSnapshot->GetDataSurface(); 1.520 + nsRefPtr<gfxASurface> updateSurface = new gfxImageSurface(updateData->GetData(), 1.521 + gfx::ThebesIntSize(updateData->GetSize()), 1.522 + updateData->Stride(), 1.523 + gfx::SurfaceFormatToImageFormat(updateData->GetFormat())); 1.524 + 1.525 + // upload tiles from temp surface 1.526 + for (unsigned i = 0; i < mImages.Length(); i++) { 1.527 + int xPos = (i % mColumns) * mTileSize; 1.528 + int yPos = (i / mColumns) * mTileSize; 1.529 + nsIntRect imageRect = nsIntRect(nsIntPoint(xPos,yPos), 1.530 + ThebesIntSize(mImages[i]->GetSize())); 1.531 + 1.532 + nsIntRegion subregion; 1.533 + subregion.And(mUpdateRegion, imageRect); 1.534 + if (subregion.IsEmpty()) 1.535 + continue; 1.536 + subregion.MoveBy(-xPos, -yPos); // Tile-local space 1.537 + // copy tile from temp target 1.538 + gfx::DrawTarget* drawTarget = mImages[i]->BeginUpdate(subregion); 1.539 + nsRefPtr<gfxContext> ctx = new gfxContext(drawTarget); 1.540 + gfxUtils::ClipToRegion(ctx, subregion); 1.541 + ctx->SetOperator(gfxContext::OPERATOR_SOURCE); 1.542 + ctx->SetSource(updateSurface, gfxPoint(-xPos, -yPos)); 1.543 + ctx->Paint(); 1.544 + mImages[i]->EndUpdate(); 1.545 + } 1.546 + 1.547 + mUpdateDrawTarget = nullptr; 1.548 + mInUpdate = false; 1.549 + mTextureFormat = mImages[0]->GetTextureFormat(); 1.550 + mTextureState = Valid; 1.551 +} 1.552 + 1.553 +void TiledTextureImage::BeginTileIteration() 1.554 +{ 1.555 + mCurrentImage = 0; 1.556 +} 1.557 + 1.558 +bool TiledTextureImage::NextTile() 1.559 +{ 1.560 + bool continueIteration = true; 1.561 + 1.562 + if (mIterationCallback) 1.563 + continueIteration = mIterationCallback(this, mCurrentImage, 1.564 + mIterationCallbackData); 1.565 + 1.566 + if (mCurrentImage + 1 < mImages.Length()) { 1.567 + mCurrentImage++; 1.568 + return continueIteration; 1.569 + } 1.570 + return false; 1.571 +} 1.572 + 1.573 +void TiledTextureImage::SetIterationCallback(TileIterationCallback aCallback, 1.574 + void* aCallbackData) 1.575 +{ 1.576 + mIterationCallback = aCallback; 1.577 + mIterationCallbackData = aCallbackData; 1.578 +} 1.579 + 1.580 +gfx::IntRect TiledTextureImage::GetTileRect() 1.581 +{ 1.582 + if (!GetTileCount()) { 1.583 + return gfx::IntRect(); 1.584 + } 1.585 + gfx::IntRect rect = mImages[mCurrentImage]->GetTileRect(); 1.586 + unsigned int xPos = (mCurrentImage % mColumns) * mTileSize; 1.587 + unsigned int yPos = (mCurrentImage / mColumns) * mTileSize; 1.588 + rect.MoveBy(xPos, yPos); 1.589 + return rect; 1.590 +} 1.591 + 1.592 +gfx::IntRect TiledTextureImage::GetSrcTileRect() 1.593 +{ 1.594 + gfx::IntRect rect = GetTileRect(); 1.595 + unsigned int srcY = mFlags & NeedsYFlip 1.596 + ? mSize.height - rect.height - rect.y 1.597 + : rect.y; 1.598 + return gfx::IntRect(rect.x, srcY, rect.width, rect.height); 1.599 +} 1.600 + 1.601 +void 1.602 +TiledTextureImage::BindTexture(GLenum aTextureUnit) 1.603 +{ 1.604 + if (!GetTileCount()) { 1.605 + return; 1.606 + } 1.607 + mImages[mCurrentImage]->BindTexture(aTextureUnit); 1.608 +} 1.609 + 1.610 +/* 1.611 + * Resize, trying to reuse tiles. The reuse strategy is to decide on reuse per 1.612 + * column. A tile on a column is reused if it hasn't changed size, otherwise it 1.613 + * is discarded/replaced. Extra tiles on a column are pruned after iterating 1.614 + * each column, and extra rows are pruned after iteration over the entire image 1.615 + * finishes. 1.616 + */ 1.617 +void TiledTextureImage::Resize(const gfx::IntSize& aSize) 1.618 +{ 1.619 + if (mSize == aSize && mTextureState != Created) { 1.620 + return; 1.621 + } 1.622 + 1.623 + // calculate rows and columns, rounding up 1.624 + unsigned int columns = (aSize.width + mTileSize - 1) / mTileSize; 1.625 + unsigned int rows = (aSize.height + mTileSize - 1) / mTileSize; 1.626 + 1.627 + // Iterate over old tile-store and insert/remove tiles as necessary 1.628 + int row; 1.629 + unsigned int i = 0; 1.630 + for (row = 0; row < (int)rows; row++) { 1.631 + // If we've gone beyond how many rows there were before, set mColumns to 1.632 + // zero so that we only create new tiles. 1.633 + if (row >= (int)mRows) 1.634 + mColumns = 0; 1.635 + 1.636 + // Similarly, if we're on the last row of old tiles and the height has 1.637 + // changed, discard all tiles in that row. 1.638 + // This will cause the pruning of columns not to work, but we don't need 1.639 + // to worry about that, as no more tiles will be reused past this point 1.640 + // anyway. 1.641 + if ((row == (int)mRows - 1) && (aSize.height != mSize.height)) 1.642 + mColumns = 0; 1.643 + 1.644 + int col; 1.645 + for (col = 0; col < (int)columns; col++) { 1.646 + nsIntSize size( // use tilesize first, then the remainder 1.647 + (col+1) * mTileSize > (unsigned int)aSize.width ? aSize.width % mTileSize : mTileSize, 1.648 + (row+1) * mTileSize > (unsigned int)aSize.height ? aSize.height % mTileSize : mTileSize); 1.649 + 1.650 + bool replace = false; 1.651 + 1.652 + // Check if we can re-use old tiles. 1.653 + if (col < (int)mColumns) { 1.654 + // Reuse an existing tile. If the tile is an end-tile and the 1.655 + // width differs, replace it instead. 1.656 + if (mSize.width != aSize.width) { 1.657 + if (col == (int)mColumns - 1) { 1.658 + // Tile at the end of the old column, replace it with 1.659 + // a new one. 1.660 + replace = true; 1.661 + } else if (col == (int)columns - 1) { 1.662 + // Tile at the end of the new column, create a new one. 1.663 + } else { 1.664 + // Before the last column on both the old and new sizes, 1.665 + // reuse existing tile. 1.666 + i++; 1.667 + continue; 1.668 + } 1.669 + } else { 1.670 + // Width hasn't changed, reuse existing tile. 1.671 + i++; 1.672 + continue; 1.673 + } 1.674 + } 1.675 + 1.676 + // Create a new tile. 1.677 + nsRefPtr<TextureImage> teximg = 1.678 + TileGenFunc(mGL, size, mContentType, mFlags, mImageFormat); 1.679 + if (replace) 1.680 + mImages.ReplaceElementAt(i, teximg); 1.681 + else 1.682 + mImages.InsertElementAt(i, teximg); 1.683 + i++; 1.684 + } 1.685 + 1.686 + // Prune any unused tiles on the end of the column. 1.687 + if (row < (int)mRows) { 1.688 + for (col = (int)mColumns - col; col > 0; col--) { 1.689 + mImages.RemoveElementAt(i); 1.690 + } 1.691 + } 1.692 + } 1.693 + 1.694 + // Prune any unused tiles at the end of the store. 1.695 + unsigned int length = mImages.Length(); 1.696 + for (; i < length; i++) 1.697 + mImages.RemoveElementAt(mImages.Length()-1); 1.698 + 1.699 + // Reset tile-store properties. 1.700 + mRows = rows; 1.701 + mColumns = columns; 1.702 + mSize = aSize; 1.703 + mTextureState = Allocated; 1.704 + mCurrentImage = 0; 1.705 +} 1.706 + 1.707 +uint32_t TiledTextureImage::GetTileCount() 1.708 +{ 1.709 + return mImages.Length(); 1.710 +} 1.711 + 1.712 +already_AddRefed<TextureImage> 1.713 +CreateBasicTextureImage(GLContext* aGL, 1.714 + const gfx::IntSize& aSize, 1.715 + TextureImage::ContentType aContentType, 1.716 + GLenum aWrapMode, 1.717 + TextureImage::Flags aFlags, 1.718 + TextureImage::ImageFormat aImageFormat) 1.719 +{ 1.720 + bool useNearestFilter = aFlags & TextureImage::UseNearestFilter; 1.721 + if (!aGL->MakeCurrent()) { 1.722 + return nullptr; 1.723 + } 1.724 + 1.725 + GLuint texture = 0; 1.726 + aGL->fGenTextures(1, &texture); 1.727 + 1.728 + ScopedBindTexture bind(aGL, texture); 1.729 + 1.730 + GLint texfilter = useNearestFilter ? LOCAL_GL_NEAREST : LOCAL_GL_LINEAR; 1.731 + aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, texfilter); 1.732 + aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, texfilter); 1.733 + aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, aWrapMode); 1.734 + aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, aWrapMode); 1.735 + 1.736 + nsRefPtr<BasicTextureImage> texImage = 1.737 + new BasicTextureImage(texture, aSize, aWrapMode, aContentType, 1.738 + aGL, aFlags, aImageFormat); 1.739 + return texImage.forget(); 1.740 +} 1.741 + 1.742 +} // namespace 1.743 +} // namespace