gfx/gl/GLTextureImage.cpp

changeset 0
6474c204b198
     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

mercurial