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#include "SkBlurMaskFilter.h" 10#include "SkBlurMask.h" 11#include "SkFlattenableBuffers.h" 12#include "SkMaskFilter.h" 13 14class SkBlurMaskFilterImpl : public SkMaskFilter { 15public: 16 SkBlurMaskFilterImpl(SkScalar radius, SkBlurMaskFilter::BlurStyle, 17 uint32_t flags); 18 19 // overrides from SkMaskFilter 20 virtual SkMask::Format getFormat() const SK_OVERRIDE; 21 virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&, 22 SkIPoint* margin) const SK_OVERRIDE; 23 virtual BlurType asABlur(BlurInfo*) const SK_OVERRIDE; 24 virtual void computeFastBounds(const SkRect&, SkRect*) const SK_OVERRIDE; 25 26 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurMaskFilterImpl) 27 28protected: 29 virtual FilterReturn filterRectsToNine(const SkRect[], int count, const SkMatrix&, 30 const SkIRect& clipBounds, 31 NinePatch*) const SK_OVERRIDE; 32 33private: 34 SkScalar fRadius; 35 SkBlurMaskFilter::BlurStyle fBlurStyle; 36 uint32_t fBlurFlags; 37 38 SkBlurMaskFilterImpl(SkFlattenableReadBuffer&); 39 virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE; 40 41 typedef SkMaskFilter INHERITED; 42}; 43 44SkMaskFilter* SkBlurMaskFilter::Create(SkScalar radius, 45 SkBlurMaskFilter::BlurStyle style, 46 uint32_t flags) { 47 // use !(radius > 0) instead of radius <= 0 to reject NaN values 48 if (!(radius > 0) || (unsigned)style >= SkBlurMaskFilter::kBlurStyleCount 49 || flags > SkBlurMaskFilter::kAll_BlurFlag) { 50 return NULL; 51 } 52 53 return SkNEW_ARGS(SkBlurMaskFilterImpl, (radius, style, flags)); 54} 55 56/////////////////////////////////////////////////////////////////////////////// 57 58SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkScalar radius, 59 SkBlurMaskFilter::BlurStyle style, 60 uint32_t flags) 61 : fRadius(radius), fBlurStyle(style), fBlurFlags(flags) { 62#if 0 63 fGamma = NULL; 64 if (gammaScale) { 65 fGamma = new U8[256]; 66 if (gammaScale > 0) 67 SkBlurMask::BuildSqrGamma(fGamma, gammaScale); 68 else 69 SkBlurMask::BuildSqrtGamma(fGamma, -gammaScale); 70 } 71#endif 72 SkASSERT(radius >= 0); 73 SkASSERT((unsigned)style < SkBlurMaskFilter::kBlurStyleCount); 74 SkASSERT(flags <= SkBlurMaskFilter::kAll_BlurFlag); 75} 76 77SkMask::Format SkBlurMaskFilterImpl::getFormat() const { 78 return SkMask::kA8_Format; 79} 80 81bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src, 82 const SkMatrix& matrix, 83 SkIPoint* margin) const{ 84 SkScalar radius; 85 if (fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag) { 86 radius = fRadius; 87 } else { 88 radius = matrix.mapRadius(fRadius); 89 } 90 91 // To avoid unseemly allocation requests (esp. for finite platforms like 92 // handset) we limit the radius so something manageable. (as opposed to 93 // a request like 10,000) 94 static const SkScalar MAX_RADIUS = SkIntToScalar(128); 95 radius = SkMinScalar(radius, MAX_RADIUS); 96 SkBlurMask::Quality blurQuality = 97 (fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ? 98 SkBlurMask::kHigh_Quality : SkBlurMask::kLow_Quality; 99 100#ifndef SK_DISABLE_SEPARABLE_MASK_BLUR 101 return SkBlurMask::BlurSeparable(dst, src, radius, (SkBlurMask::Style)fBlurStyle, 102 blurQuality, margin); 103#else 104 return SkBlurMask::Blur(dst, src, radius, (SkBlurMask::Style)fBlurStyle, 105 blurQuality, margin); 106#endif 107} 108 109#include "SkCanvas.h" 110 111static bool drawRectsIntoMask(const SkRect rects[], int count, SkMask* mask) { 112 rects[0].roundOut(&mask->fBounds); 113 mask->fRowBytes = SkAlign4(mask->fBounds.width()); 114 mask->fFormat = SkMask::kA8_Format; 115 size_t size = mask->computeImageSize(); 116 mask->fImage = SkMask::AllocImage(size); 117 if (NULL == mask->fImage) { 118 return false; 119 } 120 sk_bzero(mask->fImage, size); 121 122 SkBitmap bitmap; 123 bitmap.setConfig(SkBitmap::kA8_Config, 124 mask->fBounds.width(), mask->fBounds.height(), 125 mask->fRowBytes); 126 bitmap.setPixels(mask->fImage); 127 128 SkCanvas canvas(bitmap); 129 canvas.translate(-SkIntToScalar(mask->fBounds.left()), 130 -SkIntToScalar(mask->fBounds.top())); 131 132 SkPaint paint; 133 paint.setAntiAlias(true); 134 135 if (1 == count) { 136 canvas.drawRect(rects[0], paint); 137 } else { 138 // todo: do I need a fast way to do this? 139 SkPath path; 140 path.addRect(rects[0]); 141 path.addRect(rects[1]); 142 path.setFillType(SkPath::kEvenOdd_FillType); 143 canvas.drawPath(path, paint); 144 } 145 return true; 146} 147 148static bool rect_exceeds(const SkRect& r, SkScalar v) { 149 return r.fLeft < -v || r.fTop < -v || r.fRight > v || r.fBottom > v || 150 r.width() > v || r.height() > v; 151} 152 153SkMaskFilter::FilterReturn 154SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count, 155 const SkMatrix& matrix, 156 const SkIRect& clipBounds, 157 NinePatch* patch) const { 158 if (count < 1 || count > 2) { 159 return kUnimplemented_FilterReturn; 160 } 161 162 // TODO: report correct metrics for innerstyle, where we do not grow the 163 // total bounds, but we do need an inset the size of our blur-radius 164 if (SkBlurMaskFilter::kInner_BlurStyle == fBlurStyle) { 165 return kUnimplemented_FilterReturn; 166 } 167 168 // TODO: take clipBounds into account to limit our coordinates up front 169 // for now, just skip too-large src rects (to take the old code path). 170 if (rect_exceeds(rects[0], SkIntToScalar(32767))) { 171 return kUnimplemented_FilterReturn; 172 } 173 174 SkIPoint margin; 175 SkMask srcM, dstM; 176 rects[0].roundOut(&srcM.fBounds); 177 srcM.fImage = NULL; 178 srcM.fFormat = SkMask::kA8_Format; 179 srcM.fRowBytes = 0; 180 if (!this->filterMask(&dstM, srcM, matrix, &margin)) { 181 return kFalse_FilterReturn; 182 } 183 184 /* 185 * smallR is the smallest version of 'rect' that will still guarantee that 186 * we get the same blur results on all edges, plus 1 center row/col that is 187 * representative of the extendible/stretchable edges of the ninepatch. 188 * Since our actual edge may be fractional we inset 1 more to be sure we 189 * don't miss any interior blur. 190 * x is an added pixel of blur, and { and } are the (fractional) edge 191 * pixels from the original rect. 192 * 193 * x x { x x .... x x } x x 194 * 195 * Thus, in this case, we inset by a total of 5 (on each side) beginning 196 * with our outer-rect (dstM.fBounds) 197 */ 198 SkRect smallR[2]; 199 SkIPoint center; 200 201 // +2 is from +1 for each edge (to account for possible fractional edges 202 int smallW = dstM.fBounds.width() - srcM.fBounds.width() + 2; 203 int smallH = dstM.fBounds.height() - srcM.fBounds.height() + 2; 204 SkIRect innerIR; 205 206 if (1 == count) { 207 innerIR = srcM.fBounds; 208 center.set(smallW, smallH); 209 } else { 210 SkASSERT(2 == count); 211 rects[1].roundIn(&innerIR); 212 center.set(smallW + (innerIR.left() - srcM.fBounds.left()), 213 smallH + (innerIR.top() - srcM.fBounds.top())); 214 } 215 216 // +1 so we get a clean, stretchable, center row/col 217 smallW += 1; 218 smallH += 1; 219 220 // we want the inset amounts to be integral, so we don't change any 221 // fractional phase on the fRight or fBottom of our smallR. 222 const SkScalar dx = SkIntToScalar(innerIR.width() - smallW); 223 const SkScalar dy = SkIntToScalar(innerIR.height() - smallH); 224 if (dx < 0 || dy < 0) { 225 // we're too small, relative to our blur, to break into nine-patch, 226 // so we ask to have our normal filterMask() be called. 227 return kUnimplemented_FilterReturn; 228 } 229 230 smallR[0].set(rects[0].left(), rects[0].top(), rects[0].right() - dx, rects[0].bottom() - dy); 231 SkASSERT(!smallR[0].isEmpty()); 232 if (2 == count) { 233 smallR[1].set(rects[1].left(), rects[1].top(), 234 rects[1].right() - dx, rects[1].bottom() - dy); 235 SkASSERT(!smallR[1].isEmpty()); 236 } 237 238 if (!drawRectsIntoMask(smallR, count, &srcM)) { 239 return kFalse_FilterReturn; 240 } 241 242 SkAutoMaskFreeImage amf(srcM.fImage); 243 244 if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) { 245 return kFalse_FilterReturn; 246 } 247 patch->fMask.fBounds.offsetTo(0, 0); 248 patch->fOuterRect = dstM.fBounds; 249 patch->fCenter = center; 250 return kTrue_FilterReturn; 251} 252 253void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src, 254 SkRect* dst) const { 255 dst->set(src.fLeft - fRadius, src.fTop - fRadius, 256 src.fRight + fRadius, src.fBottom + fRadius); 257} 258 259SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkFlattenableReadBuffer& buffer) 260 : SkMaskFilter(buffer) { 261 fRadius = buffer.readScalar(); 262 fBlurStyle = (SkBlurMaskFilter::BlurStyle)buffer.readInt(); 263 fBlurFlags = buffer.readUInt() & SkBlurMaskFilter::kAll_BlurFlag; 264 SkASSERT(fRadius >= 0); 265 SkASSERT((unsigned)fBlurStyle < SkBlurMaskFilter::kBlurStyleCount); 266} 267 268void SkBlurMaskFilterImpl::flatten(SkFlattenableWriteBuffer& buffer) const { 269 this->INHERITED::flatten(buffer); 270 buffer.writeScalar(fRadius); 271 buffer.writeInt(fBlurStyle); 272 buffer.writeUInt(fBlurFlags); 273} 274 275static const SkMaskFilter::BlurType gBlurStyle2BlurType[] = { 276 SkMaskFilter::kNormal_BlurType, 277 SkMaskFilter::kSolid_BlurType, 278 SkMaskFilter::kOuter_BlurType, 279 SkMaskFilter::kInner_BlurType, 280}; 281 282SkMaskFilter::BlurType SkBlurMaskFilterImpl::asABlur(BlurInfo* info) const { 283 if (info) { 284 info->fRadius = fRadius; 285 info->fIgnoreTransform = SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag); 286 info->fHighQuality = SkToBool(fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag); 287 } 288 return gBlurStyle2BlurType[fBlurStyle]; 289} 290 291SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkBlurMaskFilter) 292 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurMaskFilterImpl) 293SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END 294