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/ganesh/gradients/GrGradientBitmapCache.h"
10
11 #include "include/private/base/SkMalloc.h"
12 #include "include/private/base/SkTemplates.h"
13 #include "src/base/SkFloatBits.h"
14 #include "src/base/SkHalf.h"
15 #include "src/core/SkRasterPipeline.h"
16 #include "src/core/SkRasterPipelineOpList.h"
17 #include "src/shaders/gradients/SkGradientBaseShader.h"
18
19 #include <functional>
20
21 using namespace skia_private;
22
23 struct GrGradientBitmapCache::Entry {
24 Entry* fPrev;
25 Entry* fNext;
26
27 void* fBuffer;
28 size_t fSize;
29 SkBitmap fBitmap;
30
EntryGrGradientBitmapCache::Entry31 Entry(const void* buffer, size_t size, const SkBitmap& bm)
32 : fPrev(nullptr),
33 fNext(nullptr),
34 fBitmap(bm) {
35 fBuffer = sk_malloc_throw(size);
36 fSize = size;
37 memcpy(fBuffer, buffer, size);
38 }
39
~EntryGrGradientBitmapCache::Entry40 ~Entry() { sk_free(fBuffer); }
41
equalsGrGradientBitmapCache::Entry42 bool equals(const void* buffer, size_t size) const {
43 return (fSize == size) && !memcmp(fBuffer, buffer, size);
44 }
45 };
46
GrGradientBitmapCache(int max,int res)47 GrGradientBitmapCache::GrGradientBitmapCache(int max, int res)
48 : fMaxEntries(max)
49 , fResolution(res) {
50 fEntryCount = 0;
51 fHead = fTail = nullptr;
52
53 this->validate();
54 }
55
~GrGradientBitmapCache()56 GrGradientBitmapCache::~GrGradientBitmapCache() {
57 this->validate();
58
59 Entry* entry = fHead;
60 while (entry) {
61 Entry* next = entry->fNext;
62 delete entry;
63 entry = next;
64 }
65 }
66
release(Entry * entry) const67 GrGradientBitmapCache::Entry* GrGradientBitmapCache::release(Entry* entry) const {
68 if (entry->fPrev) {
69 SkASSERT(fHead != entry);
70 entry->fPrev->fNext = entry->fNext;
71 } else {
72 SkASSERT(fHead == entry);
73 fHead = entry->fNext;
74 }
75 if (entry->fNext) {
76 SkASSERT(fTail != entry);
77 entry->fNext->fPrev = entry->fPrev;
78 } else {
79 SkASSERT(fTail == entry);
80 fTail = entry->fPrev;
81 }
82 return entry;
83 }
84
attachToHead(Entry * entry) const85 void GrGradientBitmapCache::attachToHead(Entry* entry) const {
86 entry->fPrev = nullptr;
87 entry->fNext = fHead;
88 if (fHead) {
89 fHead->fPrev = entry;
90 } else {
91 fTail = entry;
92 }
93 fHead = entry;
94 }
95
find(const void * buffer,size_t size,SkBitmap * bm) const96 bool GrGradientBitmapCache::find(const void* buffer, size_t size, SkBitmap* bm) const {
97 AutoValidate av(this);
98
99 Entry* entry = fHead;
100 while (entry) {
101 if (entry->equals(buffer, size)) {
102 if (bm) {
103 *bm = entry->fBitmap;
104 }
105 // move to the head of our list, so we purge it last
106 this->release(entry);
107 this->attachToHead(entry);
108 return true;
109 }
110 entry = entry->fNext;
111 }
112 return false;
113 }
114
add(const void * buffer,size_t len,const SkBitmap & bm)115 void GrGradientBitmapCache::add(const void* buffer, size_t len, const SkBitmap& bm) {
116 AutoValidate av(this);
117
118 if (fEntryCount == fMaxEntries) {
119 SkASSERT(fTail);
120 delete this->release(fTail);
121 fEntryCount -= 1;
122 }
123
124 Entry* entry = new Entry(buffer, len, bm);
125 this->attachToHead(entry);
126 fEntryCount += 1;
127 }
128
129 ///////////////////////////////////////////////////////////////////////////////
130
fillGradient(const SkPMColor4f * colors,const SkScalar * positions,int count,bool colorsAreOpaque,const SkGradientShader::Interpolation & interpolation,const SkColorSpace * intermediateColorSpace,const SkColorSpace * dstColorSpace,SkBitmap * bitmap)131 void GrGradientBitmapCache::fillGradient(const SkPMColor4f* colors,
132 const SkScalar* positions,
133 int count,
134 bool colorsAreOpaque,
135 const SkGradientShader::Interpolation& interpolation,
136 const SkColorSpace* intermediateColorSpace,
137 const SkColorSpace* dstColorSpace,
138 SkBitmap* bitmap) {
139 SkArenaAlloc alloc(/*firstHeapAllocation=*/0);
140 SkRasterPipeline p(&alloc);
141 SkRasterPipeline_MemoryCtx ctx = { bitmap->getPixels(), 0 };
142
143 p.append(SkRasterPipelineOp::seed_shader);
144 p.appendMatrix(&alloc, SkMatrix::Scale(1.0f / bitmap->width(), 1.0f));
145 SkGradientBaseShader::AppendGradientFillStages(&p, &alloc, colors, positions, count);
146 SkGradientBaseShader::AppendInterpolatedToDstStages(
147 &p, &alloc, colorsAreOpaque, interpolation, intermediateColorSpace, dstColorSpace);
148 p.appendStore(bitmap->colorType(), &ctx);
149 p.run(0, 0, bitmap->width(), 1);
150 }
151
getGradient(const SkPMColor4f * colors,const SkScalar * positions,int count,bool colorsAreOpaque,const SkGradientShader::Interpolation & interpolation,const SkColorSpace * intermediateColorSpace,const SkColorSpace * dstColorSpace,SkColorType colorType,SkAlphaType alphaType,SkBitmap * bitmap)152 void GrGradientBitmapCache::getGradient(const SkPMColor4f* colors,
153 const SkScalar* positions,
154 int count,
155 bool colorsAreOpaque,
156 const SkGradientShader::Interpolation& interpolation,
157 const SkColorSpace* intermediateColorSpace,
158 const SkColorSpace* dstColorSpace,
159 SkColorType colorType,
160 SkAlphaType alphaType,
161 SkBitmap* bitmap) {
162 // Build our key:
163 // [numColors + colors[] + positions[] + alphaType + colorType + interpolation + dstColorSpace]
164 // NOTE: colorsAreOpaque is redundant with the actual colors. intermediateColorSpace is fully
165 // determined by interpolation and dstColorSpace.
166 static_assert(sizeof(SkPMColor4f) % sizeof(int32_t) == 0, "");
167 const int colorsAsIntCount = count * sizeof(SkPMColor4f) / sizeof(int32_t);
168 SkASSERT(count > 2); // Otherwise, we should have used the single-interval colorizer
169 const int keyCount = 1 + // count
170 colorsAsIntCount + // colors
171 (count - 2) + // positions
172 1 + // alphaType
173 1 + // colorType
174 3 + // interpolation
175 (dstColorSpace ? 2 : 0); // dstColorSpace
176
177 AutoSTMalloc<64, int32_t> storage(keyCount);
178 int32_t* buffer = storage.get();
179
180 *buffer++ = count;
181 memcpy(buffer, colors, count * sizeof(SkPMColor4f));
182 buffer += colorsAsIntCount;
183 for (int i = 1; i < count - 1; i++) {
184 *buffer++ = SkFloat2Bits(positions[i]);
185 }
186 *buffer++ = static_cast<int32_t>(alphaType);
187 *buffer++ = static_cast<int32_t>(colorType);
188 *buffer++ = static_cast<int32_t>(interpolation.fInPremul);
189 *buffer++ = static_cast<int32_t>(interpolation.fColorSpace);
190 *buffer++ = static_cast<int32_t>(interpolation.fHueMethod);
191 if (dstColorSpace) {
192 *buffer++ = dstColorSpace->toXYZD50Hash();
193 *buffer++ = dstColorSpace->transferFnHash();
194 }
195 SkASSERT(buffer - storage.get() == keyCount);
196
197 ///////////////////////////////////
198
199 // acquire lock for checking/adding to cache
200 SkAutoMutexExclusive ama(fMutex);
201 size_t size = keyCount * sizeof(int32_t);
202 if (!this->find(storage.get(), size, bitmap)) {
203 SkImageInfo info = SkImageInfo::Make(fResolution, 1, colorType, alphaType);
204 bitmap->allocPixels(info);
205 this->fillGradient(colors,
206 positions,
207 count,
208 colorsAreOpaque,
209 interpolation,
210 intermediateColorSpace,
211 dstColorSpace,
212 bitmap);
213 bitmap->setImmutable();
214 this->add(storage.get(), size, *bitmap);
215 }
216 }
217
218 ///////////////////////////////////////////////////////////////////////////////
219
220 #ifdef SK_DEBUG
221
validate() const222 void GrGradientBitmapCache::validate() const {
223 SkASSERT(fEntryCount >= 0 && fEntryCount <= fMaxEntries);
224
225 if (fEntryCount > 0) {
226 SkASSERT(nullptr == fHead->fPrev);
227 SkASSERT(nullptr == fTail->fNext);
228
229 if (fEntryCount == 1) {
230 SkASSERT(fHead == fTail);
231 } else {
232 SkASSERT(fHead != fTail);
233 }
234
235 Entry* entry = fHead;
236 int count = 0;
237 while (entry) {
238 count += 1;
239 entry = entry->fNext;
240 }
241 SkASSERT(count == fEntryCount);
242
243 entry = fTail;
244 while (entry) {
245 count -= 1;
246 entry = entry->fPrev;
247 }
248 SkASSERT(0 == count);
249 } else {
250 SkASSERT(nullptr == fHead);
251 SkASSERT(nullptr == fTail);
252 }
253 }
254
255 #endif
256