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" |
| 9 | #include "SkColorSpaceXformer.h" |
Mike Klein | 6747f52 | 2018-05-22 10:32:20 -0400 | [diff] [blame] | 10 | #include "SkColorSpaceXformPriv.h" |
Matt Sarett | cdc651d | 2017-03-30 12:41:48 -0400 | [diff] [blame] | 11 | #include "SkDrawLooper.h" |
| 12 | #include "SkGradientShader.h" |
Florin Malita | 39e0855 | 2017-07-06 14:16:18 -0400 | [diff] [blame] | 13 | #include "SkImage.h" |
Matt Sarett | cdc651d | 2017-03-30 12:41:48 -0400 | [diff] [blame] | 14 | #include "SkImage_Base.h" |
Matt Sarett | 31abf1f | 2017-04-07 16:54:04 -0400 | [diff] [blame] | 15 | #include "SkImageFilter.h" |
Matt Sarett | e684483 | 2017-04-03 10:35:42 -0400 | [diff] [blame] | 16 | #include "SkImagePriv.h" |
Florin Malita | 4aed138 | 2017-05-25 10:38:07 -0400 | [diff] [blame] | 17 | #include "SkShaderBase.h" |
Matt Sarett | cdc651d | 2017-03-30 12:41:48 -0400 | [diff] [blame] | 18 | |
Florin Malita | 24a2ecf | 2017-07-06 15:12:12 -0400 | [diff] [blame] | 19 | SkColorSpaceXformer::SkColorSpaceXformer(sk_sp<SkColorSpace> dst, |
| 20 | std::unique_ptr<SkColorSpaceXform> fromSRGB) |
| 21 | : fDst(std::move(dst)) |
Florin Malita | 5449aad | 2017-07-10 11:14:40 -0400 | [diff] [blame] | 22 | , fFromSRGB(std::move(fromSRGB)) |
| 23 | , fReentryCount(0) {} |
Florin Malita | 24a2ecf | 2017-07-06 15:12:12 -0400 | [diff] [blame] | 24 | |
Florin Malita | 39e0855 | 2017-07-06 14:16:18 -0400 | [diff] [blame] | 25 | SkColorSpaceXformer::~SkColorSpaceXformer() {} |
| 26 | |
Matt Sarett | cdc651d | 2017-03-30 12:41:48 -0400 | [diff] [blame] | 27 | std::unique_ptr<SkColorSpaceXformer> SkColorSpaceXformer::Make(sk_sp<SkColorSpace> dst) { |
Mike Klein | 6747f52 | 2018-05-22 10:32:20 -0400 | [diff] [blame] | 28 | std::unique_ptr<SkColorSpaceXform> fromSRGB = SkMakeColorSpaceXform( |
Brian Osman | b62f50c | 2018-07-12 14:44:27 -0400 | [diff] [blame] | 29 | SkColorSpace::MakeSRGB().get(), dst.get()); |
Matt Sarett | cdc651d | 2017-03-30 12:41:48 -0400 | [diff] [blame] | 30 | |
Florin Malita | 24a2ecf | 2017-07-06 15:12:12 -0400 | [diff] [blame] | 31 | return fromSRGB |
| 32 | ? std::unique_ptr<SkColorSpaceXformer>(new SkColorSpaceXformer(std::move(dst), |
| 33 | std::move(fromSRGB))) |
| 34 | : nullptr; |
Matt Sarett | cdc651d | 2017-03-30 12:41:48 -0400 | [diff] [blame] | 35 | } |
| 36 | |
Florin Malita | 5449aad | 2017-07-10 11:14:40 -0400 | [diff] [blame] | 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 | |
Matt Sarett | cdc651d | 2017-03-30 12:41:48 -0400 | [diff] [blame] | 97 | sk_sp<SkImage> SkColorSpaceXformer::apply(const SkImage* src) { |
Florin Malita | 5449aad | 2017-07-10 11:14:40 -0400 | [diff] [blame] | 98 | const AutoCachePurge autoPurge(this); |
| 99 | return this->cachedApply<SkImage>(src, &fImageCache, |
| 100 | [](const SkImage* img, SkColorSpaceXformer* xformer) { |
Brian Osman | b62f50c | 2018-07-12 14:44:27 -0400 | [diff] [blame] | 101 | return img->makeColorSpace(xformer->fDst); |
Florin Malita | 5449aad | 2017-07-10 11:14:40 -0400 | [diff] [blame] | 102 | }); |
Matt Sarett | cdc651d | 2017-03-30 12:41:48 -0400 | [diff] [blame] | 103 | } |
| 104 | |
Matt Sarett | e684483 | 2017-04-03 10:35:42 -0400 | [diff] [blame] | 105 | sk_sp<SkImage> SkColorSpaceXformer::apply(const SkBitmap& src) { |
Florin Malita | 5449aad | 2017-07-10 11:14:40 -0400 | [diff] [blame] | 106 | const AutoCachePurge autoPurge(this); |
Matt Sarett | e684483 | 2017-04-03 10:35:42 -0400 | [diff] [blame] | 107 | sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(src, kNever_SkCopyPixelsMode); |
| 108 | if (!image) { |
| 109 | return nullptr; |
| 110 | } |
| 111 | |
Brian Osman | b62f50c | 2018-07-12 14:44:27 -0400 | [diff] [blame] | 112 | sk_sp<SkImage> xformed = image->makeColorSpace(fDst); |
Matt Sarett | e684483 | 2017-04-03 10:35:42 -0400 | [diff] [blame] | 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 | |
Matt Sarett | 6d72ed9 | 2017-04-10 16:35:33 -0400 | [diff] [blame] | 118 | sk_sp<SkColorFilter> SkColorSpaceXformer::apply(const SkColorFilter* colorFilter) { |
Florin Malita | 5449aad | 2017-07-10 11:14:40 -0400 | [diff] [blame] | 119 | const AutoCachePurge autoPurge(this); |
| 120 | return this->cachedApply<SkColorFilter>(colorFilter, &fColorFilterCache, |
| 121 | [](const SkColorFilter* f, SkColorSpaceXformer* xformer) { |
| 122 | return f->makeColorSpace(xformer); |
| 123 | }); |
Matt Sarett | 6d72ed9 | 2017-04-10 16:35:33 -0400 | [diff] [blame] | 124 | } |
| 125 | |
Mike Klein | e908b94 | 2017-04-25 13:09:40 -0400 | [diff] [blame] | 126 | sk_sp<SkImageFilter> SkColorSpaceXformer::apply(const SkImageFilter* imageFilter) { |
Florin Malita | 5449aad | 2017-07-10 11:14:40 -0400 | [diff] [blame] | 127 | const AutoCachePurge autoPurge(this); |
| 128 | return this->cachedApply<SkImageFilter>(imageFilter, &fImageFilterCache, |
| 129 | [](const SkImageFilter* f, SkColorSpaceXformer* xformer) { |
| 130 | return f->makeColorSpace(xformer); |
| 131 | }); |
Mike Klein | e908b94 | 2017-04-25 13:09:40 -0400 | [diff] [blame] | 132 | } |
| 133 | |
Mike Klein | 2814d91 | 2017-05-10 12:35:51 -0400 | [diff] [blame] | 134 | sk_sp<SkShader> SkColorSpaceXformer::apply(const SkShader* shader) { |
Florin Malita | 5449aad | 2017-07-10 11:14:40 -0400 | [diff] [blame] | 135 | const AutoCachePurge autoPurge(this); |
Florin Malita | 4aed138 | 2017-05-25 10:38:07 -0400 | [diff] [blame] | 136 | return as_SB(shader)->makeColorSpace(this); |
Mike Klein | 2814d91 | 2017-05-10 12:35:51 -0400 | [diff] [blame] | 137 | } |
| 138 | |
Matt Sarett | cdc651d | 2017-03-30 12:41:48 -0400 | [diff] [blame] | 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 | |
Matt Sarett | c15bb7b | 2017-04-25 13:53:11 -0400 | [diff] [blame] | 151 | SkPaint SkColorSpaceXformer::apply(const SkPaint& src) { |
Florin Malita | 5449aad | 2017-07-10 11:14:40 -0400 | [diff] [blame] | 152 | const AutoCachePurge autoPurge(this); |
| 153 | |
Matt Sarett | c15bb7b | 2017-04-25 13:53:11 -0400 | [diff] [blame] | 154 | SkPaint dst = src; |
Matt Sarett | cdc651d | 2017-03-30 12:41:48 -0400 | [diff] [blame] | 155 | |
| 156 | // All SkColorSpaces have the same black point. |
| 157 | if (src.getColor() & 0xffffff) { |
Matt Sarett | c15bb7b | 2017-04-25 13:53:11 -0400 | [diff] [blame] | 158 | dst.setColor(this->apply(src.getColor())); |
Matt Sarett | cdc651d | 2017-03-30 12:41:48 -0400 | [diff] [blame] | 159 | } |
| 160 | |
| 161 | if (auto shader = src.getShader()) { |
Mike Klein | 2814d91 | 2017-05-10 12:35:51 -0400 | [diff] [blame] | 162 | dst.setShader(this->apply(shader)); |
Matt Sarett | cdc651d | 2017-03-30 12:41:48 -0400 | [diff] [blame] | 163 | } |
| 164 | |
Matt Sarett | cdc651d | 2017-03-30 12:41:48 -0400 | [diff] [blame] | 165 | if (auto cf = src.getColorFilter()) { |
Matt Sarett | c15bb7b | 2017-04-25 13:53:11 -0400 | [diff] [blame] | 166 | dst.setColorFilter(this->apply(cf)); |
Matt Sarett | cdc651d | 2017-03-30 12:41:48 -0400 | [diff] [blame] | 167 | } |
| 168 | |
| 169 | if (auto looper = src.getDrawLooper()) { |
Matt Sarett | c15bb7b | 2017-04-25 13:53:11 -0400 | [diff] [blame] | 170 | dst.setDrawLooper(looper->makeColorSpace(this)); |
Matt Sarett | cdc651d | 2017-03-30 12:41:48 -0400 | [diff] [blame] | 171 | } |
| 172 | |
Matt Sarett | 31abf1f | 2017-04-07 16:54:04 -0400 | [diff] [blame] | 173 | if (auto imageFilter = src.getImageFilter()) { |
Matt Sarett | c15bb7b | 2017-04-25 13:53:11 -0400 | [diff] [blame] | 174 | dst.setImageFilter(this->apply(imageFilter)); |
Matt Sarett | 31abf1f | 2017-04-07 16:54:04 -0400 | [diff] [blame] | 175 | } |
| 176 | |
Matt Sarett | c15bb7b | 2017-04-25 13:53:11 -0400 | [diff] [blame] | 177 | return dst; |
Matt Sarett | cdc651d | 2017-03-30 12:41:48 -0400 | [diff] [blame] | 178 | } |
Stan Iliev | 45fd995 | 2017-12-18 14:46:30 -0500 | [diff] [blame] | 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 | } |