add ADD xfermode
safely handle (don't draw) if the size of SkMask exceeds 31bits



git-svn-id: http://skia.googlecode.com/svn/trunk@159 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp
index 93b5d4e..e3e5c49 100644
--- a/src/core/SkDraw.cpp
+++ b/src/core/SkDraw.cpp
@@ -911,11 +911,16 @@
                 return;
             }
         }
+
         mask.fFormat = SkMask::kA8_Format;
         mask.fRowBytes = SkAlign4(mask.fBounds.width());
+        size_t size = mask.computeImageSize();
+        if (0 == size) {
+            // the mask is too big to allocated, draw nothing
+            return;
+        }
 
         // allocate (and clear) our temp buffer to hold the transformed bitmap
-        size_t size = mask.computeImageSize();
         SkAutoMalloc    storage(size);
         mask.fImage = (uint8_t*)storage.get();
         memset(mask.fImage, 0, size);
@@ -2350,7 +2355,12 @@
     if (SkMask::kComputeBoundsAndRenderImage_CreateMode == mode) {
         mask->fFormat = SkMask::kA8_Format;
         mask->fRowBytes = mask->fBounds.width();
-        mask->fImage = SkMask::AllocImage(mask->computeImageSize());
+        size_t size = mask->computeImageSize();
+        if (0 == size) {
+            // we're too big to allocate the mask, abort
+            return false;
+        }
+        mask->fImage = SkMask::AllocImage(size);
         memset(mask->fImage, 0, mask->computeImageSize());
     }
 
diff --git a/src/core/SkMask.cpp b/src/core/SkMask.cpp
index b237639..337204b 100644
--- a/src/core/SkMask.cpp
+++ b/src/core/SkMask.cpp
@@ -15,19 +15,30 @@
 ** limitations under the License.
 */
 
+#include "Sk64.h"
 #include "SkMask.h"
 
