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

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

mercurial