blob: f062f135fdbebbd4f5016bfde05dc0a424e04ccb [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
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
reed@android.com8a1c16f2008-12-17 15:59:43 +00009
10#include "SkMaskFilter.h"
11#include "SkBlitter.h"
12#include "SkBounder.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkDraw.h"
reed@google.com045e62d2011-10-24 12:19:46 +000014#include "SkRasterClip.h"
scroggo@google.coma8e33a92013-11-08 18:02:53 +000015#include "SkRRect.h"
robertphillips@google.com49149312013-07-03 15:34:35 +000016#include "SkTypes.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000017
robertphillips@google.com0b828522013-07-03 15:49:05 +000018#if SK_SUPPORT_GPU
19#include "GrTexture.h"
20#include "SkGr.h"
21#include "SkGrPixelRef.h"
22#endif
reed@google.comd729b3e2012-11-09 14:30:48 +000023
reed@google.comfeb8cc82011-04-19 20:11:25 +000024bool SkMaskFilter::filterMask(SkMask*, const SkMask&, const SkMatrix&,
reed@google.com30711b72012-12-18 19:18:39 +000025 SkIPoint*) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +000026 return false;
27}
28
reed@google.comd729b3e2012-11-09 14:30:48 +000029static 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,
reed@google.comdab9b4f2012-11-19 16:45:14 +000067 const SkIPoint& center, bool fillCenter,
reed@google.comd729b3e2012-11-09 14:30:48 +000068 const SkIRect& clipR, SkBlitter* blitter) {
reed@google.comdab9b4f2012-11-19 16:45:14 +000069 int cx = center.x();
70 int cy = center.y();
reed@google.comd729b3e2012-11-09 14:30:48 +000071 SkMask m;
skia.committer@gmail.com453995e2012-11-10 02:01:26 +000072
reed@google.comd729b3e2012-11-09 14:30:48 +000073 // top-left
74 m.fBounds = mask.fBounds;
75 m.fBounds.fRight = cx;
76 m.fBounds.fBottom = cy;
tomhudson@google.com3c0ecc52013-04-25 16:56:40 +000077 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 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +000082
reed@google.comd729b3e2012-11-09 14:30:48 +000083 // top-right
84 m.fBounds = mask.fBounds;
85 m.fBounds.fLeft = cx + 1;
86 m.fBounds.fBottom = cy;
tomhudson@google.com3c0ecc52013-04-25 16:56:40 +000087 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 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +000092
reed@google.comd729b3e2012-11-09 14:30:48 +000093 // bottom-left
94 m.fBounds = mask.fBounds;
95 m.fBounds.fRight = cx;
96 m.fBounds.fTop = cy + 1;
tomhudson@google.com3c0ecc52013-04-25 16:56:40 +000097 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 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +0000102
reed@google.comd729b3e2012-11-09 14:30:48 +0000103 // bottom-right
104 m.fBounds = mask.fBounds;
105 m.fBounds.fLeft = cx + 1;
106 m.fBounds.fTop = cy + 1;
tomhudson@google.com3c0ecc52013-04-25 16:56:40 +0000107 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 }
reed@google.comd729b3e2012-11-09 14:30:48 +0000113
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()));
reed@google.comdab9b4f2012-11-19 16:45:14 +0000119 if (fillCenter) {
120 blitClippedRect(blitter, innerR, clipR);
121 }
reed@google.comd729b3e2012-11-09 14:30:48 +0000122
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,
reed@google.comdab9b4f2012-11-19 16:45:14 +0000181 const SkIPoint& center, bool fillCenter,
reed@google.comd729b3e2012-11-09 14:30:48 +0000182 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();
skia.committer@gmail.com453995e2012-11-10 02:01:26 +0000187
reed@google.comd729b3e2012-11-09 14:30:48 +0000188 SkRegion::Cliperator clipper(wrapper.getRgn(), outerR);
skia.committer@gmail.com453995e2012-11-10 02:01:26 +0000189
reed@google.comd729b3e2012-11-09 14:30:48 +0000190 if (!clipper.done() && (!bounder || bounder->doIRect(outerR))) {
191 const SkIRect& cr = clipper.rect();
192 do {
reed@google.comdab9b4f2012-11-19 16:45:14 +0000193 draw_nine_clipped(mask, outerR, center, fillCenter, cr, blitter);
reed@google.comd729b3e2012-11-09 14:30:48 +0000194 clipper.next();
195 } while (!clipper.done());
196 }
197}
198
reed@google.comdab9b4f2012-11-19 16:45:14 +0000199static 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
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000206bool 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
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226bool SkMaskFilter::filterPath(const SkPath& devPath, const SkMatrix& matrix,
reed@google.com045e62d2011-10-24 12:19:46 +0000227 const SkRasterClip& clip, SkBounder* bounder,
reed@google.com30711b72012-12-18 19:18:39 +0000228 SkBlitter* blitter, SkPaint::Style style) const {
reed@google.comdab9b4f2012-11-19 16:45:14 +0000229 SkRect rects[2];
230 int rectCount = 0;
reed@google.com5f0add32012-11-26 16:47:10 +0000231 if (SkPaint::kFill_Style == style) {
reed@google.comdab9b4f2012-11-19 16:45:14 +0000232 rectCount = countNestedRects(devPath, rects);
233 }
234 if (rectCount > 0) {
235 NinePatch patch;
reed@google.comd729b3e2012-11-09 14:30:48 +0000236
reed@google.comdab9b4f2012-11-19 16:45:14 +0000237 patch.fMask.fImage = NULL;
238 switch (this->filterRectsToNine(rects, rectCount, matrix,
239 clip.getBounds(), &patch)) {
reed@google.comd729b3e2012-11-09 14:30:48 +0000240 case kFalse_FilterReturn:
reed@google.comdab9b4f2012-11-19 16:45:14 +0000241 SkASSERT(NULL == patch.fMask.fImage);
reed@google.comd729b3e2012-11-09 14:30:48 +0000242 return false;
reed@google.comdab9b4f2012-11-19 16:45:14 +0000243
reed@google.comd729b3e2012-11-09 14:30:48 +0000244 case kTrue_FilterReturn:
reed@google.comdab9b4f2012-11-19 16:45:14 +0000245 draw_nine(patch.fMask, patch.fOuterRect, patch.fCenter,
246 1 == rectCount, clip, bounder, blitter);
247 SkMask::FreeImage(patch.fMask.fImage);
reed@google.comd729b3e2012-11-09 14:30:48 +0000248 return true;
reed@google.comdab9b4f2012-11-19 16:45:14 +0000249
reed@google.comd729b3e2012-11-09 14:30:48 +0000250 case kUnimplemented_FilterReturn:
reed@google.comdab9b4f2012-11-19 16:45:14 +0000251 SkASSERT(NULL == patch.fMask.fImage);
reed@google.comd729b3e2012-11-09 14:30:48 +0000252 // fall through
253 break;
254 }
255 }
256
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 SkMask srcM, dstM;
258
259 if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), this, &matrix, &srcM,
junov@chromium.org2ac4ef52012-04-04 15:16:51 +0000260 SkMask::kComputeBoundsAndRenderImage_CreateMode,
261 style)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262 return false;
263 }
bungeman@google.com02f55842011-10-04 21:25:00 +0000264 SkAutoMaskFreeImage autoSrc(srcM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265
reed@google.comfeb8cc82011-04-19 20:11:25 +0000266 if (!this->filterMask(&dstM, srcM, matrix, NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267 return false;
reed@google.comfeb8cc82011-04-19 20:11:25 +0000268 }
bungeman@google.com02f55842011-10-04 21:25:00 +0000269 SkAutoMaskFreeImage autoDst(dstM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000270
reed@google.com045e62d2011-10-24 12:19:46 +0000271 // 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);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276
reed@google.comfeb8cc82011-04-19 20:11:25 +0000277 if (!clipper.done() && (bounder == NULL || bounder->doIRect(dstM.fBounds))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278 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
reed@google.comd729b3e2012-11-09 14:30:48 +0000288SkMaskFilter::FilterReturn
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000289SkMaskFilter::filterRRectToNine(const SkRRect&, const SkMatrix&,
290 const SkIRect& clipBounds, NinePatch*) const {
291 return kUnimplemented_FilterReturn;
292}
293
294SkMaskFilter::FilterReturn
reed@google.comdab9b4f2012-11-19 16:45:14 +0000295SkMaskFilter::filterRectsToNine(const SkRect[], int count, const SkMatrix&,
reed@google.com30711b72012-12-18 19:18:39 +0000296 const SkIRect& clipBounds, NinePatch*) const {
reed@google.comd729b3e2012-11-09 14:30:48 +0000297 return kUnimplemented_FilterReturn;
298}
299
robertphillips@google.com0b828522013-07-03 15:49:05 +0000300#if SK_SUPPORT_GPU
robertphillips@google.com49149312013-07-03 15:34:35 +0000301bool SkMaskFilter::asNewEffect(GrEffectRef** effect, GrTexture*) const {
302 return false;
303}
304
skia.committer@gmail.com1842adf2013-07-04 07:01:07 +0000305bool SkMaskFilter::canFilterMaskGPU(const SkRect& devBounds,
robertphillips@google.com49149312013-07-03 15:34:35 +0000306 const SkIRect& clipBounds,
307 const SkMatrix& ctm,
308 SkRect* maskRect) const {
309 return false;
310}
311
312bool SkMaskFilter::filterMaskGPU(GrContext* context,
skia.committer@gmail.com1842adf2013-07-04 07:01:07 +0000313 const SkBitmap& srcBM,
314 const SkRect& maskRect,
robertphillips@google.com49149312013-07-03 15:34:35 +0000315 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
skia.committer@gmail.com1842adf2013-07-04 07:01:07 +0000327 // by the filter code. More thought needs to be devoted to the
robertphillips@google.com49149312013-07-03 15:34:35 +0000328 // "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());
reed@google.com398337b2013-12-11 21:22:39 +0000354 resultBM->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (dst)))->unref();
355 dst->unref();
robertphillips@google.com49149312013-07-03 15:34:35 +0000356 return true;
357}
358
skia.committer@gmail.com1842adf2013-07-04 07:01:07 +0000359bool SkMaskFilter::filterMaskGPU(GrTexture* src,
360 const SkRect& maskRect,
robertphillips@google.com49149312013-07-03 15:34:35 +0000361 GrTexture** result,
362 bool canOverwriteSrc) const {
363 return false;
reed@google.com2b75f422011-07-07 13:43:38 +0000364}
robertphillips@google.com0b828522013-07-03 15:49:05 +0000365#endif
reed@google.com2b75f422011-07-07 13:43:38 +0000366
reed@google.com30711b72012-12-18 19:18:39 +0000367void SkMaskFilter::computeFastBounds(const SkRect& src, SkRect* dst) const {
reed@google.com9efd9a02012-01-30 15:41:43 +0000368 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}