Reapply r5101 and r5102: Made gradient effects use GrTextureStripAtlas.

git-svn-id: http://skia.googlecode.com/svn/trunk@5192 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/effects/gradients/SkGradientShader.cpp b/src/effects/gradients/SkGradientShader.cpp
index 0bdbe54..be5b1b0 100644
--- a/src/effects/gradients/SkGradientShader.cpp
+++ b/src/effects/gradients/SkGradientShader.cpp
@@ -672,20 +672,38 @@
 
 #if SK_SUPPORT_GPU
 
+#include "effects/GrTextureStripAtlas.h"
 #include "SkGr.h"
 
 GrGLGradientStage::GrGLGradientStage(const GrProgramStageFactory& factory)
-                                     : INHERITED(factory) { }
+    : INHERITED(factory)
+    , fCachedYCoord(GR_ScalarMax)
+    , fFSYUni(GrGLUniformManager::kInvalidUniformHandle) { }
 
 GrGLGradientStage::~GrGLGradientStage() { }
 
+void GrGLGradientStage::setupVariables(GrGLShaderBuilder* builder) {
+    fFSYUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                  kFloat_GrSLType, "GradientYCoordFS");
+}
+
+void GrGLGradientStage::setData(const GrGLUniformManager& uman,
+                                const GrCustomStage& stage,
+                                const GrRenderTarget*,
+                                int stageNum) {
+    GrScalar yCoord = static_cast<const GrGradientEffect&>(stage).getYCoord();
+    if (yCoord != fCachedYCoord) {
+        uman.set1f(fFSYUni, yCoord);
+        fCachedYCoord = yCoord;
+    }
+}
+
 void GrGLGradientStage::emitColorLookup(GrGLShaderBuilder* builder, 
                                         const char* tName, 
                                         const char* outputColor,
                                         const char* samplerName) {
-    // Texture is effectively 1D so the y coordinate is 0.5, if we pack multiple
-    // gradients into a texture, we could instead pick the appropriate row here
-    builder->fSampleCoords.printf("vec2(%s, 0.5)", tName);
+    builder->fSampleCoords.printf("vec2(%s, %s)", tName,
+        builder->getUniformVariable(fFSYUni).c_str());
     builder->fComplexCoord = true;
     builder->emitDefaultFetch(outputColor, samplerName);
 }
@@ -696,7 +714,7 @@
                                    const SkGradientShaderBase& shader,
                                    GrSamplerState* sampler)
     : fTexture (NULL)
