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