1 | /* |
2 | * Copyright 2018 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 | |
9 | #include "src/gpu/gradients/GrGradientBitmapCache.h" |
10 | |
11 | #include "include/private/SkFloatBits.h" |
12 | #include "include/private/SkHalf.h" |
13 | #include "include/private/SkMalloc.h" |
14 | #include "include/private/SkTemplates.h" |
15 | |
16 | #include <functional> |
17 | |
18 | struct GrGradientBitmapCache::Entry { |
19 | Entry* fPrev; |
20 | Entry* fNext; |
21 | |
22 | void* fBuffer; |
23 | size_t fSize; |
24 | SkBitmap fBitmap; |
25 | |
26 | Entry(const void* buffer, size_t size, const SkBitmap& bm) |
27 | : fPrev(nullptr), |
28 | fNext(nullptr), |
29 | fBitmap(bm) { |
30 | fBuffer = sk_malloc_throw(size); |
31 | fSize = size; |
32 | memcpy(fBuffer, buffer, size); |
33 | } |
34 | |
35 | ~Entry() { sk_free(fBuffer); } |
36 | |
37 | bool equals(const void* buffer, size_t size) const { |
38 | return (fSize == size) && !memcmp(fBuffer, buffer, size); |
39 | } |
40 | }; |
41 | |
42 | GrGradientBitmapCache::GrGradientBitmapCache(int max, int res) |
43 | : fMaxEntries(max) |
44 | , fResolution(res) { |
45 | fEntryCount = 0; |
46 | fHead = fTail = nullptr; |
47 | |
48 | this->validate(); |
49 | } |
50 | |
51 | GrGradientBitmapCache::~GrGradientBitmapCache() { |
52 | this->validate(); |
53 | |
54 | Entry* entry = fHead; |
55 | while (entry) { |
56 | Entry* next = entry->fNext; |
57 | delete entry; |
58 | entry = next; |
59 | } |
60 | } |
61 | |
62 | GrGradientBitmapCache::Entry* GrGradientBitmapCache::release(Entry* entry) const { |
63 | if (entry->fPrev) { |
64 | SkASSERT(fHead != entry); |
65 | entry->fPrev->fNext = entry->fNext; |
66 | } else { |
67 | SkASSERT(fHead == entry); |
68 | fHead = entry->fNext; |
69 | } |
70 | if (entry->fNext) { |
71 | SkASSERT(fTail != entry); |
72 | entry->fNext->fPrev = entry->fPrev; |
73 | } else { |
74 | SkASSERT(fTail == entry); |
75 | fTail = entry->fPrev; |
76 | } |
77 | return entry; |
78 | } |
79 | |
80 | void GrGradientBitmapCache::attachToHead(Entry* entry) const { |
81 | entry->fPrev = nullptr; |
82 | entry->fNext = fHead; |
83 | if (fHead) { |
84 | fHead->fPrev = entry; |
85 | } else { |
86 | fTail = entry; |
87 | } |
88 | fHead = entry; |
89 | } |
90 | |
91 | bool GrGradientBitmapCache::find(const void* buffer, size_t size, SkBitmap* bm) const { |
92 | AutoValidate av(this); |
93 | |
94 | Entry* entry = fHead; |
95 | while (entry) { |
96 | if (entry->equals(buffer, size)) { |
97 | if (bm) { |
98 | *bm = entry->fBitmap; |
99 | } |
100 | // move to the head of our list, so we purge it last |
101 | this->release(entry); |
102 | this->attachToHead(entry); |
103 | return true; |
104 | } |
105 | entry = entry->fNext; |
106 | } |
107 | return false; |
108 | } |
109 | |
110 | void GrGradientBitmapCache::add(const void* buffer, size_t len, const SkBitmap& bm) { |
111 | AutoValidate av(this); |
112 | |
113 | if (fEntryCount == fMaxEntries) { |
114 | SkASSERT(fTail); |
115 | delete this->release(fTail); |
116 | fEntryCount -= 1; |
117 | } |
118 | |
119 | Entry* entry = new Entry(buffer, len, bm); |
120 | this->attachToHead(entry); |
121 | fEntryCount += 1; |
122 | } |
123 | |
124 | /////////////////////////////////////////////////////////////////////////////// |
125 | |
126 | |
127 | void GrGradientBitmapCache::fillGradient(const SkPMColor4f* colors, const SkScalar* positions, |
128 | int count, SkColorType colorType, SkBitmap* bitmap) { |
129 | SkHalf* pixelsF16 = reinterpret_cast<SkHalf*>(bitmap->getPixels()); |
130 | uint32_t* pixels32 = reinterpret_cast<uint32_t*>(bitmap->getPixels()); |
131 | |
132 | typedef std::function<void(const Sk4f&, int)> pixelWriteFn_t; |
133 | |
134 | pixelWriteFn_t writeF16Pixel = [&](const Sk4f& x, int index) { |
135 | Sk4h c = SkFloatToHalf_finite_ftz(x); |
136 | pixelsF16[4*index+0] = c[0]; |
137 | pixelsF16[4*index+1] = c[1]; |
138 | pixelsF16[4*index+2] = c[2]; |
139 | pixelsF16[4*index+3] = c[3]; |
140 | }; |
141 | pixelWriteFn_t write8888Pixel = [&](const Sk4f& c, int index) { |
142 | pixels32[index] = Sk4f_toL32(c); |
143 | }; |
144 | |
145 | pixelWriteFn_t writePixel = |
146 | (colorType == kRGBA_F16_SkColorType) ? writeF16Pixel : write8888Pixel; |
147 | |
148 | int prevIndex = 0; |
149 | for (int i = 1; i < count; i++) { |
150 | // Historically, stops have been mapped to [0, 256], with 256 then nudged to the next |
151 | // smaller value, then truncate for the texture index. This seems to produce the best |
152 | // results for some common distributions, so we preserve the behavior. |
153 | int nextIndex = std::min(positions[i] * fResolution, |
154 | SkIntToScalar(fResolution - 1)); |
155 | |
156 | if (nextIndex > prevIndex) { |
157 | Sk4f c0 = Sk4f::Load(colors[i - 1].vec()), |
158 | c1 = Sk4f::Load(colors[i ].vec()); |
159 | |
160 | Sk4f step = Sk4f(1.0f / static_cast<float>(nextIndex - prevIndex)); |
161 | Sk4f delta = (c1 - c0) * step; |
162 | |
163 | for (int curIndex = prevIndex; curIndex <= nextIndex; ++curIndex) { |
164 | writePixel(c0, curIndex); |
165 | c0 += delta; |
166 | } |
167 | } |
168 | prevIndex = nextIndex; |
169 | } |
170 | SkASSERT(prevIndex == fResolution - 1); |
171 | } |
172 | |
173 | void GrGradientBitmapCache::getGradient(const SkPMColor4f* colors, const SkScalar* positions, |
174 | int count, SkColorType colorType, SkAlphaType alphaType, SkBitmap* bitmap) { |
175 | // build our key: [numColors + colors[] + positions[] + alphaType + colorType ] |
176 | static_assert(sizeof(SkPMColor4f) % sizeof(int32_t) == 0, "" ); |
177 | const int colorsAsIntCount = count * sizeof(SkPMColor4f) / sizeof(int32_t); |
178 | int keyCount = 1 + colorsAsIntCount + 1 + 1; |
179 | if (count > 2) { |
180 | keyCount += count - 1; |
181 | } |
182 | |
183 | SkAutoSTMalloc<64, int32_t> storage(keyCount); |
184 | int32_t* buffer = storage.get(); |
185 | |
186 | *buffer++ = count; |
187 | memcpy(buffer, colors, count * sizeof(SkPMColor4f)); |
188 | buffer += colorsAsIntCount; |
189 | if (count > 2) { |
190 | for (int i = 1; i < count; i++) { |
191 | *buffer++ = SkFloat2Bits(positions[i]); |
192 | } |
193 | } |
194 | *buffer++ = static_cast<int32_t>(alphaType); |
195 | *buffer++ = static_cast<int32_t>(colorType); |
196 | SkASSERT(buffer - storage.get() == keyCount); |
197 | |
198 | /////////////////////////////////// |
199 | |
200 | // acquire lock for checking/adding to cache |
201 | SkAutoMutexExclusive ama(fMutex); |
202 | size_t size = keyCount * sizeof(int32_t); |
203 | if (!this->find(storage.get(), size, bitmap)) { |
204 | SkImageInfo info = SkImageInfo::Make(fResolution, 1, colorType, alphaType); |
205 | bitmap->allocPixels(info); |
206 | GrGradientBitmapCache::fillGradient(colors, positions, count, colorType, bitmap); |
207 | bitmap->setImmutable(); |
208 | this->add(storage.get(), size, *bitmap); |
209 | } |
210 | } |
211 | |
212 | /////////////////////////////////////////////////////////////////////////////// |
213 | |
214 | #ifdef SK_DEBUG |
215 | |
216 | void GrGradientBitmapCache::validate() const { |
217 | SkASSERT(fEntryCount >= 0 && fEntryCount <= fMaxEntries); |
218 | |
219 | if (fEntryCount > 0) { |
220 | SkASSERT(nullptr == fHead->fPrev); |
221 | SkASSERT(nullptr == fTail->fNext); |
222 | |
223 | if (fEntryCount == 1) { |
224 | SkASSERT(fHead == fTail); |
225 | } else { |
226 | SkASSERT(fHead != fTail); |
227 | } |
228 | |
229 | Entry* entry = fHead; |
230 | int count = 0; |
231 | while (entry) { |
232 | count += 1; |
233 | entry = entry->fNext; |
234 | } |
235 | SkASSERT(count == fEntryCount); |
236 | |
237 | entry = fTail; |
238 | while (entry) { |
239 | count -= 1; |
240 | entry = entry->fPrev; |
241 | } |
242 | SkASSERT(0 == count); |
243 | } else { |
244 | SkASSERT(nullptr == fHead); |
245 | SkASSERT(nullptr == fTail); |
246 | } |
247 | } |
248 | |
249 | #endif |
250 | |