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