Home | History | Annotate | Download | only in core
      1 
      2 /*
      3  * Copyright 2006 The Android Open Source Project
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 
     10 #include "SkMaskFilter.h"
     11 #include "SkBlitter.h"
     12 #include "SkBounder.h"
     13 #include "SkDraw.h"
     14 #include "SkRasterClip.h"
     15 #include "SkRRect.h"
     16 #include "SkTypes.h"
     17 
     18 #if SK_SUPPORT_GPU
     19 #include "GrTexture.h"
     20 #include "SkGr.h"
     21 #include "SkGrPixelRef.h"
     22 #endif
     23 
     24 SK_DEFINE_INST_COUNT(SkMaskFilter)
     25 
     26 bool SkMaskFilter::filterMask(SkMask*, const SkMask&, const SkMatrix&,
     27                               SkIPoint*) const {
     28     return false;
     29 }
     30 
     31 static void extractMaskSubset(const SkMask& src, SkMask* dst) {
     32     SkASSERT(src.fBounds.contains(dst->fBounds));
     33 
     34     const int dx = dst->fBounds.left() - src.fBounds.left();
     35     const int dy = dst->fBounds.top() - src.fBounds.top();
     36     dst->fImage = src.fImage + dy * src.fRowBytes + dx;
     37     dst->fRowBytes = src.fRowBytes;
     38     dst->fFormat = src.fFormat;
     39 }
     40 
     41 static void blitClippedMask(SkBlitter* blitter, const SkMask& mask,
     42                             const SkIRect& bounds, const SkIRect& clipR) {
     43     SkIRect r;
     44     if (r.intersect(bounds, clipR)) {
     45         blitter->blitMask(mask, r);
     46     }
     47 }
     48 
     49 static void blitClippedRect(SkBlitter* blitter, const SkIRect& rect, const SkIRect& clipR) {
     50     SkIRect r;
     51     if (r.intersect(rect, clipR)) {
     52         blitter->blitRect(r.left(), r.top(), r.width(), r.height());
     53     }
     54 }
     55 
     56 #if 0
     57 static void dump(const SkMask& mask) {
     58     for (int y = mask.fBounds.top(); y < mask.fBounds.bottom(); ++y) {
     59         for (int x = mask.fBounds.left(); x < mask.fBounds.right(); ++x) {
     60             SkDebugf("%02X", *mask.getAddr8(x, y));
     61         }
     62         SkDebugf("\n");
     63     }
     64     SkDebugf("\n");
     65 }
     66 #endif
     67 
     68 static void draw_nine_clipped(const SkMask& mask, const SkIRect& outerR,
     69                               const SkIPoint& center, bool fillCenter,
     70                               const SkIRect& clipR, SkBlitter* blitter) {
     71     int cx = center.x();
     72     int cy = center.y();
     73     SkMask m;
     74 
     75     // top-left
     76     m.fBounds = mask.fBounds;
     77     m.fBounds.fRight = cx;
     78     m.fBounds.fBottom = cy;
     79     if (m.fBounds.width() > 0 && m.fBounds.height() > 0) {
     80         extractMaskSubset(mask, &m);
     81         m.fBounds.offsetTo(outerR.left(), outerR.top());
     82         blitClippedMask(blitter, m, m.fBounds, clipR);
     83     }
     84 
     85     // top-right
     86     m.fBounds = mask.fBounds;
     87     m.fBounds.fLeft = cx + 1;
     88     m.fBounds.fBottom = cy;
     89     if (m.fBounds.width() > 0 && m.fBounds.height() > 0) {
     90         extractMaskSubset(mask, &m);
     91         m.fBounds.offsetTo(outerR.right() - m.fBounds.width(), outerR.top());
     92         blitClippedMask(blitter, m, m.fBounds, clipR);
     93     }
     94 
     95     // bottom-left
     96     m.fBounds = mask.fBounds;
     97     m.fBounds.fRight = cx;
     98     m.fBounds.fTop = cy + 1;
     99     if (m.fBounds.width() > 0 && m.fBounds.height() > 0) {
    100         extractMaskSubset(mask, &m);
    101         m.fBounds.offsetTo(outerR.left(), outerR.bottom() - m.fBounds.height());
    102         blitClippedMask(blitter, m, m.fBounds, clipR);
    103     }
    104 
    105     // bottom-right
    106     m.fBounds = mask.fBounds;
    107     m.fBounds.fLeft = cx + 1;
    108     m.fBounds.fTop = cy + 1;
    109     if (m.fBounds.width() > 0 && m.fBounds.height() > 0) {
    110         extractMaskSubset(mask, &m);
    111         m.fBounds.offsetTo(outerR.right() - m.fBounds.width(),
    112                            outerR.bottom() - m.fBounds.height());
    113         blitClippedMask(blitter, m, m.fBounds, clipR);
    114     }
    115 
    116     SkIRect innerR;
    117     innerR.set(outerR.left() + cx - mask.fBounds.left(),
    118                outerR.top() + cy - mask.fBounds.top(),
    119                outerR.right() + (cx + 1 - mask.fBounds.right()),
    120                outerR.bottom() + (cy + 1 - mask.fBounds.bottom()));
    121     if (fillCenter) {
    122         blitClippedRect(blitter, innerR, clipR);
    123     }
    124 
    125     const int innerW = innerR.width();
    126     size_t storageSize = (innerW + 1) * (sizeof(int16_t) + sizeof(uint8_t));
    127     SkAutoSMalloc<4*1024> storage(storageSize);
    128     int16_t* runs = (int16_t*)storage.get();
    129     uint8_t* alpha = (uint8_t*)(runs + innerW + 1);
    130 
    131     SkIRect r;
    132     // top
    133     r.set(innerR.left(), outerR.top(), innerR.right(), innerR.top());
    134     if (r.intersect(clipR)) {
    135         int startY = SkMax32(0, r.top() - outerR.top());
    136         int stopY = startY + r.height();
    137         int width = r.width();
    138         for (int y = startY; y < stopY; ++y) {
    139             runs[0] = width;
    140             runs[width] = 0;
    141             alpha[0] = *mask.getAddr8(cx, mask.fBounds.top() + y);
    142             blitter->blitAntiH(r.left(), outerR.top() + y, alpha, runs);
    143         }
    144     }
    145     // bottom
    146     r.set(innerR.left(), innerR.bottom(), innerR.right(), outerR.bottom());
    147     if (r.intersect(clipR)) {
    148         int startY = outerR.bottom() - r.bottom();
    149         int stopY = startY + r.height();
    150         int width = r.width();
    151         for (int y = startY; y < stopY; ++y) {
    152             runs[0] = width;
    153             runs[width] = 0;
    154             alpha[0] = *mask.getAddr8(cx, mask.fBounds.bottom() - y - 1);
    155             blitter->blitAntiH(r.left(), outerR.bottom() - y - 1, alpha, runs);
    156         }
    157     }
    158     // left
    159     r.set(outerR.left(), innerR.top(), innerR.left(), innerR.bottom());
    160     if (r.intersect(clipR)) {
    161         int startX = r.left() - outerR.left();
    162         int stopX = startX + r.width();
    163         int height = r.height();
    164         for (int x = startX; x < stopX; ++x) {
    165             blitter->blitV(outerR.left() + x, r.top(), height,
    166                            *mask.getAddr8(mask.fBounds.left() + x, mask.fBounds.top() + cy));
    167         }
    168     }
    169     // right
    170     r.set(innerR.right(), innerR.top(), outerR.right(), innerR.bottom());
    171     if (r.intersect(clipR)) {
    172         int startX = outerR.right() - r.right();
    173         int stopX = startX + r.width();
    174         int height = r.height();
    175         for (int x = startX; x < stopX; ++x) {
    176             blitter->blitV(outerR.right() - x - 1, r.top(), height,
    177                            *mask.getAddr8(mask.fBounds.right() - x - 1, mask.fBounds.top() + cy));
    178         }
    179     }
    180 }
    181 
    182 static void draw_nine(const SkMask& mask, const SkIRect& outerR,
    183                       const SkIPoint& center, bool fillCenter,
    184                       const SkRasterClip& clip, SkBounder* bounder,
    185                       SkBlitter* blitter) {
    186     // if we get here, we need to (possibly) resolve the clip and blitter
    187     SkAAClipBlitterWrapper wrapper(clip, blitter);
    188     blitter = wrapper.getBlitter();
    189 
    190     SkRegion::Cliperator clipper(wrapper.getRgn(), outerR);
    191 
    192     if (!clipper.done() && (!bounder || bounder->doIRect(outerR))) {
    193         const SkIRect& cr = clipper.rect();
    194         do {
    195             draw_nine_clipped(mask, outerR, center, fillCenter, cr, blitter);
    196             clipper.next();
    197         } while (!clipper.done());
    198     }
    199 }
    200 
    201 static int countNestedRects(const SkPath& path, SkRect rects[2]) {
    202     if (path.isNestedRects(rects)) {
    203         return 2;
    204     }
    205     return path.isRect(&rects[0]);
    206 }
    207 
    208 bool SkMaskFilter::filterRRect(const SkRRect& devRRect, const SkMatrix& matrix,
    209                                const SkRasterClip& clip, SkBounder* bounder,
    210                                SkBlitter* blitter, SkPaint::Style style) const {
    211     // Attempt to speed up drawing by creating a nine patch. If a nine patch
    212     // cannot be used, return false to allow our caller to recover and perform
    213     // the drawing another way.
    214     NinePatch patch;
    215     patch.fMask.fImage = NULL;
    216     if (kTrue_FilterReturn != this->filterRRectToNine(devRRect, matrix,
    217                                                       clip.getBounds(),
    218                                                       &patch)) {
    219         SkASSERT(NULL == patch.fMask.fImage);
    220         return false;
    221     }
    222     draw_nine(patch.fMask, patch.fOuterRect, patch.fCenter, true, clip,
    223               bounder, blitter);
    224     SkMask::FreeImage(patch.fMask.fImage);
    225     return true;
    226 }
    227 
    228 bool SkMaskFilter::filterPath(const SkPath& devPath, const SkMatrix& matrix,
    229                               const SkRasterClip& clip, SkBounder* bounder,
    230                               SkBlitter* blitter, SkPaint::Style style) const {
    231     SkRect rects[2];
    232     int rectCount = 0;
    233     if (SkPaint::kFill_Style == style) {
    234         rectCount = countNestedRects(devPath, rects);
    235     }
    236     if (rectCount > 0) {
    237         NinePatch patch;
    238 
    239         patch.fMask.fImage = NULL;
    240         switch (this->filterRectsToNine(rects, rectCount, matrix,
    241                                         clip.getBounds(), &patch)) {
    242             case kFalse_FilterReturn:
    243                 SkASSERT(NULL == patch.fMask.fImage);
    244                 return false;
    245 
    246             case kTrue_FilterReturn:
    247                 draw_nine(patch.fMask, patch.fOuterRect, patch.fCenter,
    248                           1 == rectCount, clip, bounder, blitter);
    249                 SkMask::FreeImage(patch.fMask.fImage);
    250                 return true;
    251 
    252             case kUnimplemented_FilterReturn:
    253                 SkASSERT(NULL == patch.fMask.fImage);
    254                 // fall through
    255                 break;
    256         }
    257     }
    258 
    259     SkMask  srcM, dstM;
    260 
    261     if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), this, &matrix, &srcM,
    262                             SkMask::kComputeBoundsAndRenderImage_CreateMode,
    263                             style)) {
    264         return false;
    265     }
    266     SkAutoMaskFreeImage autoSrc(srcM.fImage);
    267 
    268     if (!this->filterMask(&dstM, srcM, matrix, NULL)) {
    269         return false;
    270     }
    271     SkAutoMaskFreeImage autoDst(dstM.fImage);
    272 
    273     // if we get here, we need to (possibly) resolve the clip and blitter
    274     SkAAClipBlitterWrapper wrapper(clip, blitter);
    275     blitter = wrapper.getBlitter();
    276 
    277     SkRegion::Cliperator clipper(wrapper.getRgn(), dstM.fBounds);
    278 
    279     if (!clipper.done() && (bounder == NULL || bounder->doIRect(dstM.fBounds))) {
    280         const SkIRect& cr = clipper.rect();
    281         do {
    282             blitter->blitMask(dstM, cr);
    283             clipper.next();
    284         } while (!clipper.done());
    285     }
    286 
    287     return true;
    288 }
    289 
    290 SkMaskFilter::FilterReturn
    291 SkMaskFilter::filterRRectToNine(const SkRRect&, const SkMatrix&,
    292                                 const SkIRect& clipBounds, NinePatch*) const {
    293     return kUnimplemented_FilterReturn;
    294 }
    295 
    296 SkMaskFilter::FilterReturn
    297 SkMaskFilter::filterRectsToNine(const SkRect[], int count, const SkMatrix&,
    298                                 const SkIRect& clipBounds, NinePatch*) const {
    299     return kUnimplemented_FilterReturn;
    300 }
    301 
    302 #if SK_SUPPORT_GPU
    303 bool SkMaskFilter::asNewEffect(GrEffectRef** effect, GrTexture*) const {
    304     return false;
    305 }
    306 
    307 bool SkMaskFilter::canFilterMaskGPU(const SkRect& devBounds,
    308                                     const SkIRect& clipBounds,
    309                                     const SkMatrix& ctm,
    310                                     SkRect* maskRect) const {
    311     return false;
    312 }
    313 
    314 bool SkMaskFilter::filterMaskGPU(GrContext* context,
    315                                  const SkBitmap& srcBM,
    316                                  const SkRect& maskRect,
    317                                  SkBitmap* resultBM) const {
    318     SkAutoTUnref<GrTexture> src;
    319     bool canOverwriteSrc = false;
    320     if (NULL == srcBM.getTexture()) {
    321         GrTextureDesc desc;
    322         // Needs to be a render target to be overwritten in filterMaskGPU
    323         desc.fFlags     = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
    324         desc.fConfig    = SkBitmapConfig2GrPixelConfig(srcBM.config());
    325         desc.fWidth     = srcBM.width();
    326         desc.fHeight    = srcBM.height();
    327 
    328         // TODO: right now this is exact to guard against out of bounds reads
    329         // by the filter code. More thought needs to be devoted to the
    330         // "filterMaskGPU" contract and then enforced (i.e., clamp the code
    331         // in "filterMaskGPU" so it never samples beyond maskRect)
    332         GrAutoScratchTexture ast(context, desc, GrContext::kExact_ScratchTexMatch);
    333         if (NULL == ast.texture()) {
    334             return false;
    335         }
    336 
    337         SkAutoLockPixels alp(srcBM);
    338         ast.texture()->writePixels(0, 0, srcBM.width(), srcBM.height(),
    339                                    desc.fConfig,
    340                                    srcBM.getPixels(), srcBM.rowBytes());
    341 
    342         src.reset(ast.detach());
    343         canOverwriteSrc = true;
    344     } else {
    345         src.reset((GrTexture*) srcBM.getTexture());
    346         src.get()->ref();
    347     }
    348     GrTexture* dst;
    349 
    350     bool result = this->filterMaskGPU(src, maskRect, &dst, canOverwriteSrc);
    351     if (!result) {
    352         return false;
    353     }
    354 
    355     resultBM->setConfig(srcBM.config(), dst->width(), dst->height());
    356     resultBM->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (dst)))->unref();
    357     dst->unref();
    358     return true;
    359 }
    360 
    361 bool SkMaskFilter::filterMaskGPU(GrTexture* src,
    362                                  const SkRect& maskRect,
    363                                  GrTexture** result,
    364                                  bool canOverwriteSrc) const {
    365     return false;
    366 }
    367 #endif
    368 
    369 void SkMaskFilter::computeFastBounds(const SkRect& src, SkRect* dst) const {
    370     SkMask  srcM, dstM;
    371 
    372     srcM.fImage = NULL;
    373     src.roundOut(&srcM.fBounds);
    374     srcM.fRowBytes = 0;
    375     srcM.fFormat = SkMask::kA8_Format;
    376 
    377     SkIPoint margin;    // ignored
    378     if (this->filterMask(&dstM, srcM, SkMatrix::I(), &margin)) {
    379         dst->set(dstM.fBounds);
    380     } else {
    381         dst->set(srcM.fBounds);
    382     }
    383 }
    384