merge in gpu changes to gradientshaders



git-svn-id: http://skia.googlecode.com/svn/trunk@655 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/effects/SkGradientShader.cpp b/src/effects/SkGradientShader.cpp
index 5a3a80a..cabce20 100644
--- a/src/effects/SkGradientShader.cpp
+++ b/src/effects/SkGradientShader.cpp
@@ -17,8 +17,11 @@
 
 #include "SkGradientShader.h"
 #include "SkColorPriv.h"
+#include "SkMallocPixelRef.h"
 #include "SkUnitMapper.h"
 #include "SkUtils.h"
+#include "SkTemplates.h"
+#include "SkBitmapCache.h"
 
 ///////////////////////////////////////////////////////////////////////////
 
@@ -119,8 +122,9 @@
     const uint16_t*     getCache16();
     const SkPMColor*    getCache32();
 
-    // called when we kill our cached colors (to be rebuilt later on demand)
-    virtual void onCacheReset()  = 0;
+    SkMallocPixelRef* fCache32PixelRef;
+
+    void commonAsABitmap(SkBitmap*);
 
 private:
     enum {
@@ -135,7 +139,6 @@
     SkPMColor*  fCache32;   // working ptr. If this is NULL, we need to recompute the cache values
 
     uint16_t*   fCache16Storage;    // storage for fCache16, allocated on demand
-    SkPMColor*  fCache32Storage;    // storage for fCache32, allocated on demand
     unsigned    fCacheAlpha;        // the alpha value we used when we computed the cache. larger than 8bits so we can store uninitialized value
 
     static void Build16bitCache(uint16_t[], SkColor c0, SkColor c1, int count);
@@ -168,7 +171,8 @@
     fTileProc = gTileProcs[mode];
     
     fCache16 = fCache16Storage = NULL;
-    fCache32 = fCache32Storage = NULL;
+    fCache32 = NULL;
+    fCache32PixelRef = NULL;
 
     /*  Note: we let the caller skip the first and/or last position.
         i.e. pos[0] = 0.3, pos[1] = 0.7
@@ -276,7 +280,8 @@
     fMapper = static_cast<SkUnitMapper*>(buffer.readFlattenable());
 
     fCache16 = fCache16Storage = NULL;
-    fCache32 = fCache32Storage = NULL;
+    fCache32 = NULL;
+    fCache32PixelRef = NULL;
 
     int colorCount = fColorCount = buffer.readU32();
     if (colorCount > kColorStorageCount) {
@@ -306,9 +311,7 @@
     if (fCache16Storage) {
         sk_free(fCache16Storage);
     }
-    if (fCache32Storage) {
-        sk_free(fCache32Storage);
-    }
+    SkSafeUnref(fCache32PixelRef);
     if (fOrigColors != fStorage) {
         sk_free(fOrigColors);
     }
@@ -377,7 +380,9 @@
         fCache32 = NULL;                // inval the cache
         fCacheAlpha = paintAlpha;       // record the new alpha
         // inform our subclasses
-        this->onCacheReset();
+        if (fCache32PixelRef) {
+            fCache32PixelRef->notifyPixelsChanged();
+        }
     }
     return true;
 }
@@ -542,10 +547,12 @@
 
 const SkPMColor* Gradient_Shader::getCache32() {
     if (fCache32 == NULL) {
-        if (fCache32Storage == NULL) // set the storage and our working ptr
-            fCache32Storage = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * kCache32Count);
-
-        fCache32 = fCache32Storage;
+        if (NULL == fCache32PixelRef) {
+            fCache32PixelRef = SkNEW_ARGS(SkMallocPixelRef, (NULL,
+                                                             sizeof(SkPMColor) * kCache32Count,
+                                                             NULL));
+        }
+        fCache32 = (SkPMColor*)fCache32PixelRef->getAddr();
         if (fColorCount == 2) {
             build_32bit_cache(fCache32, fOrigColors[0], fOrigColors[1],
                               kCache32Count, fCacheAlpha);
@@ -566,20 +573,84 @@
         }
 
         if (fMapper) {
-            fCache32Storage = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * kCache32Count);
+            SkMallocPixelRef* newPR = SkNEW_ARGS(SkMallocPixelRef,
+                                                 (NULL,
+                                                  sizeof(SkPMColor) * kCache32Count,
+                                                  NULL));
             SkPMColor* linear = fCache32;           // just computed linear data
-            SkPMColor* mapped = fCache32Storage;    // storage for mapped data
+            SkPMColor* mapped = (SkPMColor*)newPR->getAddr();    // storage for mapped data
             SkUnitMapper* map = fMapper;
             for (int i = 0; i < 256; i++) {
                 mapped[i] = linear[map->mapUnit16((i << 8) | i) >> 8];
             }
-            sk_free(fCache32);
-            fCache32 = fCache32Storage;
+            fCache32PixelRef->unref();
+            fCache32PixelRef = newPR;
+            fCache32 = (SkPMColor*)newPR->getAddr();
         }
     }
     return fCache32;
 }
 
+/*
+ *  Because our caller might rebuild the same (logically the same) gradient
+ *  over and over, we'd like to return exactly the same "bitmap" if possible,
+ *  allowing the client to utilize a cache of our bitmap (e.g. with a GPU).
+ *  To do that, we maintain a private cache of built-bitmaps, based on our
+ *  colors and positions. Note: we don't try to flatten the fMapper, so if one
+ *  is present, we skip the cache for now.
+ */
+void Gradient_Shader::commonAsABitmap(SkBitmap* bitmap) {
+    // don't have a way to put the mapper into our cache-key yet
+    if (fMapper) {
+        // force our cahce32pixelref to be built
+        (void)this->getCache32();
+        bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
+        bitmap->setPixelRef(fCache32PixelRef);
+        return;
+    }
+
+    // build our key: [numColors + colors[] + {positions[]} ]
+    int count = 1 + fColorCount;
+    if (fColorCount > 2) {
+        count += fColorCount - 1;    // fRecs[].fPos
+    }
+
+    SkAutoSTMalloc<16, int32_t> storage(count);
+    int32_t* buffer = storage.get();
+
+    *buffer++ = fColorCount;
+    memcpy(buffer, fOrigColors, fColorCount * sizeof(SkColor));
+    buffer += fColorCount;
+    if (fColorCount > 2) {
+        for (int i = 1; i < fColorCount; i++) {
+            *buffer++ = fRecs[i].fPos;
+        }
+    }
+    SkASSERT(buffer - storage.get() == count);
+
+    ///////////////////////////////////
+
+    static SkMutex gMutex;
+    static SkBitmapCache* gCache;
+    // each cache cost 1K of RAM, since each bitmap will be 1x256 at 32bpp
+    static const int MAX_NUM_CACHED_GRADIENT_BITMAPS = 32;
+    SkAutoMutexAcquire ama(gMutex);
+    
+    if (NULL == gCache) {
+        gCache = new SkBitmapCache(MAX_NUM_CACHED_GRADIENT_BITMAPS);
+    }
+    size_t size = count * sizeof(int32_t);
+
+    if (!gCache->find(storage.get(), size, bitmap)) {
+        // force our cahce32pixelref to be built
+        (void)this->getCache32();
+        bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
+        bitmap->setPixelRef(fCache32PixelRef);
+
+        gCache->add(storage.get(), size, *bitmap);
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////
 
 static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) {
@@ -602,39 +673,24 @@
                     SkShader::TileMode mode, SkUnitMapper* mapper)
         : Gradient_Shader(colors, pos, colorCount, mode, mapper)
     {
-        fCachedBitmap = NULL;
         pts_to_unit_matrix(pts, &fPtsToUnit);
     }
-    virtual ~Linear_Gradient() {
-        if (fCachedBitmap) {
-            SkDELETE(fCachedBitmap);
-        }
-    }
 
     virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
     virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
     virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
-    virtual bool asABitmap(SkBitmap*, SkMatrix*, TileMode*);
-    virtual void onCacheReset() {
-        if (fCachedBitmap) {
-            SkDELETE(fCachedBitmap);
-            fCachedBitmap = NULL;
-        }
-    }
+    virtual BitmapType asABitmap(SkBitmap*, SkMatrix*, 
+                                 TileMode*, SkScalar* twoPointRadialParams);
 
     static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { 
         return SkNEW_ARGS(Linear_Gradient, (buffer));
     }
 
 protected:
-    Linear_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {
-        fCachedBitmap = NULL;
-    }
+    Linear_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {}
     virtual Factory getFactory() { return CreateProc; }
 
 private:
-    SkBitmap* fCachedBitmap;    // allocated on demand
-
     typedef Gradient_Shader INHERITED;
 };
 
@@ -674,10 +730,9 @@
     const SkPMColor*    cache = this->getCache32();
 
     if (fDstToIndexClass != kPerspective_MatrixClass) {
-        dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
+        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
+                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
         SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
-        // preround fx by half the amount we throw away
-        fx += 1 << 7;
 
         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
             SkFixed dxStorage[1];
@@ -752,18 +807,12 @@
     }
 }
 
