Detect color masks, and divert to draw-sprite instead of maskblitters.
authorreed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 2 May 2013 18:55:44 +0000 (18:55 +0000)
committerreed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 2 May 2013 18:55:44 +0000 (18:55 +0000)
Review URL: https://codereview.chromium.org/14637007

git-svn-id: http://skia.googlecode.com/svn/trunk@8967 2bbb7eff-a529-9590-31e7-b0007b416f81

include/core/SkMask.h
src/core/SkDraw.cpp
src/core/SkDrawProcs.h
src/core/SkScalerContext.cpp

index 4265595..4cf3ae7 100644 (file)
@@ -98,7 +98,20 @@ struct SkMask {
         uint32_t* row = (uint32_t*)(fImage + (y - fBounds.fTop) * fRowBytes);
         return row + (x - fBounds.fLeft);
     }
-
+    
+    /**
+     *  Return the address of the specified 32bit mask. In the debug build,
+     *  this asserts that the mask's format is 32bits, and that (x,y)
+     *  are contained in the mask's fBounds.
+     */
+    uint32_t* getAddr32(int x, int y) const {
+        SkASSERT(kLCD32_Format == fFormat || kARGB32_Format == fFormat);
+        SkASSERT(fBounds.contains(x, y));
+        SkASSERT(fImage != NULL);
+        uint32_t* row = (uint32_t*)(fImage + (y - fBounds.fTop) * fRowBytes);
+        return row + (x - fBounds.fLeft);
+    }
+    
     /**
      *  Returns the address of the specified pixel, computing the pixel-size
      *  at runtime based on the mask format. This will be slightly slower than
index 3c3e3a3..fc615ab 100644 (file)
@@ -1489,7 +1489,7 @@ static void D1G_NoBounder_RectClip(const SkDraw1Glyph& state,
     mask.fRowBytes = glyph.rowBytes();
     mask.fFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
     mask.fImage = aa;
-    state.fBlitter->blitMask(mask, *bounds);
+    state.blitMask(mask, *bounds);
 }
 
 static void D1G_NoBounder_RgnClip(const SkDraw1Glyph& state,
@@ -1523,7 +1523,7 @@ static void D1G_NoBounder_RgnClip(const SkDraw1Glyph& state,
         mask.fFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
         mask.fImage = (uint8_t*)aa;
         do {
-            state.fBlitter->blitMask(mask, cr);
+            state.blitMask(mask, cr);
             clipper.next();
         } while (!clipper.done());
     }
@@ -1563,7 +1563,7 @@ static void D1G_Bounder(const SkDraw1Glyph& state,
             mask.fFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
             mask.fImage = (uint8_t*)aa;
             do {
-                state.fBlitter->blitMask(mask, cr);
+                state.blitMask(mask, cr);
                 clipper.next();
             } while (!clipper.done());
         }
@@ -1592,11 +1592,12 @@ static bool needsRasterTextBlit(const SkDraw& draw) {
 }
 
 SkDraw1Glyph::Proc SkDraw1Glyph::init(const SkDraw* draw, SkBlitter* blitter,
-                                      SkGlyphCache* cache) {
+                                      SkGlyphCache* cache, const SkPaint& pnt) {
     fDraw = draw;
     fBounder = draw->fBounder;
     fBlitter = blitter;
     fCache = cache;
+    fPaint = &pnt;
 
     if (cache->isSubpixel()) {
         fHalfSampleX = fHalfSampleY = (SK_FixedHalf >> SkGlyph::kSubBits);
@@ -1636,6 +1637,17 @@ SkDraw1Glyph::Proc SkDraw1Glyph::init(const SkDraw* draw, SkBlitter* blitter,
     }
 }
 
+void SkDraw1Glyph::blitMaskAsSprite(const SkMask& mask) const {
+    SkASSERT(SkMask::kARGB32_Format == mask.fFormat);
+
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kARGB_8888_Config,
+                 mask.fBounds.width(), mask.fBounds.height(), mask.fRowBytes);
+    bm.setPixels((SkPMColor*)mask.fImage);
+
+    fDraw->drawSprite(bm, mask.fBounds.x(), mask.fBounds.y(), *fPaint);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 void SkDraw::drawText(const char text[], size_t byteLength,
@@ -1704,7 +1716,7 @@ void SkDraw::drawText(const char text[], size_t byteLength,
 
     SkAutoKern          autokern;
     SkDraw1Glyph        d1g;
-    SkDraw1Glyph::Proc  proc = d1g.init(this, blitter, cache);
+    SkDraw1Glyph::Proc  proc = d1g.init(this, blitter, cache, paint);
 
     SkFixed fxMask = ~0;
     SkFixed fyMask = ~0;
@@ -1866,7 +1878,7 @@ void SkDraw::drawPosText(const char text[], size_t byteLength,
     const char*        stop = text + byteLength;
     AlignProc          alignProc = pick_align_proc(paint.getTextAlign());
     SkDraw1Glyph       d1g;
-    SkDraw1Glyph::Proc proc = d1g.init(this, blitter, cache);
+    SkDraw1Glyph::Proc proc = d1g.init(this, blitter, cache, paint);
     TextMapState       tms(*fMatrix, constY);
     TextMapState::Proc tmsProc = tms.pickProc(scalarsPerPosition);
 
index 68cc9ff..8b8c382 100644 (file)
@@ -8,6 +8,7 @@
 #ifndef SkDrawProcs_DEFINED
 #define SkDrawProcs_DEFINED
 
+#include "SkBlitter.h"
 #include "SkDraw.h"
 
 class SkAAClip;
@@ -20,6 +21,7 @@ struct SkDraw1Glyph {
     const SkAAClip* fAAClip;
     SkBlitter* fBlitter;
     SkGlyphCache* fCache;
+    const SkPaint* fPaint;
     SkIRect fClipBounds;
     /** Half the sampling frequency of the rasterized glyph in x. */
     SkFixed fHalfSampleX;
