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