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