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