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