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