Revert "Revert of https://codereview.chromium.org/108773003/"

This reverts commit 947e6a3142af66b750f1247ef933b11ed8455dd4.

BUG=

Review URL: https://codereview.chromium.org/112963003

git-svn-id: http://skia.googlecode.com/svn/trunk/src@12630 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/core/SkBitmap.cpp b/core/SkBitmap.cpp
index 25a6b1d..7a7f690 100644
--- a/core/SkBitmap.cpp
+++ b/core/SkBitmap.cpp
@@ -453,10 +453,20 @@
         return;
     }
 
-    Sk64 size = this->getSize64();
-    SkASSERT(!size.isNeg() && size.is32());
+    SkImageInfo info;
+    if (!this->asImageInfo(&info)) {
+        this->setPixelRef(NULL, 0);
+        return;
+    }
 
-    this->setPixelRef(new SkMallocPixelRef(p, size.get32(), ctable, false))->unref();
+    SkPixelRef* pr = SkMallocPixelRef::NewDirect(info, p, fRowBytes, ctable);
+    if (NULL == pr) {
+        this->setPixelRef(NULL, 0);
+        return;
+    }
+
+    this->setPixelRef(pr)->unref();
+
     // since we're already allocated, we lockPixels right away
     this->lockPixels();
     SkDEBUGCODE(this->validate();)
