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