@@ -35,7 +37,22 @@ struct SkDraw1Glyph {
      */
     typedef void (*Proc)(const SkDraw1Glyph&, SkFixed x, SkFixed y, const SkGlyph&);
 
-    Proc init(const SkDraw* draw, SkBlitter* blitter, SkGlyphCache* cache);
+    Proc init(const SkDraw* draw, SkBlitter* blitter, SkGlyphCache* cache,
+              const SkPaint&);
+
+    // call this instead of fBlitter->blitMask() since this wrapper will handle
+    // the case when the mask is ARGB32_Format
+    //
+    void blitMask(const SkMask& mask, const SkIRect& clip) const {
+        if (SkMask::kARGB32_Format == mask.fFormat) {
+            this->blitMaskAsSprite(mask);
+        } else {
+            fBlitter->blitMask(mask, clip);
+        }
+    }
+
+    // mask must be kARGB32_Format
+    void blitMaskAsSprite(const SkMask& mask) const;
 };
 
 struct SkDrawProcs {
index c02dbca..fa4f863 100644 (file)
@@ -337,6 +337,12 @@ void SkScalerContext::getMetrics(SkGlyph* glyph) {
         glyph->fMaskFormat = fRec.fMaskFormat;
     }
 
+    // If we are going to create the mask, then we cannot keep the color
+    if ((fGenerateImageFromPath || fMaskFilter) &&
+            SkMask::kARGB32_Format == glyph->fMaskFormat) {
+        glyph->fMaskFormat = SkMask::kA8_Format;
+    }
+
     if (fMaskFilter) {
         SkMask      src, dst;
         SkMatrix    matrix;
@@ -515,10 +521,40 @@ static void generateMask(const SkMask& mask, const SkPath& path,
     }
 }
 
+static void extract_alpha(const SkMask& dst,
+                          const SkPMColor* srcRow, size_t srcRB) {
+    int width = dst.fBounds.width();
+    int height = dst.fBounds.height();
+    int dstRB = dst.fRowBytes;
+    uint8_t* dstRow = dst.fImage;
+
+    for (int y = 0; y < height; ++y) {
+        for (int x = 0; x < width; ++x) {
+            dstRow[x] = SkGetPackedA32(srcRow[x]);
+        }
+        // zero any padding on each row
+        for (int x = width; x < dstRB; ++x) {
+            dstRow[x] = 0;
+        }
+        dstRow += dstRB;
+        srcRow = (const SkPMColor*)((const char*)srcRow + srcRB);
+    }
+}
+
 void SkScalerContext::getImage(const SkGlyph& origGlyph) {
     const SkGlyph*  glyph = &origGlyph;
     SkGlyph         tmpGlyph;
 
+    // in case we need to call generateImage on a mask-format that is different
+    // (i.e. larger) than what our caller allocated by looking at origGlyph.
+    SkAutoMalloc tmpGlyphImageStorage;
+
+    // If we are going to draw-from-path, then we cannot generate color, since
+    // the path only makes a mask. This case should have been caught up in
+    // generateMetrics().
+    SkASSERT(!fGenerateImageFromPath ||
+             SkMask::kARGB32_Format != origGlyph.fMaskFormat);
+
     if (fMaskFilter) {   // restore the prefilter bounds
         tmpGlyph.init(origGlyph.fID);
 
@@ -528,11 +564,16 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) {
         this->getMetrics(&tmpGlyph);
         fMaskFilter = mf;               // restore
 
-        tmpGlyph.fImage = origGlyph.fImage;
-
         // we need the prefilter bounds to be <= filter bounds
         SkASSERT(tmpGlyph.fWidth <= origGlyph.fWidth);
         SkASSERT(tmpGlyph.fHeight <= origGlyph.fHeight);
+
+        if (tmpGlyph.fMaskFormat == origGlyph.fMaskFormat) {
+            tmpGlyph.fImage = origGlyph.fImage;
+        } else {
+            tmpGlyphImageStorage.reset(tmpGlyph.computeImageSize());
+            tmpGlyph.fImage = tmpGlyphImageStorage.get();
+        }
         glyph = &tmpGlyph;
     }
 
@@ -557,6 +598,7 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) {
                 applyLUTToA8Mask(mask, fPreBlend.fG);
             }
         } else {
+            SkASSERT(SkMask::kARGB32_Format != mask.fFormat);
             generateMask(mask, devPath, fPreBlend);
         }
     } else {
@@ -569,7 +611,21 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) {
 
         // the src glyph image shouldn't be 3D
         SkASSERT(SkMask::k3D_Format != glyph->fMaskFormat);
+
+        SkAutoSMalloc<32*32> a8storage;
         glyph->toMask(&srcM);
+        if (SkMask::kARGB32_Format == srcM.fFormat) {
+            // now we need to extract the alpha-channel from the glyph's image
+            // and copy it into a temp buffer, and then point srcM at that temp.
+            srcM.fFormat = SkMask::kA8_Format;
+            srcM.fRowBytes = SkAlign4(srcM.fBounds.width());
+            size_t size = srcM.computeImageSize();
+            a8storage.reset(size);
+            srcM.fImage = (uint8_t*)a8storage.get();
+            extract_alpha(srcM,
+                          (const SkPMColor*)glyph->fImage, glyph->rowBytes());
+        }
+        
         fRec.getMatrixFrom2x2(&matrix);
 
         if (fMaskFilter->filterMask(&dstM, srcM, matrix, NULL)) {