1 /* 2 * Copyright 2017 Google Inc. 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 "SkColorFilter.h" 9 #include "SkColorSpaceXformer.h" 10 #include "SkColorSpaceXform_Base.h" 11 #include "SkDrawLooper.h" 12 #include "SkGradientShader.h" 13 #include "SkImage.h" 14 #include "SkImage_Base.h" 15 #include "SkImageFilter.h" 16 #include "SkImagePriv.h" 17 #include "SkShaderBase.h" 18 19 SkColorSpaceXformer::SkColorSpaceXformer(sk_sp<SkColorSpace> dst, 20 std::unique_ptr<SkColorSpaceXform> fromSRGB) 21 : fDst(std::move(dst)) 22 , fFromSRGB(std::move(fromSRGB)) 23 , fReentryCount(0) {} 24 25 SkColorSpaceXformer::~SkColorSpaceXformer() {} 26 27 std::unique_ptr<SkColorSpaceXformer> SkColorSpaceXformer::Make(sk_sp<SkColorSpace> dst) { 28 std::unique_ptr<SkColorSpaceXform> fromSRGB = SkColorSpaceXform_Base::New( 29 SkColorSpace::MakeSRGB().get(), dst.get(), SkTransferFunctionBehavior::kIgnore); 30 31 return fromSRGB 32 ? std::unique_ptr<SkColorSpaceXformer>(new SkColorSpaceXformer(std::move(dst), 33 std::move(fromSRGB))) 34 : nullptr; 35 } 36 37 // So what's up with these caches? 38 // 39 // We want to cache transformed objects for a couple of reasons: 40 // 41 // 1) to avoid redundant work - the inputs are a DAG, not a tree (e.g. same SkImage drawn multiple 42 // times in a SkPicture), so if we blindly recurse we could end up transforming the same objects 43 // repeatedly. 44 // 45 // 2) to avoid topology changes - we want the output to remain isomorphic with the input -- this is 46 // particularly important for image filters (to maintain their original DAG structure in order 47 // to not defeat their own/internal caching), but also for avoiding unnecessary cloning 48 // (e.g. duplicated SkImages allocated for the example in #1 above). 49 // 50 // The caching scope is naturaly bound by the lifetime of the SkColorSpaceXformer object, but 51 // clients may choose to not discard xformers immediately - in which case, caching indefinitely 52 // is problematic. The solution is to limit the cache scope to the top level apply() call 53 // (i.e. we only keep cached objects alive while transforming). 54 55 class SkColorSpaceXformer::AutoCachePurge { 56 public: 57 AutoCachePurge(SkColorSpaceXformer* xformer) 58 : fXformer(xformer) { 59 fXformer->fReentryCount++; 60 } 61 62 ~AutoCachePurge() { 63 SkASSERT(fXformer->fReentryCount > 0); 64 if (--fXformer->fReentryCount == 0) { 65 fXformer->purgeCaches(); 66 } 67 } 68 69 private: 70 SkColorSpaceXformer* fXformer; 71 }; 72 73 template <typename T> 74 sk_sp<T> SkColorSpaceXformer::cachedApply(const T* src, Cache<T>* cache, 75 sk_sp<T> (*applyFunc)(const T*, SkColorSpaceXformer*)) { 76 if (!src) { 77 return nullptr; 78 } 79 80 auto key = sk_ref_sp(const_cast<T*>(src)); 81 if (auto* xformed = cache->find(key)) { 82 return sk_ref_sp(xformed->get()); 83 } 84 85 auto xformed = applyFunc(src, this); 86 cache->set(std::move(key), xformed); 87 88 return xformed; 89 } 90 91 void SkColorSpaceXformer::purgeCaches() { 92 fImageCache.reset(); 93 fColorFilterCache.reset(); 94 fImageFilterCache.reset(); 95 } 96 97 sk_sp<SkImage> SkColorSpaceXformer::apply(const SkImage* src) { 98 const AutoCachePurge autoPurge(this); 99 return this->cachedApply<SkImage>(src, &fImageCache, 100 [](const SkImage* img, SkColorSpaceXformer* xformer) { 101 return img->makeColorSpace(xformer->fDst, SkTransferFunctionBehavior::kIgnore); 102 }); 103 } 104 105 sk_sp<SkImage> SkColorSpaceXformer::apply(const SkBitmap& src) { 106 const AutoCachePurge autoPurge(this); 107 sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(src, kNever_SkCopyPixelsMode); 108 if (!image) { 109 return nullptr; 110 } 111 112 sk_sp<SkImage> xformed = image->makeColorSpace(fDst, SkTransferFunctionBehavior::kIgnore); 113 // We want to be sure we don't let the kNever_SkCopyPixelsMode image escape this stack frame. 114 SkASSERT(xformed != image); 115 return xformed; 116 } 117 118 sk_sp<SkColorFilter> SkColorSpaceXformer::apply(const SkColorFilter* colorFilter) { 119 const AutoCachePurge autoPurge(this); 120 return this->cachedApply<SkColorFilter>(colorFilter, &fColorFilterCache, 121 [](const SkColorFilter* f, SkColorSpaceXformer* xformer) { 122 return f->makeColorSpace(xformer); 123 }); 124 } 125 126 sk_sp<SkImageFilter> SkColorSpaceXformer::apply(const SkImageFilter* imageFilter) { 127 const AutoCachePurge autoPurge(this); 128 return this->cachedApply<SkImageFilter>(imageFilter, &fImageFilterCache, 129 [](const SkImageFilter* f, SkColorSpaceXformer* xformer) { 130 return f->makeColorSpace(xformer); 131 }); 132 } 133 134 sk_sp<SkShader> SkColorSpaceXformer::apply(const SkShader* shader) { 135 const AutoCachePurge autoPurge(this); 136 return as_SB(shader)->makeColorSpace(this); 137 } 138 139 void SkColorSpaceXformer::apply(SkColor* xformed, const SkColor* srgb, int n) { 140 SkAssertResult(fFromSRGB->apply(SkColorSpaceXform::kBGRA_8888_ColorFormat, xformed, 141 SkColorSpaceXform::kBGRA_8888_ColorFormat, srgb, 142 n, kUnpremul_SkAlphaType)); 143 } 144 145 SkColor SkColorSpaceXformer::apply(SkColor srgb) { 146 SkColor xformed; 147 this->apply(&xformed, &srgb, 1); 148 return xformed; 149 } 150 151 SkPaint SkColorSpaceXformer::apply(const SkPaint& src) { 152 const AutoCachePurge autoPurge(this); 153 154 SkPaint dst = src; 155 156 // All SkColorSpaces have the same black point. 157 if (src.getColor() & 0xffffff) { 158 dst.setColor(this->apply(src.getColor())); 159 } 160 161 if (auto shader = src.getShader()) { 162 dst.setShader(this->apply(shader)); 163 } 164 165 if (auto cf = src.getColorFilter()) { 166 dst.setColorFilter(this->apply(cf)); 167 } 168 169 if (auto looper = src.getDrawLooper()) { 170 dst.setDrawLooper(looper->makeColorSpace(this)); 171 } 172 173 if (auto imageFilter = src.getImageFilter()) { 174 dst.setImageFilter(this->apply(imageFilter)); 175 } 176 177 return dst; 178 } 179 180 SkCanvas::Lattice SkColorSpaceXformer::apply(const SkCanvas::Lattice& lattice, 181 SkColor* colorBuffer, int count) { 182 if (count) { 183 this->apply(colorBuffer, lattice.fColors, count); 184 return {lattice.fXDivs, lattice.fYDivs, lattice.fRectTypes, 185 lattice.fXCount, lattice.fYCount, lattice.fBounds, colorBuffer}; 186 } 187 188 return lattice; 189 } 190