@@ -521,17 +531,19 @@
  */
 bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst,
                                             SkColorTable* ctable) {
-    Sk64 size = dst->getSize64();
-    if (size.isNeg() || !size.is32()) {
+    SkImageInfo info;
+    if (!dst->asImageInfo(&info)) {
+//        SkDebugf("unsupported config for info %d\n", dst->config());
+        return false;
+    }
+    
+    SkPixelRef* pr = SkMallocPixelRef::NewAllocate(info, dst->rowBytes(),
+                                                   ctable);
+    if (NULL == pr) {
         return false;
     }
 
-    void* addr = sk_malloc_flags(size.get32(), 0);  // returns NULL on failure
-    if (NULL == addr) {
-        return false;
-    }
-
-    dst->setPixelRef(new SkMallocPixelRef(addr, size.get32(), ctable))->unref();
+    dst->setPixelRef(pr, 0)->unref();
     // since we're already allocated, we lockPixels right away
     dst->lockPixels();
     return true;
diff --git a/core/SkBitmapDevice.cpp b/core/SkBitmapDevice.cpp
index 1668618..368c807 100644
--- a/core/SkBitmapDevice.cpp
+++ b/core/SkBitmapDevice.cpp
@@ -24,31 +24,30 @@
     , fBitmap(bitmap) {
 }
 
-SkBitmapDevice::SkBitmapDevice(SkBitmap::Config config, int width, int height, bool isOpaque) {
+void SkBitmapDevice::init(SkBitmap::Config config, int width, int height, bool isOpaque) {
     fBitmap.setConfig(config, width, height, 0, isOpaque ?
                       kOpaque_SkAlphaType : kPremul_SkAlphaType);
-    if (!fBitmap.allocPixels()) {
-        fBitmap.setConfig(config, 0, 0, 0, isOpaque ?
-                          kOpaque_SkAlphaType : kPremul_SkAlphaType);
+    
+    if (SkBitmap::kNo_Config != config) {
+        if (!fBitmap.allocPixels()) {
+            // indicate failure by zeroing our bitmap
+            fBitmap.setConfig(config, 0, 0, 0, isOpaque ?
+                              kOpaque_SkAlphaType : kPremul_SkAlphaType);
+        } else if (!isOpaque) {
+            fBitmap.eraseColor(SK_ColorTRANSPARENT);
+        }
     }
-    if (!isOpaque) {
-        fBitmap.eraseColor(SK_ColorTRANSPARENT);
-    }
+}
+
+SkBitmapDevice::SkBitmapDevice(SkBitmap::Config config, int width, int height, bool isOpaque) {
+    this->init(config, width, height, isOpaque);
 }
 
 SkBitmapDevice::SkBitmapDevice(SkBitmap::Config config, int width, int height, bool isOpaque,
                                const SkDeviceProperties& deviceProperties)
-    : SkBaseDevice(deviceProperties) {
-
-    fBitmap.setConfig(config, width, height, 0, isOpaque ?
-                      kOpaque_SkAlphaType : kPremul_SkAlphaType);
-    if (!fBitmap.allocPixels()) {
-        fBitmap.setConfig(config, 0, 0, 0, isOpaque ?
-                          kOpaque_SkAlphaType : kPremul_SkAlphaType);
-    }
-    if (!isOpaque) {
-        fBitmap.eraseColor(SK_ColorTRANSPARENT);
-    }
+    : SkBaseDevice(deviceProperties)
+{
+    this->init(config, width, height, isOpaque);
 }
 
 SkBitmapDevice::~SkBitmapDevice() {
diff --git a/core/SkImageFilterUtils.cpp b/core/SkImageFilterUtils.cpp
index 8385fb4..e535d93 100644
--- a/core/SkImageFilterUtils.cpp
+++ b/core/SkImageFilterUtils.cpp
@@ -15,8 +15,14 @@
 #include "SkGr.h"
 
 bool SkImageFilterUtils::WrapTexture(GrTexture* texture, int width, int height, SkBitmap* result) {
-    result->setConfig(SkBitmap::kARGB_8888_Config, width, height);
-    result->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (texture)))->unref();
+    SkImageInfo info;
+    info.fWidth = width;
+    info.fHeight = height;
+    info.fColorType = kPMColor_SkColorType;
+    info.fAlphaType = kPremul_SkAlphaType;
+
+    result->setConfig(info);
+    result->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (info, texture)))->unref();
     return true;
 }
 
@@ -36,8 +42,12 @@
     } else {
         if (filter->filterImage(proxy, src, ctm, result, offset)) {
             if (!result->getTexture()) {
+                SkImageInfo info;
+                if (!result->asImageInfo(&info)) {
+                    return false;
+                }
                 GrTexture* resultTex = GrLockAndRefCachedBitmapTexture(context, *result, NULL);
-                result->setPixelRef(new SkGrPixelRef(resultTex))->unref();
+                result->setPixelRef(new SkGrPixelRef(info, resultTex))->unref();
                 GrUnlockAndUnrefCachedBitmapTexture(resultTex);
             }
             return true;
diff --git a/core/SkMallocPixelRef.cpp b/core/SkMallocPixelRef.cpp
index f229e9d..613491a 100644
--- a/core/SkMallocPixelRef.cpp
+++ b/core/SkMallocPixelRef.cpp
@@ -1,27 +1,105 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
+
 #include "SkMallocPixelRef.h"
 #include "SkBitmap.h"
 #include "SkFlattenableBuffers.h"
 
-SkMallocPixelRef::SkMallocPixelRef(void* storage, size_t size,
-                                   SkColorTable* ctable, bool ownPixels) {
-    if (NULL == storage) {
-        SkASSERT(ownPixels);
-        storage = sk_malloc_throw(size);
+static bool check_info(const SkImageInfo& info, SkColorTable* ctable) {
+    if (info.fWidth < 0 ||
+        info.fHeight < 0 ||
+        (unsigned)info.fColorType > (unsigned)kLastEnum_SkColorType ||
+        (unsigned)info.fAlphaType > (unsigned)kLastEnum_SkAlphaType)
+    {
+        return false;
     }
-    fStorage = storage;
-    fSize = size;
-    fCTable = ctable;
-    SkSafeRef(ctable);
-    fOwnPixels = ownPixels;
+    
+    // these seem like good checks, but currently we have (at least) tests
+    // that expect the pixelref to succeed even when there is a mismatch
+    // with colortables. fix?
+#if 0
+    if (kIndex8_SkColorType == info.fColorType && NULL == ctable) {
+        return false;
+    }
+    if (kIndex8_SkColorType != info.fColorType && NULL != ctable) {
+        return false;
+    }
+#endif
+    return true;
+}
 
-    this->setPreLocked(fStorage, fCTable);
+SkMallocPixelRef* SkMallocPixelRef::NewDirect(const SkImageInfo& info,
+                                              void* addr,
+                                              size_t rowBytes,
+                                              SkColorTable* ctable) {
+    if (!check_info(info, ctable)) {
+        return NULL;
+    }
+    return SkNEW_ARGS(SkMallocPixelRef, (info, addr, rowBytes, ctable, false));
+}
+
+SkMallocPixelRef* SkMallocPixelRef::NewAllocate(const SkImageInfo& info,
+                                                size_t requestedRowBytes,
+                                                SkColorTable* ctable) {
+    if (!check_info(info, ctable)) {
+        return NULL;
+    }
+
+    int32_t minRB = info.minRowBytes();
+    if (minRB < 0) {
+        return NULL;    // allocation will be too large
+    }
+    if (requestedRowBytes > 0 && (int32_t)requestedRowBytes < minRB) {
+        return NULL;    // cannot meet requested rowbytes
+    }
+
+    int32_t rowBytes;
+    if (requestedRowBytes) {
+        rowBytes = requestedRowBytes;
+    } else {
+        rowBytes = minRB;
+    }
+
+    Sk64 bigSize;
+    bigSize.setMul(info.fHeight, rowBytes);
+    if (!bigSize.is32()) {
+        return NULL;
+    }
+
+    size_t size = bigSize.get32();
+    void* addr = sk_malloc_flags(size, 0);
+    if (NULL == addr) {
+        return NULL;
+    }
+
+    return SkNEW_ARGS(SkMallocPixelRef, (info, addr, rowBytes, ctable, true));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkMallocPixelRef::SkMallocPixelRef(const SkImageInfo& info, void* storage,
+                                   size_t rowBytes, SkColorTable* ctable,
+                                   bool ownsPixels)
+    : SkPixelRef(info)
+    , fOwnPixels(ownsPixels)
+{
+    SkASSERT(check_info(info, ctable));
+    SkASSERT(rowBytes >= info.minRowBytes());
+
+    if (kIndex_8_SkColorType != info.fColorType) {
+        ctable = NULL;
+    }
+
+    fStorage = storage;
+    fCTable = ctable;
+    fRB = rowBytes;
+    SkSafeRef(ctable);
+    
+    this->setPreLocked(fStorage, fRB, fCTable);
 }
 
 SkMallocPixelRef::~SkMallocPixelRef() {
@@ -31,19 +109,30 @@
     }
 }
 
-void* SkMallocPixelRef::onLockPixels(SkColorTable** ct) {
-    *ct = fCTable;
-    return fStorage;
+bool SkMallocPixelRef::onNewLockPixels(LockRec* rec) {
+    rec->fPixels = fStorage;
+    rec->fRowBytes = fRB;
+    rec->fColorTable = fCTable;
+    return true;
 }
 
 void SkMallocPixelRef::onUnlockPixels() {
     // nothing to do
 }
 
+size_t SkMallocPixelRef::getAllocatedSizeInBytes() const {
+    return this->info().getSafeSize(fRB);
+}
+
 void SkMallocPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const {
     this->INHERITED::flatten(buffer);
 
-    buffer.writeByteArray(fStorage, fSize);
+    buffer.write32(fRB);
+
+    // TODO: replace this bulk write with a chunky one that can trim off any
+    // trailing bytes on each scanline (in case rowbytes > width*size)
+    size_t size = this->info().getSafeSize(fRB);
+    buffer.writeByteArray(fStorage, size);
     buffer.writeBool(fCTable != NULL);
     if (fCTable) {
         fCTable->writeToBuffer(buffer);
@@ -51,16 +140,18 @@
 }
 
 SkMallocPixelRef::SkMallocPixelRef(SkFlattenableReadBuffer& buffer)
-        : INHERITED(buffer, NULL) {
-    fSize = buffer.getArrayCount();
-    fStorage = sk_malloc_throw(fSize);
-    buffer.readByteArray(fStorage, fSize);
+    : INHERITED(buffer, NULL)
+    , fOwnPixels(true)
+{
+    fRB = buffer.read32();
+    size_t size = this->info().getSafeSize(fRB);
+    fStorage = sk_malloc_throw(size);
+    buffer.readByteArray(fStorage, size);
     if (buffer.readBool()) {
         fCTable = SkNEW_ARGS(SkColorTable, (buffer));
     } else {
         fCTable = NULL;
     }
-    fOwnPixels = true;
 
-    this->setPreLocked(fStorage, fCTable);
+    this->setPreLocked(fStorage, fRB, fCTable);
 }
diff --git a/core/SkMaskFilter.cpp b/core/SkMaskFilter.cpp
index f062f13..adfed41 100644
--- a/core/SkMaskFilter.cpp
+++ b/core/SkMaskFilter.cpp
@@ -349,10 +349,14 @@
     if (!result) {
         return false;
     }
+    SkAutoUnref aur(dst);
 
+    SkImageInfo info;
     resultBM->setConfig(srcBM.config(), dst->width(), dst->height());
-    resultBM->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (dst)))->unref();
-    dst->unref();
+    if (resultBM->asImageInfo(&info)) {
+        return false;
+    }
+    resultBM->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (info, dst)))->unref();
     return true;
 }
 
diff --git a/core/SkPixelRef.cpp b/core/SkPixelRef.cpp
index b5daf0b..60b5cfb 100644
--- a/core/SkPixelRef.cpp
+++ b/core/SkPixelRef.cpp
@@ -82,44 +82,32 @@
 // just need a > 0 value, so pick a funny one to aid in debugging
 #define SKPIXELREF_PRELOCKED_LOCKCOUNT     123456789
 
-SkPixelRef::SkPixelRef(const SkImageInfo&, SkBaseMutex* mutex) {
-    this->setMutex(mutex);
-    fPixels = NULL;
-    fColorTable = NULL; // we do not track ownership of this
-    fLockCount = 0;
-    this->needsNewGenID();
-    fIsImmutable = false;
-    fPreLocked = false;
-}
-
-SkPixelRef::SkPixelRef(const SkImageInfo&) {
+SkPixelRef::SkPixelRef(const SkImageInfo& info) {
     this->setMutex(NULL);
-    fPixels = NULL;
-    fColorTable = NULL; // we do not track ownership of this
+    fInfo = info;
+    fRec.zero();
     fLockCount = 0;
     this->needsNewGenID();
     fIsImmutable = false;
     fPreLocked = false;
 }
 
-#ifdef SK_SUPPORT_LEGACY_PIXELREF_CONSTRUCTOR
-// THIS GUY IS DEPRECATED -- don't use me!
-SkPixelRef::SkPixelRef(SkBaseMutex* mutex) {
+SkPixelRef::SkPixelRef(const SkImageInfo& info, SkBaseMutex* mutex) {
     this->setMutex(mutex);
-    fPixels = NULL;
-    fColorTable = NULL; // we do not track ownership of this
+    fInfo = info;
+    fRec.zero();
     fLockCount = 0;
     this->needsNewGenID();
     fIsImmutable = false;
     fPreLocked = false;
 }
-#endif
 
 SkPixelRef::SkPixelRef(SkFlattenableReadBuffer& buffer, SkBaseMutex* mutex)
         : INHERITED(buffer) {
     this->setMutex(mutex);
-    fPixels = NULL;
-    fColorTable = NULL; // we do not track ownership of this
+
+    fInfo.unflatten(buffer);
+    fRec.zero();
     fLockCount = 0;
     fIsImmutable = buffer.readBool();
     fGenerationID = buffer.readUInt();
@@ -143,12 +131,13 @@
     that.fUniqueGenerationID = false;
 }
 
-void SkPixelRef::setPreLocked(void* pixels, SkColorTable* ctable) {
+void SkPixelRef::setPreLocked(void* pixels, size_t rowBytes, SkColorTable* ctable) {
 #ifndef SK_IGNORE_PIXELREF_SETPRELOCKED
     // only call me in your constructor, otherwise fLockCount tracking can get
     // out of sync.
-    fPixels = pixels;
-    fColorTable = ctable;
+    fRec.fPixels = pixels;
+    fRec.fColorTable = ctable;
+    fRec.fRowBytes = rowBytes;
     fLockCount = SKPIXELREF_PRELOCKED_LOCKCOUNT;
     fPreLocked = true;
 #endif
@@ -156,6 +145,8 @@
 
 void SkPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const {
     this->INHERITED::flatten(buffer);
+
+    fInfo.flatten(buffer);
     buffer.writeBool(fIsImmutable);
     // We write the gen ID into the picture for within-process recording. This
     // is safe since the same genID will never refer to two different sets of
@@ -170,16 +161,27 @@
     }
 }
 
-void SkPixelRef::lockPixels() {
+bool SkPixelRef::lockPixels(LockRec* rec) {
     SkASSERT(!fPreLocked || SKPIXELREF_PRELOCKED_LOCKCOUNT == fLockCount);
-
+    
     if (!fPreLocked) {
         SkAutoMutexAcquire  ac(*fMutex);
-
+        
         if (1 == ++fLockCount) {
-            fPixels = this->onLockPixels(&fColorTable);
+            LockRec rec;
+            if (!this->onNewLockPixels(&rec)) {
+                return false;
+            }
+            fRec = rec;
         }
     }
+    *rec = fRec;
+    return true;
+}
+
+bool SkPixelRef::lockPixels() {
+    LockRec rec;
+    return this->lockPixels(&rec);
 }
 
 void SkPixelRef::unlockPixels() {
@@ -191,8 +193,7 @@
         SkASSERT(fLockCount > 0);
         if (0 == --fLockCount) {
             this->onUnlockPixels();
-            fPixels = NULL;
-            fColorTable = NULL;
+            fRec.zero();
         }
     }
 }
@@ -273,6 +274,29 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+#ifdef SK_SUPPORT_LEGACY_ONLOCKPIXELS
+
+void* SkPixelRef::onLockPixels(SkColorTable** ctable) {
+    return NULL;
+}
+
+bool SkPixelRef::onNewLockPixels(LockRec* rec) {
+    SkColorTable* ctable;
+    void* pixels = this->onLockPixels(&ctable);
+    if (!pixels) {
+        return false;
+    }
+
+    rec->fPixels = pixels;
+    rec->fColorTable = ctable;
+    rec->fRowBytes = 0; // callers don't currently need this (thank goodness)
+    return true;
+}
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
 #ifdef SK_BUILD_FOR_ANDROID
 void SkPixelRef::globalRef(void* data) {
     this->ref();
diff --git a/effects/gradients/SkGradientShader.cpp b/effects/gradients/SkGradientShader.cpp
index 2776199..5d200d1 100644
--- a/effects/gradients/SkGradientShader.cpp
+++ b/effects/gradients/SkGradientShader.cpp
@@ -513,13 +513,14 @@
 
 const SkPMColor* SkGradientShaderBase::getCache32() const {
     if (fCache32 == NULL) {
-        // double the count for dither entries
-        const int entryCount = kCache32Count * 4;
-        const size_t allocSize = sizeof(SkPMColor) * entryCount;
+        SkImageInfo info;
+        info.fWidth = kCache32Count;
+        info.fHeight = 4;   // for our 4 dither rows
+        info.fAlphaType = kPremul_SkAlphaType;
+        info.fColorType = kPMColor_SkColorType;
 
         if (NULL == fCache32PixelRef) {
-            fCache32PixelRef = SkNEW_ARGS(SkMallocPixelRef,
-                                          (NULL, allocSize, NULL));
+            fCache32PixelRef = SkMallocPixelRef::NewAllocate(info, 0, NULL);
         }
         fCache32 = (SkPMColor*)fCache32PixelRef->getAddr();
         if (fColorCount == 2) {
@@ -541,8 +542,7 @@
         }
 
         if (fMapper) {
-            SkMallocPixelRef* newPR = SkNEW_ARGS(SkMallocPixelRef,
-                                                 (NULL, allocSize, NULL));
+            SkMallocPixelRef* newPR = SkMallocPixelRef::NewAllocate(info, 0, NULL);
             SkPMColor* linear = fCache32;           // just computed linear data
             SkPMColor* mapped = (SkPMColor*)newPR->getAddr();    // storage for mapped data
             SkUnitMapper* map = fMapper;
diff --git a/gpu/GrSurface.cpp b/gpu/GrSurface.cpp
index fed95f2..1fcc4ff 100644
--- a/gpu/GrSurface.cpp
+++ b/gpu/GrSurface.cpp
@@ -8,9 +8,19 @@
 #include "GrSurface.h"
 
 #include "SkBitmap.h"
+#include "SkGr.h"
 #include "SkImageEncoder.h"
 #include <stdio.h>
 
+void GrSurface::asImageInfo(SkImageInfo* info) const {
+    if (!GrPixelConfig2ColorType(this->config(), &info->fColorType)) {
+        sk_throw();
+    }
+    info->fWidth = this->width();
+    info->fHeight = this->height();
+    info->fAlphaType = kPremul_SkAlphaType;
+}
+
 bool GrSurface::savePixels(const char* filename) {
     SkBitmap bm;
     bm.setConfig(SkBitmap::kARGB_8888_Config, this->width(), this->height());