-    , fUseTexture (false) {
+    , fUseTexture (true) {
     // TODO: check for simple cases where we don't need a texture:
     //GradientInfo info;
     //shader.asAGradient(&info);
@@ -705,19 +723,38 @@
     SkBitmap bitmap;
     shader.getGradientTableBitmap(&bitmap);
 
-    fTexture = GrLockCachedBitmapTexture(ctx, bitmap,
-                                         sampler->textureParams());
-    SkSafeRef(fTexture);
-    fUseTexture = true;
-
-    // Unlock immediately, this is not great, but we don't have a way of
-    // knowing when else to unlock it currently, so it may get purged from
-    // the cache, but it'll still be ref'd until it's no longer being used.
-    GrUnlockCachedBitmapTexture(fTexture);
+    GrTextureStripAtlas::Desc desc;
+    desc.fWidth  = bitmap.width();
+    desc.fHeight = 32;
+    desc.fRowHeight = bitmap.height();
+    desc.fContext = ctx;
+    desc.fConfig = SkBitmapConfig2GrPixelConfig(bitmap.config());
+    fAtlas = GrTextureStripAtlas::GetAtlas(desc);
+    GrAssert(NULL != fAtlas);
+ 
+    fRow = fAtlas->lockRow(bitmap);
+    if (-1 != fRow) {
+        fYCoord = fAtlas->getYOffset(fRow) + GR_ScalarHalf *
+                  fAtlas->getVerticalScaleFactor();
+        fTexture = fAtlas->getTexture();
+    } else {
+        fTexture = GrLockCachedBitmapTexture(ctx, bitmap, sampler->textureParams());
+        SkSafeRef(fTexture);
+        fYCoord = GR_ScalarHalf;
+ 
+        // Unlock immediately, this is not great, but we don't have a way of
+        // knowing when else to unlock it currently, so it may get purged from
+        // the cache, but it'll still be ref'd until it's no longer being used.
+        GrUnlockCachedBitmapTexture(fTexture);
+    }
 }
 
 GrGradientEffect::~GrGradientEffect() {
-    SkSafeUnref(fTexture);
+    if (this->useAtlas()) {
+        fAtlas->unlockRow(fRow);
+    } else {
+        SkSafeUnref(fTexture);
+    }
 }
 
 unsigned int GrGradientEffect::numTextures() const {
diff --git a/src/effects/gradients/SkGradientShaderPriv.h b/src/effects/gradients/SkGradientShaderPriv.h
index 8152d5f..4c3b6c7 100644
--- a/src/effects/gradients/SkGradientShaderPriv.h
+++ b/src/effects/gradients/SkGradientShaderPriv.h
@@ -220,6 +220,8 @@
  *  determines the gradient value.
  */
 
+ class GrTextureStripAtlas;
+
 // Base class for Gr gradient effects
 class GrGradientEffect : public GrCustomStage {
 public:
@@ -233,6 +235,14 @@
     GrTexture* texture(unsigned int index) const;
 
     bool useTexture() const { return fUseTexture; }
+    bool useAtlas() const { return SkToBool(-1 != fRow); }
+    GrScalar getYCoord() const { GrAssert(fUseTexture); return fYCoord; };
+
+    bool isEqual(const GrCustomStage& stage) const {
+        const GrGradientEffect& s = static_cast<const GrGradientEffect&>(stage);
+        return INHERITED::isEqual(stage) && this->useAtlas() == s.useAtlas() &&
+               fYCoord == s.getYCoord();
+    }
 
 protected:
 
@@ -252,6 +262,9 @@
 private:
     GrTexture* fTexture;
     bool fUseTexture;
+    GrScalar fYCoord;
+    GrTextureStripAtlas* fAtlas;
+    int fRow;
 
     typedef GrCustomStage INHERITED;
 
@@ -266,6 +279,12 @@
     GrGLGradientStage(const GrProgramStageFactory& factory);
     virtual ~GrGLGradientStage();
 
+    virtual void setupVariables(GrGLShaderBuilder* builder) SK_OVERRIDE;
+    virtual void setData(const GrGLUniformManager&,
+                         const GrCustomStage&,
+                         const GrRenderTarget*,
+                         int stageNum) SK_OVERRIDE;
+
     // emit code that gets a fragment's color from an expression for t; for now
     // this always uses the texture, but for simpler cases we'll be able to lerp
     void emitColorLookup(GrGLShaderBuilder* builder, const char* t, 
@@ -273,6 +292,9 @@
 
 private:
 
+    GrScalar fCachedYCoord;
+    GrGLUniformManager::UniformHandle fFSYUni;
+
     typedef GrGLProgramStage INHERITED;
 };
 
diff --git a/src/effects/gradients/SkTwoPointConicalGradient.cpp b/src/effects/gradients/SkTwoPointConicalGradient.cpp
index 528291b..e1d8cb4 100644
--- a/src/effects/gradients/SkTwoPointConicalGradient.cpp
+++ b/src/effects/gradients/SkTwoPointConicalGradient.cpp
@@ -468,6 +468,7 @@
 }
 
 void GrGLConical2Gradient::setupVariables(GrGLShaderBuilder* builder) {
+    INHERITED::setupVariables(builder);
     // 2 copies of uniform array, 1 for each of vertex & fragment shader,
     // to work around Xoom bug. Doesn't seem to cause performance decrease
     // in test apps, but need to keep an eye on it.
@@ -631,8 +632,9 @@
 
 void GrGLConical2Gradient::setData(const GrGLUniformManager& uman,
                                    const GrCustomStage& baseData,
-                                   const GrRenderTarget*,
+                                   const GrRenderTarget* target,
                                    int stageNum) {
+    INHERITED::setData(uman, baseData, target, stageNum);
     const GrConical2Gradient& data =
         static_cast<const GrConical2Gradient&>(baseData);
     GrAssert(data.isDegenerate() == fIsDegenerate);
diff --git a/src/effects/gradients/SkTwoPointRadialGradient.cpp b/src/effects/gradients/SkTwoPointRadialGradient.cpp
index 441f5a8..9d93771 100644
--- a/src/effects/gradients/SkTwoPointRadialGradient.cpp
+++ b/src/effects/gradients/SkTwoPointRadialGradient.cpp
@@ -501,6 +501,7 @@
 }
 
 void GrGLRadial2Gradient::setupVariables(GrGLShaderBuilder* builder) {
+    INHERITED::setupVariables(builder);
     // 2 copies of uniform array, 1 for each of vertex & fragment shader,
     // to work around Xoom bug. Doesn't seem to cause performance decrease
     // in test apps, but need to keep an eye on it.
@@ -606,8 +607,9 @@
 
 void GrGLRadial2Gradient::setData(const GrGLUniformManager& uman,
                                   const GrCustomStage& baseData,
-                                  const GrRenderTarget*,
+                                  const GrRenderTarget* target,
                                   int stageNum) {
+    INHERITED::setData(uman, baseData, target, stageNum);
     const GrRadial2Gradient& data =
         static_cast<const GrRadial2Gradient&>(baseData);
     GrAssert(data.isDegenerate() == fIsDegenerate);
diff --git a/src/gpu/effects/GrTextureStripAtlas.cpp b/src/gpu/effects/GrTextureStripAtlas.cpp
index a1a9d03..f31d7d7 100644
--- a/src/gpu/effects/GrTextureStripAtlas.cpp
+++ b/src/gpu/effects/GrTextureStripAtlas.cpp
@@ -34,14 +34,8 @@
     GrTextureStripAtlas* fAtlas;
 };
 
-// Ugly way of ensuring that we clean up the atlases on exit
-struct AtlasEntries {
-    ~AtlasEntries() { fEntries.deleteAll(); }
-    SkTDArray<AtlasEntry*> fEntries;
-};
-
 GrTextureStripAtlas* GrTextureStripAtlas::GetAtlas(const GrTextureStripAtlas::Desc& desc) {
-    static AtlasEntries gAtlasEntries;
+    static SkTDArray<AtlasEntry> gAtlasEntries;
     static GrTHashTable<AtlasEntry, AtlasHashKey, 8> gAtlasCache;
     AtlasHashKey key;
     key.setKeyData(desc.asKey());
@@ -49,8 +43,7 @@
     if (NULL != entry) {
         return entry->fAtlas;
     } else {
-        entry = SkNEW(AtlasEntry);
-        gAtlasEntries.fEntries.push(entry);
+        entry = gAtlasEntries.push();
         entry->fAtlas = SkNEW_ARGS(GrTextureStripAtlas, (desc));
         entry->fKey = key;
         gAtlasCache.insert(key, entry);
@@ -93,6 +86,7 @@
             this->removeFromLRU(row);
         }
         ++row->fLocks;
+        ++fLockedRows;
 
         // Since all the rows are always stored in a contiguous array, we can save the memory 
         // required for storing row numbers and just compute it with some pointer arithmetic
@@ -104,11 +98,14 @@
         // We don't have this data cached, so pick the least recently used row to copy into
         AtlasRow* row = this->getLRU();
 
+        ++fLockedRows;
+
         if (NULL == row) {
             // force a flush, which should unlock all the rows; then try again
             fDesc.fContext->flush();
             row = this->getLRU();
             if (NULL == row) {
+                --fLockedRows;
                 return -1;
             }
         }
@@ -116,8 +113,6 @@
         this->removeFromLRU(row);
 
         uint32_t oldKey = row->fKey;
-        row->fKey = key;
-        row->fLocks = 1;
 
         // If we are writing into a row that already held bitmap data, we need to remove the
         // reference to that genID which is stored in our sorted table of key values.
@@ -126,13 +121,15 @@
             // Find the entry in the list; if it's before the index where we plan on adding the new
             // entry, we decrement since it will shift elements ahead of it back by one.
             int oldIndex = this->searchByKey(oldKey);
-            if (oldIndex <= index) {
+            if (oldIndex < index) {
                 --index;
             }
 
             fKeyTable.remove(oldIndex);
         }
 
+        row->fKey = key;
+        row->fLocks = 1;
         fKeyTable.insert(index, 1, &row);
         rowNumber = static_cast<int>(row - fRows); 
 
@@ -149,7 +146,6 @@
                                                    GrContext::kDontFlush_PixelOpsFlag);
     }
 
-    ++fLockedRows;
     GrAssert(rowNumber >= 0);
     VALIDATE;
     return rowNumber;
@@ -233,12 +229,16 @@
         if (NULL == row->fNext) {
             GrAssert(row == fLRUBack);
             fLRUBack = row->fPrev;
-            fLRUBack->fNext = NULL;
+            if (fLRUBack) {
+                fLRUBack->fNext = NULL;
+            }
         }
         if (NULL == row->fPrev) {
             GrAssert(row == fLRUFront);
             fLRUFront = row->fNext;
-            fLRUFront->fPrev = NULL;
+            if (fLRUFront) {
+                fLRUFront->fPrev = NULL;
+            }
         } 
     }
     row->fNext = NULL;
@@ -303,8 +303,10 @@
         GrAssert(fRows[i].fKey == kEmptyAtlasRowKey || this->searchByKey(fRows[i].fKey) >= 0);
     }
 
-    // Our count of locks should equal the sum of row locks
-    GrAssert(rowLocks == fLockedRows);
+    // Our count of locks should equal the sum of row locks, unless we ran out of rows and flushed,
+    // in which case we'll have one more lock than recorded in the rows (to represent the pending
+    // lock of a row; which ensures we don't unlock the texture prematurely).
+    GrAssert(rowLocks == fLockedRows || rowLocks + 1 == fLockedRows);
 
     // We should have one lru entry for each free row
     GrAssert(freeRows == lruCount);