-size_t SkMask::computeImageSize() const
-{
-    return fBounds.height() * fRowBytes;
+/** returns the product if it is positive and fits in 31 bits. Otherwise this
+    returns 0.
+ */
+static int32_t safeMul32(int32_t a, int32_t b) {
+    Sk64 size;
+    size.setMul(a, b);
+    if (size.is32() && size.isPos()) {
+        return size.get32();
+    }
+    return 0;
 }
 
-size_t SkMask::computeTotalImageSize() const
-{
-    size_t size = this->computeImageSize();
+size_t SkMask::computeImageSize() const {
+    return safeMul32(fBounds.height(), fRowBytes);
+}
 
-    if (fFormat == SkMask::k3D_Format)
-        size *= 3;
+size_t SkMask::computeTotalImageSize() const {
+    size_t size = this->computeImageSize();
+    if (fFormat == SkMask::k3D_Format) {
+        size = safeMul32(size, 3);
+    }
     return size;
 }
 
diff --git a/src/core/SkXfermode.cpp b/src/core/SkXfermode.cpp
index 7c336c2..9514bfa 100644
--- a/src/core/SkXfermode.cpp
+++ b/src/core/SkXfermode.cpp
@@ -45,6 +45,16 @@
 }
 #endif
 
+static unsigned saturated_add(unsigned a, unsigned b) {
+    SkASSERT(a <= 255);
+    SkASSERT(b <= 255);
+    unsigned sum = a + b;
+    if (sum > 255) {
+        sum = 255;
+    }
+    return sum;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 bool SkXfermode::asCoeff(Coeff* src, Coeff* dst) {
@@ -500,6 +510,14 @@
     return SkPackARGB32(a, r, g, b);
 }
 
+static SkPMColor add_modeproc(SkPMColor src, SkPMColor dst) {
+    unsigned a = saturated_add(SkGetPackedA32(src), SkGetPackedA32(dst));
+    unsigned r = saturated_add(SkGetPackedR32(src), SkGetPackedR32(dst));
+    unsigned g = saturated_add(SkGetPackedG32(src), SkGetPackedG32(dst));
+    unsigned b = saturated_add(SkGetPackedB32(src), SkGetPackedB32(dst));
+    return SkPackARGB32(a, r, g, b);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 class SkClearXfermode : public SkProcCoeffXfermode {
@@ -701,6 +719,8 @@
     SkXfermode::Coeff   fDC;
 };
 
+#define CANNOT_USE_COEFF    SkXfermode::Coeff(-1)
+
 static const ProcCoeff gProcCoeffs[] = {
     { clear_modeproc,   SkXfermode::kZero_Coeff,    SkXfermode::kZero_Coeff },
     { src_modeproc,     SkXfermode::kOne_Coeff,     SkXfermode::kZero_Coeff },
@@ -714,12 +734,11 @@
     { srcatop_modeproc, SkXfermode::kDA_Coeff,      SkXfermode::kISA_Coeff },
     { dstatop_modeproc, SkXfermode::kIDA_Coeff,     SkXfermode::kSA_Coeff },
     { xor_modeproc,     SkXfermode::kIDA_Coeff,     SkXfermode::kISA_Coeff },
-    // these two can't be represented as coefficients
-    { darken_modeproc,  SkXfermode::Coeff(-1),      SkXfermode::Coeff(-1) },
-    { lighten_modeproc, SkXfermode::Coeff(-1),      SkXfermode::Coeff(-1) },
-    // these can use coefficients
+    { darken_modeproc,  CANNOT_USE_COEFF,           CANNOT_USE_COEFF },
+    { lighten_modeproc, CANNOT_USE_COEFF,           CANNOT_USE_COEFF },
     { mult_modeproc,    SkXfermode::kZero_Coeff,    SkXfermode::kSC_Coeff },
-    { screen_modeproc,  SkXfermode::kOne_Coeff,     SkXfermode::kISC_Coeff }
+    { screen_modeproc,  SkXfermode::kOne_Coeff,     SkXfermode::kISC_Coeff },
+    { add_modeproc,     CANNOT_USE_COEFF,           CANNOT_USE_COEFF }
 };
 
 SkXfermode* SkPorterDuff::CreateXfermode(SkPorterDuff::Mode mode) {
diff --git a/src/effects/SkBlurMask.cpp b/src/effects/SkBlurMask.cpp
index 416eda4..8c7c64e 100644
--- a/src/effects/SkBlurMask.cpp
+++ b/src/effects/SkBlurMask.cpp
@@ -234,10 +234,15 @@
 
     if (src.fImage)
     {
+        size_t dstSize = dst->computeImageSize();
+        if (0 == dstSize) {
+            return false;   // too big to allocate, abort
+        }
+
         int             sw = src.fBounds.width();
         int             sh = src.fBounds.height();
         const uint8_t*  sp = src.fImage;
-        uint8_t*        dp = SkMask::AllocImage(dst->computeImageSize());
+        uint8_t*        dp = SkMask::AllocImage(dstSize);
 
         SkAutoTCallVProc<uint8_t, SkMask_FreeImage> autoCall(dp);
 
@@ -258,7 +263,11 @@
         // the blur into it (applying the src)
         if (style == kInner_Style)
         {
-            dst->fImage = SkMask::AllocImage(src.computeImageSize());
+            size_t srcSize = src.computeImageSize();
+            if (0 == srcSize) {
+                return false;   // too big to allocate, abort
+            }
+            dst->fImage = SkMask::AllocImage(srcSize);
             merge_src_with_blur(dst->fImage, sp, sw, sh,
                                 dp + rx + ry*dst->fBounds.width(),
                                 dst->fBounds.width());
diff --git a/src/effects/SkEmbossMaskFilter.cpp b/src/effects/SkEmbossMaskFilter.cpp
index fcfad9d..67c8024 100644
--- a/src/effects/SkEmbossMaskFilter.cpp
+++ b/src/effects/SkEmbossMaskFilter.cpp
@@ -88,7 +88,9 @@
     {
         uint8_t* alphaPlane = dst->fImage;
         size_t   planeSize = dst->computeImageSize();
-
+        if (0 == planeSize) {
+            return false;   // too big to allocate, abort
+        }
         dst->fImage = SkMask::AllocImage(planeSize * 3);
         memcpy(dst->fImage, alphaPlane, planeSize);
         SkMask::FreeImage(alphaPlane);
diff --git a/src/effects/SkKernel33MaskFilter.cpp b/src/effects/SkKernel33MaskFilter.cpp
index a30ea4a..429e120 100644
--- a/src/effects/SkKernel33MaskFilter.cpp
+++ b/src/effects/SkKernel33MaskFilter.cpp
@@ -19,6 +19,9 @@
     
     dst->fRowBytes = dst->fBounds.width();
     size_t size = dst->computeImageSize();
+    if (0 == size) {
+        return false;   // too big to allocate, abort
+    }
     dst->fImage = SkMask::AllocImage(size);
     
     const int h = src.fBounds.height();
diff --git a/src/effects/SkLayerRasterizer.cpp b/src/effects/SkLayerRasterizer.cpp
index ad16ea8..5aa883d 100644
--- a/src/effects/SkLayerRasterizer.cpp
+++ b/src/effects/SkLayerRasterizer.cpp
@@ -108,8 +108,12 @@
     {
         mask->fFormat   = SkMask::kA8_Format;
         mask->fRowBytes = mask->fBounds.width();
-        mask->fImage = SkMask::AllocImage(mask->computeImageSize());
-        memset(mask->fImage, 0, mask->computeImageSize());
+        size_t size = mask->computeImageSize();
+        if (0 == size) {
+            return false;   // too big to allocate, abort
+        }
+        mask->fImage = SkMask::AllocImage(size);
+        memset(mask->fImage, 0, size);
     }
 
     if (SkMask::kJustComputeBounds_CreateMode != mode)