-bool Linear_Gradient::asABitmap(SkBitmap* bitmap, SkMatrix* matrix,
-                                TileMode xy[]) {
-    // we cache our "bitmap", so it's generationID will be const on subsequent
-    // calls to asABitmap
-    if (NULL == fCachedBitmap) {
-        fCachedBitmap = SkNEW(SkBitmap);
-        fCachedBitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
-        fCachedBitmap->setPixels((void*)this->getCache32(), NULL);
-    }
-
+SkShader::BitmapType Linear_Gradient::asABitmap(SkBitmap* bitmap, 
+                                                SkMatrix* matrix,
+                                                TileMode xy[],
+                                                SkScalar* twoPointRadialParams) {
     if (bitmap) {
-        *bitmap = *fCachedBitmap;
+        this->commonAsABitmap(bitmap);
     }
     if (matrix) {
         matrix->setScale(SkIntToScalar(kCache32Count), SK_Scalar1);
@@ -773,7 +822,7 @@
         xy[0] = fTileMode;
         xy[1] = kClamp_TileMode;
     }
-    return true;
+    return kDefault_BitmapType;
 }
 
 static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
@@ -802,10 +851,9 @@
     int                 toggle = ((x ^ y) & 1) << kCache16Bits;
 
     if (fDstToIndexClass != kPerspective_MatrixClass) {
-        dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
+        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
+                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
         SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
-        // preround fx by half the amount we throw away
-        fx += 1 << 7;
 
         if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
             SkFixed dxStorage[1];
@@ -934,7 +982,8 @@
 
         if (fDstToIndexClass != kPerspective_MatrixClass)
         {
-            dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
+            dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
+                                 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
             SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
             SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
 
@@ -1017,7 +1066,8 @@
         int                 toggle = ((x ^ y) & 1) << kCache16Bits;
 
         if (fDstToIndexClass != kPerspective_MatrixClass) {
-            dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
+            dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
+                                 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
             SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
             SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
 
@@ -1107,6 +1157,24 @@
         }
     }
 
+    virtual BitmapType asABitmap(SkBitmap* bitmap, 
+                                 SkMatrix* matrix, 
+                                 TileMode* xy,
+                                 SkScalar* twoPointRadialParams) {
+        if (bitmap) {
+            this->commonAsABitmap(bitmap);
+        }
+        if (matrix) {
+            matrix->setScale(SkIntToScalar(kCache32Count), SkIntToScalar(kCache32Count));
+            matrix->preConcat(fPtsToUnit);
+        }
+        if (xy) {
+            xy[0] = fTileMode;
+            xy[1] = kClamp_TileMode;
+        }
+        return kRadial_BitmapType;
+    }
+    
     static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { 
         return SkNEW_ARGS(Radial_Gradient, (buffer));
     }
@@ -1114,7 +1182,6 @@
 protected:
     Radial_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {};
     virtual Factory getFactory() { return CreateProc; }
-    virtual void onCacheReset() {}
 
 private:
     typedef Gradient_Shader INHERITED;
@@ -1228,6 +1295,37 @@
         fPtsToUnit.setTranslate(-start.fX, -start.fY);
         fPtsToUnit.postScale(inv, inv);
     }
+    
+    virtual BitmapType asABitmap(SkBitmap* bitmap, 
+                                 SkMatrix* matrix, 
+                                 TileMode* xy,
+                                 SkScalar* twoPointRadialParams) {
+        if (bitmap) {
+            this->commonAsABitmap(bitmap);
+        }
+        SkScalar diffL = 0; // just to avoid gcc warning
+        if (matrix || twoPointRadialParams) {
+            diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) + 
+                                 SkScalarSquare(fDiff.fY));
+        }
+        if (matrix) {
+            SkScalar invDiffL = SkScalarInvert(diffL);
+            matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY), 
+                              SkScalarMul(invDiffL, fDiff.fX));
+            matrix->preConcat(fPtsToUnit);
+        }
+        if (xy) {
+            xy[0] = fTileMode;
+            xy[1] = kClamp_TileMode;
+        }
+        if (NULL != twoPointRadialParams) {
+            twoPointRadialParams[0] = diffL;
+            twoPointRadialParams[1] = fStartRadius;
+            twoPointRadialParams[2] = fDiffRadius;
+        }
+        return kTwoPointRadial_BitmapType;
+    }
+    
     virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count)
     {
         SkASSERT(count > 0);
@@ -1250,7 +1348,8 @@
         if (fDstToIndexClass != kPerspective_MatrixClass)
         {
             SkPoint srcPt;
-            dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
+            dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
+                                 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
             SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
             SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
 
@@ -1365,7 +1464,6 @@
         fOneOverTwoA = buffer.readScalar();
     };
     virtual Factory getFactory() { return CreateProc; }
-    virtual void onCacheReset() {}
 
 private:
     typedef Gradient_Shader INHERITED;
@@ -1386,6 +1484,23 @@
     virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
     virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
     
+    virtual BitmapType asABitmap(SkBitmap* bitmap, 
+                                 SkMatrix* matrix, 
+                                 TileMode* xy,
+                                 SkScalar* twoPointRadialParams) {
+        if (bitmap) {
+            this->commonAsABitmap(bitmap);
+        }
+        if (matrix) {
+            *matrix = fPtsToUnit;
+        }
+        if (xy) {
+            xy[0] = fTileMode;
+            xy[1] = kClamp_TileMode;
+        }
+        return kSweep_BitmapType;
+    }
+
     static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
         return SkNEW_ARGS(Sweep_Gradient, (buffer));
     }
@@ -1393,7 +1508,6 @@
 protected:
     Sweep_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {}
     virtual Factory getFactory() { return CreateProc; }
-    virtual void onCacheReset() {}
 
 private:
     typedef Gradient_Shader INHERITED;