gfx/gl/GLTextureImage.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "GLTextureImage.h"
michael@0 7 #include "GLContext.h"
michael@0 8 #include "gfxContext.h"
michael@0 9 #include "gfxPlatform.h"
michael@0 10 #include "gfxUtils.h"
michael@0 11 #include "gfx2DGlue.h"
michael@0 12 #include "ScopedGLHelpers.h"
michael@0 13 #include "GLUploadHelpers.h"
michael@0 14
michael@0 15 #include "TextureImageEGL.h"
michael@0 16 #ifdef XP_MACOSX
michael@0 17 #include "TextureImageCGL.h"
michael@0 18 #endif
michael@0 19
michael@0 20 namespace mozilla {
michael@0 21 namespace gl {
michael@0 22
michael@0 23 already_AddRefed<TextureImage>
michael@0 24 CreateTextureImage(GLContext* gl,
michael@0 25 const gfx::IntSize& aSize,
michael@0 26 TextureImage::ContentType aContentType,
michael@0 27 GLenum aWrapMode,
michael@0 28 TextureImage::Flags aFlags,
michael@0 29 TextureImage::ImageFormat aImageFormat)
michael@0 30 {
michael@0 31 switch (gl->GetContextType()) {
michael@0 32 #ifdef XP_MACOSX
michael@0 33 case GLContextType::CGL:
michael@0 34 return CreateTextureImageCGL(gl, aSize, aContentType, aWrapMode, aFlags, aImageFormat);
michael@0 35 #endif
michael@0 36 case GLContextType::EGL:
michael@0 37 return CreateTextureImageEGL(gl, aSize, aContentType, aWrapMode, aFlags, aImageFormat);
michael@0 38 default:
michael@0 39 return CreateBasicTextureImage(gl, aSize, aContentType, aWrapMode, aFlags, aImageFormat);
michael@0 40 }
michael@0 41 }
michael@0 42
michael@0 43
michael@0 44 static already_AddRefed<TextureImage>
michael@0 45 TileGenFunc(GLContext* gl,
michael@0 46 const nsIntSize& aSize,
michael@0 47 TextureImage::ContentType aContentType,
michael@0 48 TextureImage::Flags aFlags,
michael@0 49 TextureImage::ImageFormat aImageFormat)
michael@0 50 {
michael@0 51 switch (gl->GetContextType()) {
michael@0 52 #ifdef XP_MACOSX
michael@0 53 case GLContextType::CGL:
michael@0 54 return TileGenFuncCGL(gl, aSize, aContentType, aFlags, aImageFormat);
michael@0 55 #endif
michael@0 56 case GLContextType::EGL:
michael@0 57 return TileGenFuncEGL(gl, aSize, aContentType, aFlags, aImageFormat);
michael@0 58 default:
michael@0 59 return nullptr;
michael@0 60 }
michael@0 61 }
michael@0 62
michael@0 63 already_AddRefed<TextureImage>
michael@0 64 TextureImage::Create(GLContext* gl,
michael@0 65 const nsIntSize& size,
michael@0 66 TextureImage::ContentType contentType,
michael@0 67 GLenum wrapMode,
michael@0 68 TextureImage::Flags flags)
michael@0 69 {
michael@0 70 return Create(gl, size.ToIntSize(), contentType, wrapMode, flags);
michael@0 71 }
michael@0 72
michael@0 73 // Moz2D equivalent...
michael@0 74 already_AddRefed<TextureImage>
michael@0 75 TextureImage::Create(GLContext* gl,
michael@0 76 const gfx::IntSize& size,
michael@0 77 TextureImage::ContentType contentType,
michael@0 78 GLenum wrapMode,
michael@0 79 TextureImage::Flags flags)
michael@0 80 {
michael@0 81 return CreateTextureImage(gl, size, contentType, wrapMode, flags);
michael@0 82 }
michael@0 83
michael@0 84 bool
michael@0 85 TextureImage::UpdateFromDataSource(gfx::DataSourceSurface *aSurface,
michael@0 86 const nsIntRegion* aDestRegion,
michael@0 87 const gfx::IntPoint* aSrcPoint)
michael@0 88 {
michael@0 89 nsIntRegion destRegion = aDestRegion ? *aDestRegion
michael@0 90 : nsIntRect(0, 0,
michael@0 91 aSurface->GetSize().width,
michael@0 92 aSurface->GetSize().height);
michael@0 93 gfx::IntPoint srcPoint = aSrcPoint ? *aSrcPoint
michael@0 94 : gfx::IntPoint(0, 0);
michael@0 95 return DirectUpdate(aSurface, destRegion, srcPoint);
michael@0 96 }
michael@0 97
michael@0 98 gfx::IntRect TextureImage::GetTileRect() {
michael@0 99 return gfx::IntRect(gfx::IntPoint(0,0), mSize);
michael@0 100 }
michael@0 101
michael@0 102 gfx::IntRect TextureImage::GetSrcTileRect() {
michael@0 103 return GetTileRect();
michael@0 104 }
michael@0 105
michael@0 106 BasicTextureImage::~BasicTextureImage()
michael@0 107 {
michael@0 108 GLContext *ctx = mGLContext;
michael@0 109 if (ctx->IsDestroyed() || !ctx->IsOwningThreadCurrent()) {
michael@0 110 ctx = ctx->GetSharedContext();
michael@0 111 }
michael@0 112
michael@0 113 // If we have a context, then we need to delete the texture;
michael@0 114 // if we don't have a context (either real or shared),
michael@0 115 // then they went away when the contex was deleted, because it
michael@0 116 // was the only one that had access to it.
michael@0 117 if (ctx && ctx->MakeCurrent()) {
michael@0 118 ctx->fDeleteTextures(1, &mTexture);
michael@0 119 }
michael@0 120 }
michael@0 121
michael@0 122 gfx::DrawTarget*
michael@0 123 BasicTextureImage::BeginUpdate(nsIntRegion& aRegion)
michael@0 124 {
michael@0 125 NS_ASSERTION(!mUpdateDrawTarget, "BeginUpdate() without EndUpdate()?");
michael@0 126
michael@0 127 // determine the region the client will need to repaint
michael@0 128 if (CanUploadSubTextures(mGLContext)) {
michael@0 129 GetUpdateRegion(aRegion);
michael@0 130 } else {
michael@0 131 aRegion = nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize));
michael@0 132 }
michael@0 133
michael@0 134 mUpdateRegion = aRegion;
michael@0 135
michael@0 136 nsIntRect rgnSize = mUpdateRegion.GetBounds();
michael@0 137 if (!nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize)).Contains(rgnSize)) {
michael@0 138 NS_ERROR("update outside of image");
michael@0 139 return nullptr;
michael@0 140 }
michael@0 141
michael@0 142 gfx::SurfaceFormat format =
michael@0 143 (GetContentType() == gfxContentType::COLOR) ?
michael@0 144 gfx::SurfaceFormat::B8G8R8X8 : gfx::SurfaceFormat::B8G8R8A8;
michael@0 145 mUpdateDrawTarget =
michael@0 146 GetDrawTargetForUpdate(gfx::IntSize(rgnSize.width, rgnSize.height), format);
michael@0 147
michael@0 148 return mUpdateDrawTarget;
michael@0 149 }
michael@0 150
michael@0 151 void
michael@0 152 BasicTextureImage::GetUpdateRegion(nsIntRegion& aForRegion)
michael@0 153 {
michael@0 154 // if the texture hasn't been initialized yet, or something important
michael@0 155 // changed, we need to recreate our backing surface and force the
michael@0 156 // client to paint everything
michael@0 157 if (mTextureState != Valid)
michael@0 158 aForRegion = nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize));
michael@0 159 }
michael@0 160
michael@0 161 void
michael@0 162 BasicTextureImage::EndUpdate()
michael@0 163 {
michael@0 164 NS_ASSERTION(!!mUpdateDrawTarget, "EndUpdate() without BeginUpdate()?");
michael@0 165
michael@0 166 // FIXME: this is the slow boat. Make me fast (with GLXPixmap?).
michael@0 167
michael@0 168 RefPtr<gfx::SourceSurface> updateSnapshot = mUpdateDrawTarget->Snapshot();
michael@0 169 RefPtr<gfx::DataSourceSurface> updateData = updateSnapshot->GetDataSurface();
michael@0 170
michael@0 171 bool relative = FinishedSurfaceUpdate();
michael@0 172
michael@0 173 mTextureFormat =
michael@0 174 UploadSurfaceToTexture(mGLContext,
michael@0 175 updateData,
michael@0 176 mUpdateRegion,
michael@0 177 mTexture,
michael@0 178 mTextureState == Created,
michael@0 179 mUpdateOffset,
michael@0 180 relative);
michael@0 181 FinishedSurfaceUpload();
michael@0 182
michael@0 183 mUpdateDrawTarget = nullptr;
michael@0 184 mTextureState = Valid;
michael@0 185 }
michael@0 186
michael@0 187 void
michael@0 188 BasicTextureImage::BindTexture(GLenum aTextureUnit)
michael@0 189 {
michael@0 190 mGLContext->fActiveTexture(aTextureUnit);
michael@0 191 mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
michael@0 192 mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
michael@0 193 }
michael@0 194
michael@0 195 TemporaryRef<gfx::DrawTarget>
michael@0 196 BasicTextureImage::GetDrawTargetForUpdate(const gfx::IntSize& aSize, gfx::SurfaceFormat aFmt)
michael@0 197 {
michael@0 198 return gfx::Factory::CreateDrawTarget(gfx::BackendType::CAIRO, aSize, aFmt);
michael@0 199 }
michael@0 200
michael@0 201 bool
michael@0 202 BasicTextureImage::FinishedSurfaceUpdate()
michael@0 203 {
michael@0 204 return false;
michael@0 205 }
michael@0 206
michael@0 207 void
michael@0 208 BasicTextureImage::FinishedSurfaceUpload()
michael@0 209 {
michael@0 210 }
michael@0 211
michael@0 212 bool
michael@0 213 BasicTextureImage::DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion, const gfx::IntPoint& aFrom /* = gfx::IntPoint(0, 0) */)
michael@0 214 {
michael@0 215 nsIntRect bounds = aRegion.GetBounds();
michael@0 216 nsIntRegion region;
michael@0 217 if (mTextureState != Valid) {
michael@0 218 bounds = nsIntRect(0, 0, mSize.width, mSize.height);
michael@0 219 region = nsIntRegion(bounds);
michael@0 220 } else {
michael@0 221 region = aRegion;
michael@0 222 }
michael@0 223
michael@0 224 mTextureFormat =
michael@0 225 UploadSurfaceToTexture(mGLContext,
michael@0 226 aSurf,
michael@0 227 region,
michael@0 228 mTexture,
michael@0 229 mTextureState == Created,
michael@0 230 bounds.TopLeft() + nsIntPoint(aFrom.x, aFrom.y),
michael@0 231 false);
michael@0 232 mTextureState = Valid;
michael@0 233 return true;
michael@0 234 }
michael@0 235
michael@0 236 void
michael@0 237 BasicTextureImage::Resize(const gfx::IntSize& aSize)
michael@0 238 {
michael@0 239 NS_ASSERTION(!mUpdateDrawTarget, "Resize() while in update?");
michael@0 240
michael@0 241 mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
michael@0 242
michael@0 243 mGLContext->fTexImage2D(LOCAL_GL_TEXTURE_2D,
michael@0 244 0,
michael@0 245 LOCAL_GL_RGBA,
michael@0 246 aSize.width,
michael@0 247 aSize.height,
michael@0 248 0,
michael@0 249 LOCAL_GL_RGBA,
michael@0 250 LOCAL_GL_UNSIGNED_BYTE,
michael@0 251 nullptr);
michael@0 252
michael@0 253 mTextureState = Allocated;
michael@0 254 mSize = aSize;
michael@0 255 }
michael@0 256
michael@0 257 gfx::IntSize TextureImage::GetSize() const {
michael@0 258 return mSize;
michael@0 259 }
michael@0 260
michael@0 261 TextureImage::TextureImage(const gfx::IntSize& aSize,
michael@0 262 GLenum aWrapMode, ContentType aContentType,
michael@0 263 Flags aFlags)
michael@0 264 : mSize(aSize)
michael@0 265 , mWrapMode(aWrapMode)
michael@0 266 , mContentType(aContentType)
michael@0 267 , mFilter(GraphicsFilter::FILTER_GOOD)
michael@0 268 , mFlags(aFlags)
michael@0 269 {}
michael@0 270
michael@0 271 BasicTextureImage::BasicTextureImage(GLuint aTexture,
michael@0 272 const nsIntSize& aSize,
michael@0 273 GLenum aWrapMode,
michael@0 274 ContentType aContentType,
michael@0 275 GLContext* aContext,
michael@0 276 TextureImage::Flags aFlags /* = TextureImage::NoFlags */,
michael@0 277 TextureImage::ImageFormat aImageFormat /* = gfxImageFormat::Unknown */)
michael@0 278 : TextureImage(aSize, aWrapMode, aContentType, aFlags, aImageFormat)
michael@0 279 , mTexture(aTexture)
michael@0 280 , mTextureState(Created)
michael@0 281 , mGLContext(aContext)
michael@0 282 , mUpdateOffset(0, 0)
michael@0 283 {
michael@0 284 }
michael@0 285
michael@0 286 BasicTextureImage::BasicTextureImage(GLuint aTexture,
michael@0 287 const gfx::IntSize& aSize,
michael@0 288 GLenum aWrapMode,
michael@0 289 ContentType aContentType,
michael@0 290 GLContext* aContext,
michael@0 291 TextureImage::Flags aFlags,
michael@0 292 TextureImage::ImageFormat aImageFormat)
michael@0 293 : TextureImage(ThebesIntSize(aSize), aWrapMode, aContentType, aFlags, aImageFormat)
michael@0 294 , mTexture(aTexture)
michael@0 295 , mTextureState(Created)
michael@0 296 , mGLContext(aContext)
michael@0 297 , mUpdateOffset(0, 0)
michael@0 298 {}
michael@0 299
michael@0 300 static bool
michael@0 301 WantsSmallTiles(GLContext* gl)
michael@0 302 {
michael@0 303 // We must use small tiles for good performance if we can't use
michael@0 304 // glTexSubImage2D() for some reason.
michael@0 305 if (!CanUploadSubTextures(gl))
michael@0 306 return true;
michael@0 307
michael@0 308 // We can't use small tiles on the SGX 540, because of races in texture upload.
michael@0 309 if (gl->WorkAroundDriverBugs() &&
michael@0 310 gl->Renderer() == GLRenderer::SGX540)
michael@0 311 return false;
michael@0 312
michael@0 313 // Don't use small tiles otherwise. (If we implement incremental texture upload,
michael@0 314 // then we will want to revisit this.)
michael@0 315 return false;
michael@0 316 }
michael@0 317
michael@0 318 TiledTextureImage::TiledTextureImage(GLContext* aGL,
michael@0 319 gfx::IntSize aSize,
michael@0 320 TextureImage::ContentType aContentType,
michael@0 321 TextureImage::Flags aFlags,
michael@0 322 TextureImage::ImageFormat aImageFormat)
michael@0 323 : TextureImage(aSize, LOCAL_GL_CLAMP_TO_EDGE, aContentType, aFlags)
michael@0 324 , mCurrentImage(0)
michael@0 325 , mIterationCallback(nullptr)
michael@0 326 , mInUpdate(false)
michael@0 327 , mRows(0)
michael@0 328 , mColumns(0)
michael@0 329 , mGL(aGL)
michael@0 330 , mTextureState(Created)
michael@0 331 , mImageFormat(aImageFormat)
michael@0 332 {
michael@0 333 if (!(aFlags & TextureImage::DisallowBigImage) && WantsSmallTiles(mGL)) {
michael@0 334 mTileSize = 256;
michael@0 335 } else {
michael@0 336 mGL->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, (GLint*) &mTileSize);
michael@0 337 }
michael@0 338 if (aSize.width != 0 && aSize.height != 0) {
michael@0 339 Resize(aSize);
michael@0 340 }
michael@0 341 }
michael@0 342
michael@0 343 TiledTextureImage::~TiledTextureImage()
michael@0 344 {
michael@0 345 }
michael@0 346
michael@0 347 bool
michael@0 348 TiledTextureImage::DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion, const gfx::IntPoint& aFrom /* = gfx::IntPoint(0, 0) */)
michael@0 349 {
michael@0 350 if (mSize.width == 0 || mSize.height == 0) {
michael@0 351 return true;
michael@0 352 }
michael@0 353
michael@0 354 nsIntRegion region;
michael@0 355
michael@0 356 if (mTextureState != Valid) {
michael@0 357 nsIntRect bounds = nsIntRect(0, 0, mSize.width, mSize.height);
michael@0 358 region = nsIntRegion(bounds);
michael@0 359 } else {
michael@0 360 region = aRegion;
michael@0 361 }
michael@0 362
michael@0 363 bool result = true;
michael@0 364 int oldCurrentImage = mCurrentImage;
michael@0 365 BeginTileIteration();
michael@0 366 do {
michael@0 367 nsIntRect tileRect = ThebesIntRect(GetSrcTileRect());
michael@0 368 int xPos = tileRect.x;
michael@0 369 int yPos = tileRect.y;
michael@0 370
michael@0 371 nsIntRegion tileRegion;
michael@0 372 tileRegion.And(region, tileRect); // intersect with tile
michael@0 373
michael@0 374 if (tileRegion.IsEmpty())
michael@0 375 continue;
michael@0 376
michael@0 377 if (CanUploadSubTextures(mGL)) {
michael@0 378 tileRegion.MoveBy(-xPos, -yPos); // translate into tile local space
michael@0 379 } else {
michael@0 380 // If sub-textures are unsupported, expand to tile boundaries
michael@0 381 tileRect.x = tileRect.y = 0;
michael@0 382 tileRegion = nsIntRegion(tileRect);
michael@0 383 }
michael@0 384
michael@0 385 result &= mImages[mCurrentImage]->
michael@0 386 DirectUpdate(aSurf, tileRegion, aFrom + gfx::IntPoint(xPos, yPos));
michael@0 387
michael@0 388 if (mCurrentImage == mImages.Length() - 1) {
michael@0 389 // We know we're done, but we still need to ensure that the callback
michael@0 390 // gets called (e.g. to update the uploaded region).
michael@0 391 NextTile();
michael@0 392 break;
michael@0 393 }
michael@0 394 // Override a callback cancelling iteration if the texture wasn't valid.
michael@0 395 // We need to force the update in that situation, or we may end up
michael@0 396 // showing invalid/out-of-date texture data.
michael@0 397 } while (NextTile() || (mTextureState != Valid));
michael@0 398 mCurrentImage = oldCurrentImage;
michael@0 399
michael@0 400 mTextureFormat = mImages[0]->GetTextureFormat();
michael@0 401 mTextureState = Valid;
michael@0 402 return result;
michael@0 403 }
michael@0 404
michael@0 405 void
michael@0 406 TiledTextureImage::GetUpdateRegion(nsIntRegion& aForRegion)
michael@0 407 {
michael@0 408 if (mTextureState != Valid) {
michael@0 409 // if the texture hasn't been initialized yet, or something important
michael@0 410 // changed, we need to recreate our backing surface and force the
michael@0 411 // client to paint everything
michael@0 412 aForRegion = nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize));
michael@0 413 return;
michael@0 414 }
michael@0 415
michael@0 416 nsIntRegion newRegion;
michael@0 417
michael@0 418 // We need to query each texture with the region it will be drawing and
michael@0 419 // set aForRegion to be the combination of all of these regions
michael@0 420 for (unsigned i = 0; i < mImages.Length(); i++) {
michael@0 421 int xPos = (i % mColumns) * mTileSize;
michael@0 422 int yPos = (i / mColumns) * mTileSize;
michael@0 423 nsIntRect imageRect = nsIntRect(nsIntPoint(xPos,yPos),
michael@0 424 ThebesIntSize(mImages[i]->GetSize()));
michael@0 425
michael@0 426 if (aForRegion.Intersects(imageRect)) {
michael@0 427 // Make a copy of the region
michael@0 428 nsIntRegion subRegion;
michael@0 429 subRegion.And(aForRegion, imageRect);
michael@0 430 // Translate it into tile-space
michael@0 431 subRegion.MoveBy(-xPos, -yPos);
michael@0 432 // Query region
michael@0 433 mImages[i]->GetUpdateRegion(subRegion);
michael@0 434 // Translate back
michael@0 435 subRegion.MoveBy(xPos, yPos);
michael@0 436 // Add to the accumulated region
michael@0 437 newRegion.Or(newRegion, subRegion);
michael@0 438 }
michael@0 439 }
michael@0 440
michael@0 441 aForRegion = newRegion;
michael@0 442 }
michael@0 443
michael@0 444 gfx::DrawTarget*
michael@0 445 TiledTextureImage::BeginUpdate(nsIntRegion& aRegion)
michael@0 446 {
michael@0 447 NS_ASSERTION(!mInUpdate, "nested update");
michael@0 448 mInUpdate = true;
michael@0 449
michael@0 450 // Note, we don't call GetUpdateRegion here as if the updated region is
michael@0 451 // fully contained in a single tile, we get to avoid iterating through
michael@0 452 // the tiles again (and a little copying).
michael@0 453 if (mTextureState != Valid)
michael@0 454 {
michael@0 455 // if the texture hasn't been initialized yet, or something important
michael@0 456 // changed, we need to recreate our backing surface and force the
michael@0 457 // client to paint everything
michael@0 458 aRegion = nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize));
michael@0 459 }
michael@0 460
michael@0 461 nsIntRect bounds = aRegion.GetBounds();
michael@0 462
michael@0 463 for (unsigned i = 0; i < mImages.Length(); i++) {
michael@0 464 int xPos = (i % mColumns) * mTileSize;
michael@0 465 int yPos = (i / mColumns) * mTileSize;
michael@0 466 nsIntRegion imageRegion =
michael@0 467 nsIntRegion(nsIntRect(nsIntPoint(xPos,yPos),
michael@0 468 ThebesIntSize(mImages[i]->GetSize())));
michael@0 469
michael@0 470 // a single Image can handle this update request
michael@0 471 if (imageRegion.Contains(aRegion)) {
michael@0 472 // adjust for tile offset
michael@0 473 aRegion.MoveBy(-xPos, -yPos);
michael@0 474 // forward the actual call
michael@0 475 RefPtr<gfx::DrawTarget> drawTarget = mImages[i]->BeginUpdate(aRegion);
michael@0 476 // caller expects container space
michael@0 477 aRegion.MoveBy(xPos, yPos);
michael@0 478 // we don't have a temp surface
michael@0 479 mUpdateDrawTarget = nullptr;
michael@0 480 // remember which image to EndUpdate
michael@0 481 mCurrentImage = i;
michael@0 482 return drawTarget.get();
michael@0 483 }
michael@0 484 }
michael@0 485
michael@0 486 // Get the real updated region, taking into account the capabilities of
michael@0 487 // each TextureImage tile
michael@0 488 GetUpdateRegion(aRegion);
michael@0 489 mUpdateRegion = aRegion;
michael@0 490 bounds = aRegion.GetBounds();
michael@0 491
michael@0 492 // update covers multiple Images - create a temp surface to paint in
michael@0 493 gfx::SurfaceFormat format =
michael@0 494 (GetContentType() == gfxContentType::COLOR) ?
michael@0 495 gfx::SurfaceFormat::B8G8R8X8: gfx::SurfaceFormat::B8G8R8A8;
michael@0 496 mUpdateDrawTarget = gfx::Factory::CreateDrawTarget(gfx::BackendType::CAIRO,
michael@0 497 bounds.Size().ToIntSize(),
michael@0 498 format);
michael@0 499
michael@0 500 return mUpdateDrawTarget;;
michael@0 501 }
michael@0 502
michael@0 503 void
michael@0 504 TiledTextureImage::EndUpdate()
michael@0 505 {
michael@0 506 NS_ASSERTION(mInUpdate, "EndUpdate not in update");
michael@0 507 if (!mUpdateDrawTarget) { // update was to a single TextureImage
michael@0 508 mImages[mCurrentImage]->EndUpdate();
michael@0 509 mInUpdate = false;
michael@0 510 mTextureState = Valid;
michael@0 511 mTextureFormat = mImages[mCurrentImage]->GetTextureFormat();
michael@0 512 return;
michael@0 513 }
michael@0 514
michael@0 515 RefPtr<gfx::SourceSurface> updateSnapshot = mUpdateDrawTarget->Snapshot();
michael@0 516 RefPtr<gfx::DataSourceSurface> updateData = updateSnapshot->GetDataSurface();
michael@0 517 nsRefPtr<gfxASurface> updateSurface = new gfxImageSurface(updateData->GetData(),
michael@0 518 gfx::ThebesIntSize(updateData->GetSize()),
michael@0 519 updateData->Stride(),
michael@0 520 gfx::SurfaceFormatToImageFormat(updateData->GetFormat()));
michael@0 521
michael@0 522 // upload tiles from temp surface
michael@0 523 for (unsigned i = 0; i < mImages.Length(); i++) {
michael@0 524 int xPos = (i % mColumns) * mTileSize;
michael@0 525 int yPos = (i / mColumns) * mTileSize;
michael@0 526 nsIntRect imageRect = nsIntRect(nsIntPoint(xPos,yPos),
michael@0 527 ThebesIntSize(mImages[i]->GetSize()));
michael@0 528
michael@0 529 nsIntRegion subregion;
michael@0 530 subregion.And(mUpdateRegion, imageRect);
michael@0 531 if (subregion.IsEmpty())
michael@0 532 continue;
michael@0 533 subregion.MoveBy(-xPos, -yPos); // Tile-local space
michael@0 534 // copy tile from temp target
michael@0 535 gfx::DrawTarget* drawTarget = mImages[i]->BeginUpdate(subregion);
michael@0 536 nsRefPtr<gfxContext> ctx = new gfxContext(drawTarget);
michael@0 537 gfxUtils::ClipToRegion(ctx, subregion);
michael@0 538 ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
michael@0 539 ctx->SetSource(updateSurface, gfxPoint(-xPos, -yPos));
michael@0 540 ctx->Paint();
michael@0 541 mImages[i]->EndUpdate();
michael@0 542 }
michael@0 543
michael@0 544 mUpdateDrawTarget = nullptr;
michael@0 545 mInUpdate = false;
michael@0 546 mTextureFormat = mImages[0]->GetTextureFormat();
michael@0 547 mTextureState = Valid;
michael@0 548 }
michael@0 549
michael@0 550 void TiledTextureImage::BeginTileIteration()
michael@0 551 {
michael@0 552 mCurrentImage = 0;
michael@0 553 }
michael@0 554
michael@0 555 bool TiledTextureImage::NextTile()
michael@0 556 {
michael@0 557 bool continueIteration = true;
michael@0 558
michael@0 559 if (mIterationCallback)
michael@0 560 continueIteration = mIterationCallback(this, mCurrentImage,
michael@0 561 mIterationCallbackData);
michael@0 562
michael@0 563 if (mCurrentImage + 1 < mImages.Length()) {
michael@0 564 mCurrentImage++;
michael@0 565 return continueIteration;
michael@0 566 }
michael@0 567 return false;
michael@0 568 }
michael@0 569
michael@0 570 void TiledTextureImage::SetIterationCallback(TileIterationCallback aCallback,
michael@0 571 void* aCallbackData)
michael@0 572 {
michael@0 573 mIterationCallback = aCallback;
michael@0 574 mIterationCallbackData = aCallbackData;
michael@0 575 }
michael@0 576
michael@0 577 gfx::IntRect TiledTextureImage::GetTileRect()
michael@0 578 {
michael@0 579 if (!GetTileCount()) {
michael@0 580 return gfx::IntRect();
michael@0 581 }
michael@0 582 gfx::IntRect rect = mImages[mCurrentImage]->GetTileRect();
michael@0 583 unsigned int xPos = (mCurrentImage % mColumns) * mTileSize;
michael@0 584 unsigned int yPos = (mCurrentImage / mColumns) * mTileSize;
michael@0 585 rect.MoveBy(xPos, yPos);
michael@0 586 return rect;
michael@0 587 }
michael@0 588
michael@0 589 gfx::IntRect TiledTextureImage::GetSrcTileRect()
michael@0 590 {
michael@0 591 gfx::IntRect rect = GetTileRect();
michael@0 592 unsigned int srcY = mFlags & NeedsYFlip
michael@0 593 ? mSize.height - rect.height - rect.y
michael@0 594 : rect.y;
michael@0 595 return gfx::IntRect(rect.x, srcY, rect.width, rect.height);
michael@0 596 }
michael@0 597
michael@0 598 void
michael@0 599 TiledTextureImage::BindTexture(GLenum aTextureUnit)
michael@0 600 {
michael@0 601 if (!GetTileCount()) {
michael@0 602 return;
michael@0 603 }
michael@0 604 mImages[mCurrentImage]->BindTexture(aTextureUnit);
michael@0 605 }
michael@0 606
michael@0 607 /*
michael@0 608 * Resize, trying to reuse tiles. The reuse strategy is to decide on reuse per
michael@0 609 * column. A tile on a column is reused if it hasn't changed size, otherwise it
michael@0 610 * is discarded/replaced. Extra tiles on a column are pruned after iterating
michael@0 611 * each column, and extra rows are pruned after iteration over the entire image
michael@0 612 * finishes.
michael@0 613 */
michael@0 614 void TiledTextureImage::Resize(const gfx::IntSize& aSize)
michael@0 615 {
michael@0 616 if (mSize == aSize && mTextureState != Created) {
michael@0 617 return;
michael@0 618 }
michael@0 619
michael@0 620 // calculate rows and columns, rounding up
michael@0 621 unsigned int columns = (aSize.width + mTileSize - 1) / mTileSize;
michael@0 622 unsigned int rows = (aSize.height + mTileSize - 1) / mTileSize;
michael@0 623
michael@0 624 // Iterate over old tile-store and insert/remove tiles as necessary
michael@0 625 int row;
michael@0 626 unsigned int i = 0;
michael@0 627 for (row = 0; row < (int)rows; row++) {
michael@0 628 // If we've gone beyond how many rows there were before, set mColumns to
michael@0 629 // zero so that we only create new tiles.
michael@0 630 if (row >= (int)mRows)
michael@0 631 mColumns = 0;
michael@0 632
michael@0 633 // Similarly, if we're on the last row of old tiles and the height has
michael@0 634 // changed, discard all tiles in that row.
michael@0 635 // This will cause the pruning of columns not to work, but we don't need
michael@0 636 // to worry about that, as no more tiles will be reused past this point
michael@0 637 // anyway.
michael@0 638 if ((row == (int)mRows - 1) && (aSize.height != mSize.height))
michael@0 639 mColumns = 0;
michael@0 640
michael@0 641 int col;
michael@0 642 for (col = 0; col < (int)columns; col++) {
michael@0 643 nsIntSize size( // use tilesize first, then the remainder
michael@0 644 (col+1) * mTileSize > (unsigned int)aSize.width ? aSize.width % mTileSize : mTileSize,
michael@0 645 (row+1) * mTileSize > (unsigned int)aSize.height ? aSize.height % mTileSize : mTileSize);
michael@0 646
michael@0 647 bool replace = false;
michael@0 648
michael@0 649 // Check if we can re-use old tiles.
michael@0 650 if (col < (int)mColumns) {
michael@0 651 // Reuse an existing tile. If the tile is an end-tile and the
michael@0 652 // width differs, replace it instead.
michael@0 653 if (mSize.width != aSize.width) {
michael@0 654 if (col == (int)mColumns - 1) {
michael@0 655 // Tile at the end of the old column, replace it with
michael@0 656 // a new one.
michael@0 657 replace = true;
michael@0 658 } else if (col == (int)columns - 1) {
michael@0 659 // Tile at the end of the new column, create a new one.
michael@0 660 } else {
michael@0 661 // Before the last column on both the old and new sizes,
michael@0 662 // reuse existing tile.
michael@0 663 i++;
michael@0 664 continue;
michael@0 665 }
michael@0 666 } else {
michael@0 667 // Width hasn't changed, reuse existing tile.
michael@0 668 i++;
michael@0 669 continue;
michael@0 670 }
michael@0 671 }
michael@0 672
michael@0 673 // Create a new tile.
michael@0 674 nsRefPtr<TextureImage> teximg =
michael@0 675 TileGenFunc(mGL, size, mContentType, mFlags, mImageFormat);
michael@0 676 if (replace)
michael@0 677 mImages.ReplaceElementAt(i, teximg);
michael@0 678 else
michael@0 679 mImages.InsertElementAt(i, teximg);
michael@0 680 i++;
michael@0 681 }
michael@0 682
michael@0 683 // Prune any unused tiles on the end of the column.
michael@0 684 if (row < (int)mRows) {
michael@0 685 for (col = (int)mColumns - col; col > 0; col--) {
michael@0 686 mImages.RemoveElementAt(i);
michael@0 687 }
michael@0 688 }
michael@0 689 }
michael@0 690
michael@0 691 // Prune any unused tiles at the end of the store.
michael@0 692 unsigned int length = mImages.Length();
michael@0 693 for (; i < length; i++)
michael@0 694 mImages.RemoveElementAt(mImages.Length()-1);
michael@0 695
michael@0 696 // Reset tile-store properties.
michael@0 697 mRows = rows;
michael@0 698 mColumns = columns;
michael@0 699 mSize = aSize;
michael@0 700 mTextureState = Allocated;
michael@0 701 mCurrentImage = 0;
michael@0 702 }
michael@0 703
michael@0 704 uint32_t TiledTextureImage::GetTileCount()
michael@0 705 {
michael@0 706 return mImages.Length();
michael@0 707 }
michael@0 708
michael@0 709 already_AddRefed<TextureImage>
michael@0 710 CreateBasicTextureImage(GLContext* aGL,
michael@0 711 const gfx::IntSize& aSize,
michael@0 712 TextureImage::ContentType aContentType,
michael@0 713 GLenum aWrapMode,
michael@0 714 TextureImage::Flags aFlags,
michael@0 715 TextureImage::ImageFormat aImageFormat)
michael@0 716 {
michael@0 717 bool useNearestFilter = aFlags & TextureImage::UseNearestFilter;
michael@0 718 if (!aGL->MakeCurrent()) {
michael@0 719 return nullptr;
michael@0 720 }
michael@0 721
michael@0 722 GLuint texture = 0;
michael@0 723 aGL->fGenTextures(1, &texture);
michael@0 724
michael@0 725 ScopedBindTexture bind(aGL, texture);
michael@0 726
michael@0 727 GLint texfilter = useNearestFilter ? LOCAL_GL_NEAREST : LOCAL_GL_LINEAR;
michael@0 728 aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, texfilter);
michael@0 729 aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, texfilter);
michael@0 730 aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, aWrapMode);
michael@0 731 aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, aWrapMode);
michael@0 732
michael@0 733 nsRefPtr<BasicTextureImage> texImage =
michael@0 734 new BasicTextureImage(texture, aSize, aWrapMode, aContentType,
michael@0 735 aGL, aFlags, aImageFormat);
michael@0 736 return texImage.forget();
michael@0 737 }
michael@0 738
michael@0 739 } // namespace
michael@0 740 } // namespace

mercurial