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 
SkColorSpaceXformer(sk_sp<SkColorSpace> dst)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 
~SkColorSpaceXformer()34 SkColorSpaceXformer::~SkColorSpaceXformer() {}
35 
Make(sk_sp<SkColorSpace> dst)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:
AutoCachePurge(SkColorSpaceXformer * xformer)60     AutoCachePurge(SkColorSpaceXformer* xformer)
61         : fXformer(xformer) {
62         fXformer->fReentryCount++;
63     }
64 
~AutoCachePurge()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>
cachedApply(const T * src,Cache<T> * cache,sk_sp<T> (* applyFunc)(const T *,SkColorSpaceXformer *))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 
purgeCaches()94 void SkColorSpaceXformer::purgeCaches() {
95     fImageCache.reset();
96     fColorFilterCache.reset();
97     fImageFilterCache.reset();
98 }
99 
apply(const SkImage * src)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 
apply(const SkBitmap & src)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 
apply(const SkColorFilter * colorFilter)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 
apply(const SkImageFilter * imageFilter)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 
apply(const SkShader * shader)137 sk_sp<SkShader> SkColorSpaceXformer::apply(const SkShader* shader) {
138     const AutoCachePurge autoPurge(this);
139     return as_SB(shader)->makeColorSpace(this);
140 }
141 
apply(SkColor * xformed,const SkColor * srgb,int n)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 
apply(SkColor srgb)148 SkColor SkColorSpaceXformer::apply(SkColor srgb) {
149     SkColor xformed;
150     this->apply(&xformed, &srgb, 1);
151     return xformed;
152 }
153 
apply(const SkPaint & src)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 
apply(const SkCanvas::Lattice & lattice,SkColor * colorBuffer,int count)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