SkImageFilter.cpp revision 874a62acef5ec2ecccdbb99ec4d86402a3341e6a
1/* 2 * Copyright 2012 The Android Open Source Project 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "SkImageFilter.h" 9 10#include "SkBitmap.h" 11#include "SkDevice.h" 12#include "SkReadBuffer.h" 13#include "SkWriteBuffer.h" 14#include "SkRect.h" 15#include "SkTDynamicHash.h" 16#include "SkValidationUtils.h" 17#if SK_SUPPORT_GPU 18#include "GrContext.h" 19#include "SkGrPixelRef.h" 20#include "SkGr.h" 21#endif 22 23SkImageFilter::Cache* gExternalCache; 24 25SkImageFilter::SkImageFilter(int inputCount, SkImageFilter** inputs, const CropRect* cropRect) 26 : fInputCount(inputCount), 27 fInputs(new SkImageFilter*[inputCount]), 28 fCropRect(cropRect ? *cropRect : CropRect(SkRect(), 0x0)) { 29 for (int i = 0; i < inputCount; ++i) { 30 fInputs[i] = inputs[i]; 31 SkSafeRef(fInputs[i]); 32 } 33} 34 35SkImageFilter::~SkImageFilter() { 36 for (int i = 0; i < fInputCount; i++) { 37 SkSafeUnref(fInputs[i]); 38 } 39 delete[] fInputs; 40} 41 42SkImageFilter::SkImageFilter(int inputCount, SkReadBuffer& buffer) { 43 fInputCount = buffer.readInt(); 44 if (buffer.validate((fInputCount >= 0) && ((inputCount < 0) || (fInputCount == inputCount)))) { 45 fInputs = new SkImageFilter*[fInputCount]; 46 for (int i = 0; i < fInputCount; i++) { 47 if (buffer.readBool()) { 48 fInputs[i] = buffer.readImageFilter(); 49 } else { 50 fInputs[i] = NULL; 51 } 52 if (!buffer.isValid()) { 53 fInputCount = i; // Do not use fInputs past that point in the destructor 54 break; 55 } 56 } 57 SkRect rect; 58 buffer.readRect(&rect); 59 if (buffer.isValid() && buffer.validate(SkIsValidRect(rect))) { 60 uint32_t flags = buffer.readUInt(); 61 fCropRect = CropRect(rect, flags); 62 } 63 } else { 64 fInputCount = 0; 65 fInputs = NULL; 66 } 67} 68 69void SkImageFilter::flatten(SkWriteBuffer& buffer) const { 70 buffer.writeInt(fInputCount); 71 for (int i = 0; i < fInputCount; i++) { 72 SkImageFilter* input = getInput(i); 73 buffer.writeBool(input != NULL); 74 if (input != NULL) { 75 buffer.writeFlattenable(input); 76 } 77 } 78 buffer.writeRect(fCropRect.rect()); 79 buffer.writeUInt(fCropRect.flags()); 80} 81 82bool SkImageFilter::filterImage(Proxy* proxy, const SkBitmap& src, 83 const Context& context, 84 SkBitmap* result, SkIPoint* offset) const { 85 Cache* cache = context.cache(); 86 SkASSERT(result); 87 SkASSERT(offset); 88 SkASSERT(cache); 89 if (cache->get(this, result, offset)) { 90 return true; 91 } 92 /* 93 * Give the proxy first shot at the filter. If it returns false, ask 94 * the filter to do it. 95 */ 96 if ((proxy && proxy->filterImage(this, src, context, result, offset)) || 97 this->onFilterImage(proxy, src, context, result, offset)) { 98 cache->set(this, *result, *offset); 99 return true; 100 } 101 return false; 102} 103 104bool SkImageFilter::filterBounds(const SkIRect& src, const SkMatrix& ctm, 105 SkIRect* dst) const { 106 SkASSERT(&src); 107 SkASSERT(dst); 108 if (SkImageFilter::GetExternalCache()) { 109 /* 110 * When the external cache is active, do not intersect the saveLayer 111 * bounds with the clip bounds. This is so that the cached result 112 * is always the full size of the primitive's bounds, 113 * regardless of the clip active on first draw. 114 */ 115 *dst = SkIRect::MakeLargest(); 116 return true; 117 } 118 return this->onFilterBounds(src, ctm, dst); 119} 120 121void SkImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const { 122 if (0 == fInputCount) { 123 *dst = src; 124 return; 125 } 126 if (this->getInput(0)) { 127 this->getInput(0)->computeFastBounds(src, dst); 128 } else { 129 *dst = src; 130 } 131 for (int i = 1; i < fInputCount; i++) { 132 SkImageFilter* input = this->getInput(i); 133 if (input) { 134 SkRect bounds; 135 input->computeFastBounds(src, &bounds); 136 dst->join(bounds); 137 } else { 138 dst->join(src); 139 } 140 } 141} 142 143bool SkImageFilter::onFilterImage(Proxy*, const SkBitmap&, const Context&, 144 SkBitmap*, SkIPoint*) const { 145 return false; 146} 147 148bool SkImageFilter::canFilterImageGPU() const { 149 return this->asNewEffect(NULL, NULL, SkMatrix::I(), SkIRect()); 150} 151 152bool SkImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx, 153 SkBitmap* result, SkIPoint* offset) const { 154#if SK_SUPPORT_GPU 155 SkBitmap input = src; 156 SkASSERT(fInputCount == 1); 157 SkIPoint srcOffset = SkIPoint::Make(0, 0); 158 if (this->getInput(0) && 159 !this->getInput(0)->getInputResultGPU(proxy, src, ctx, &input, &srcOffset)) { 160 return false; 161 } 162 GrTexture* srcTexture = input.getTexture(); 163 SkIRect bounds; 164 if (!this->applyCropRect(ctx, proxy, input, &srcOffset, &bounds, &input)) { 165 return false; 166 } 167 SkRect srcRect = SkRect::Make(bounds); 168 SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); 169 GrContext* context = srcTexture->getContext(); 170 171 GrTextureDesc desc; 172 desc.fFlags = kRenderTarget_GrTextureFlagBit, 173 desc.fWidth = bounds.width(); 174 desc.fHeight = bounds.height(); 175 desc.fConfig = kRGBA_8888_GrPixelConfig; 176 177 GrAutoScratchTexture dst(context, desc); 178 GrContext::AutoMatrix am; 179 am.setIdentity(context); 180 GrContext::AutoRenderTarget art(context, dst.texture()->asRenderTarget()); 181 GrContext::AutoClip acs(context, dstRect); 182 GrEffect* effect; 183 offset->fX = bounds.left(); 184 offset->fY = bounds.top(); 185 bounds.offset(-srcOffset); 186 SkMatrix matrix(ctx.ctm()); 187 matrix.postTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top())); 188 this->asNewEffect(&effect, srcTexture, matrix, bounds); 189 SkASSERT(effect); 190 GrPaint paint; 191 paint.addColorEffect(effect)->unref(); 192 context->drawRectToRect(paint, dstRect, srcRect); 193 194 SkAutoTUnref<GrTexture> resultTex(dst.detach()); 195 WrapTexture(resultTex, bounds.width(), bounds.height(), result); 196 return true; 197#else 198 return false; 199#endif 200} 201 202bool SkImageFilter::applyCropRect(const Context& ctx, const SkBitmap& src, 203 const SkIPoint& srcOffset, SkIRect* bounds) const { 204 SkIRect srcBounds; 205 src.getBounds(&srcBounds); 206 srcBounds.offset(srcOffset); 207 SkRect cropRect; 208 ctx.ctm().mapRect(&cropRect, fCropRect.rect()); 209 SkIRect cropRectI; 210 cropRect.roundOut(&cropRectI); 211 uint32_t flags = fCropRect.flags(); 212 if (flags & CropRect::kHasLeft_CropEdge) srcBounds.fLeft = cropRectI.fLeft; 213 if (flags & CropRect::kHasTop_CropEdge) srcBounds.fTop = cropRectI.fTop; 214 if (flags & CropRect::kHasRight_CropEdge) srcBounds.fRight = cropRectI.fRight; 215 if (flags & CropRect::kHasBottom_CropEdge) srcBounds.fBottom = cropRectI.fBottom; 216 if (!srcBounds.intersect(ctx.clipBounds())) { 217 return false; 218 } 219 *bounds = srcBounds; 220 return true; 221} 222 223bool SkImageFilter::applyCropRect(const Context& ctx, Proxy* proxy, const SkBitmap& src, 224 SkIPoint* srcOffset, SkIRect* bounds, SkBitmap* dst) const { 225 SkIRect srcBounds; 226 src.getBounds(&srcBounds); 227 srcBounds.offset(*srcOffset); 228 SkRect cropRect; 229 ctx.ctm().mapRect(&cropRect, fCropRect.rect()); 230 SkIRect cropRectI; 231 cropRect.roundOut(&cropRectI); 232 uint32_t flags = fCropRect.flags(); 233 *bounds = srcBounds; 234 if (flags & CropRect::kHasLeft_CropEdge) bounds->fLeft = cropRectI.fLeft; 235 if (flags & CropRect::kHasTop_CropEdge) bounds->fTop = cropRectI.fTop; 236 if (flags & CropRect::kHasRight_CropEdge) bounds->fRight = cropRectI.fRight; 237 if (flags & CropRect::kHasBottom_CropEdge) bounds->fBottom = cropRectI.fBottom; 238 if (!bounds->intersect(ctx.clipBounds())) { 239 return false; 240 } 241 if (srcBounds.contains(*bounds)) { 242 *dst = src; 243 return true; 244 } else { 245 SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds->width(), bounds->height())); 246 if (!device) { 247 return false; 248 } 249 SkCanvas canvas(device); 250 canvas.clear(0x00000000); 251 canvas.drawBitmap(src, srcOffset->x() - bounds->x(), srcOffset->y() - bounds->y()); 252 *srcOffset = SkIPoint::Make(bounds->x(), bounds->y()); 253 *dst = device->accessBitmap(false); 254 return true; 255 } 256} 257 258bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, 259 SkIRect* dst) const { 260 if (fInputCount < 1) { 261 return false; 262 } 263 264 SkIRect bounds; 265 for (int i = 0; i < fInputCount; ++i) { 266 SkImageFilter* filter = this->getInput(i); 267 SkIRect rect = src; 268 if (filter && !filter->filterBounds(src, ctm, &rect)) { 269 return false; 270 } 271 if (0 == i) { 272 bounds = rect; 273 } else { 274 bounds.join(rect); 275 } 276 } 277 278 // don't modify dst until now, so we don't accidentally change it in the 279 // loop, but then return false on the next filter. 280 *dst = bounds; 281 return true; 282} 283 284bool SkImageFilter::asNewEffect(GrEffect**, GrTexture*, const SkMatrix&, const SkIRect&) const { 285 return false; 286} 287 288bool SkImageFilter::asColorFilter(SkColorFilter**) const { 289 return false; 290} 291 292void SkImageFilter::SetExternalCache(Cache* cache) { 293 SkRefCnt_SafeAssign(gExternalCache, cache); 294} 295 296SkImageFilter::Cache* SkImageFilter::GetExternalCache() { 297 return gExternalCache; 298} 299 300#if SK_SUPPORT_GPU 301 302void SkImageFilter::WrapTexture(GrTexture* texture, int width, int height, SkBitmap* result) { 303 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height); 304 result->setInfo(info); 305 result->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (info, texture)))->unref(); 306} 307 308bool SkImageFilter::getInputResultGPU(SkImageFilter::Proxy* proxy, 309 const SkBitmap& src, const Context& ctx, 310 SkBitmap* result, SkIPoint* offset) const { 311 // Ensure that GrContext calls under filterImage and filterImageGPU below will see an identity 312 // matrix with no clip and that the matrix, clip, and render target set before this function was 313 // called are restored before we return to the caller. 314 GrContext* context = src.getTexture()->getContext(); 315 GrContext::AutoWideOpenIdentityDraw awoid(context, NULL); 316 if (this->canFilterImageGPU()) { 317 return this->filterImageGPU(proxy, src, ctx, result, offset); 318 } else { 319 if (this->filterImage(proxy, src, ctx, result, offset)) { 320 if (!result->getTexture()) { 321 const SkImageInfo info = result->info(); 322 if (kUnknown_SkColorType == info.colorType()) { 323 return false; 324 } 325 GrTexture* resultTex = GrLockAndRefCachedBitmapTexture(context, *result, NULL); 326 result->setPixelRef(new SkGrPixelRef(info, resultTex))->unref(); 327 GrUnlockAndUnrefCachedBitmapTexture(resultTex); 328 } 329 return true; 330 } else { 331 return false; 332 } 333 } 334} 335#endif 336 337static uint32_t compute_hash(const uint32_t* data, int count) { 338 uint32_t hash = 0; 339 340 for (int i = 0; i < count; ++i) { 341 uint32_t k = data[i]; 342 k *= 0xcc9e2d51; 343 k = (k << 15) | (k >> 17); 344 k *= 0x1b873593; 345 346 hash ^= k; 347 hash = (hash << 13) | (hash >> 19); 348 hash *= 5; 349 hash += 0xe6546b64; 350 } 351 352 // hash ^= size; 353 hash ^= hash >> 16; 354 hash *= 0x85ebca6b; 355 hash ^= hash >> 13; 356 hash *= 0xc2b2ae35; 357 hash ^= hash >> 16; 358 359 return hash; 360} 361 362class CacheImpl : public SkImageFilter::Cache { 363public: 364 explicit CacheImpl(int minChildren) : fMinChildren(minChildren) {} 365 virtual ~CacheImpl(); 366 bool get(const SkImageFilter* key, SkBitmap* result, SkIPoint* offset) SK_OVERRIDE; 367 void set(const SkImageFilter* key, const SkBitmap& result, const SkIPoint& offset) SK_OVERRIDE; 368 void remove(const SkImageFilter* key) SK_OVERRIDE; 369private: 370 typedef const SkImageFilter* Key; 371 struct Value { 372 Value(Key key, const SkBitmap& bitmap, const SkIPoint& offset) 373 : fKey(key), fBitmap(bitmap), fOffset(offset) {} 374 Key fKey; 375 SkBitmap fBitmap; 376 SkIPoint fOffset; 377 static const Key& GetKey(const Value& v) { 378 return v.fKey; 379 } 380 static uint32_t Hash(Key key) { 381 return compute_hash(reinterpret_cast<const uint32_t*>(&key), sizeof(Key) / sizeof(uint32_t)); 382 } 383 }; 384 SkTDynamicHash<Value, Key> fData; 385 int fMinChildren; 386}; 387 388bool CacheImpl::get(const SkImageFilter* key, SkBitmap* result, SkIPoint* offset) { 389 Value* v = fData.find(key); 390 if (v) { 391 *result = v->fBitmap; 392 *offset = v->fOffset; 393 return true; 394 } 395 return false; 396} 397 398void CacheImpl::remove(const SkImageFilter* key) { 399 Value* v = fData.find(key); 400 if (v) { 401 fData.remove(key); 402 delete v; 403 } 404} 405 406void CacheImpl::set(const SkImageFilter* key, const SkBitmap& result, const SkIPoint& offset) { 407 if (key->getRefCnt() >= fMinChildren) { 408 fData.add(new Value(key, result, offset)); 409 } 410} 411 412SkImageFilter::Cache* SkImageFilter::Cache::Create(int minChildren) { 413 return new CacheImpl(minChildren); 414} 415 416CacheImpl::~CacheImpl() { 417 SkTDynamicHash<Value, Key>::Iter iter(&fData); 418 419 while (!iter.done()) { 420 Value* v = &*iter; 421 ++iter; 422 delete v; 423 } 424} 425