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/SkFloatBits.h"
12 #include "src/base/SkHalf.h"
13 #include "include/private/base/SkTemplates.h"
14 #include "include/private/base/SkMalloc.h"
15 #include "src/core/SkRasterPipeline.h"
16 #include "src/shaders/gradients/SkGradientShaderBase.h"
17
18 #include <functional>
19
20 using namespace skia_private;
21
22 struct GrGradientBitmapCache::Entry {
23 Entry* fPrev;
24 Entry* fNext;
25
26 void* fBuffer;
27 size_t fSize;
28 SkBitmap fBitmap;
29
EntryGrGradientBitmapCache::Entry30 Entry(const void* buffer, size_t size, const SkBitmap& bm)
31 : fPrev(nullptr),
32 fNext(nullptr),
33 fBitmap(bm) {
34 fBuffer = sk_malloc_throw(size);
35 fSize = size;
36 memcpy(fBuffer, buffer, size);
37 }
38
~EntryGrGradientBitmapCache::Entry39 ~Entry() { sk_free(fBuffer); }
40
equalsGrGradientBitmapCache::Entry41 bool equals(const void* buffer, size_t size) const {
42 return (fSize == size) && !memcmp(fBuffer, buffer, size);
43 }
44 };
45
GrGradientBitmapCache(int max,int res)46 GrGradientBitmapCache::GrGradientBitmapCache(int max, int res)
47 : fMaxEntries(max)
48 , fResolution(res) {
49 fEntryCount = 0;
50 fHead = fTail = nullptr;
51
52 this->validate();
53 }
54
~GrGradientBitmapCache()55 GrGradientBitmapCache::~GrGradientBitmapCache() {
56 this->validate();
57
58 Entry* entry = fHead;
59 while (entry) {
60 Entry* next = entry->fNext;
61 delete entry;
62 entry = next;
63 }
64 }
65
release(Entry * entry) const66 GrGradientBitmapCache::Entry* GrGradientBitmapCache::release(Entry* entry) const {
67 if (entry->fPrev) {
68 SkASSERT(fHead != entry);
69 entry->fPrev->fNext = entry->fNext;
70 } else {
71 SkASSERT(fHead == entry);
72 fHead = entry->fNext;
73 }
74 if (entry->fNext) {
75 SkASSERT(fTail != entry);
76 entry->fNext->fPrev = entry->fPrev;
77 } else {
78 SkASSERT(fTail == entry);
79 fTail = entry->fPrev;
80 }
81 return entry;
82 }
83
attachToHead(Entry * entry) const84 void GrGradientBitmapCache::attachToHead(Entry* entry) const {
85 entry->fPrev = nullptr;
86 entry->fNext = fHead;
87 if (fHead) {
88 fHead->fPrev = entry;
89 } else {
90 fTail = entry;
91 }
92 fHead = entry;
93 }
94
find(const void * buffer,size_t size,SkBitmap * bm) const95 bool GrGradientBitmapCache::find(const void* buffer, size_t size, SkBitmap* bm) const {
96 AutoValidate av(this);
97
98 Entry* entry = fHead;
99 while (entry) {
100 if (entry->equals(buffer, size)) {
101 if (bm) {
102 *bm = entry->fBitmap;
103 }
104 // move to the head of our list, so we purge it last
105 this->release(entry);
106 this->attachToHead(entry);
107 return true;
108 }
109 entry = entry->fNext;
110 }
111 return false;
112 }
113
add(const void * buffer,size_t len,const SkBitmap & bm)114 void GrGradientBitmapCache::add(const void* buffer, size_t len, const SkBitmap& bm) {
115 AutoValidate av(this);
116
117 if (fEntryCount == fMaxEntries) {
118 SkASSERT(fTail);
119 delete this->release(fTail);
120 fEntryCount -= 1;
121 }
122
123 Entry* entry = new Entry(buffer, len, bm);
124 this->attachToHead(entry);
125 fEntryCount += 1;
126 }
127
128 ///////////////////////////////////////////////////////////////////////////////
129
fillGradient(const SkPMColor4f * colors,const SkScalar * positions,int count,SkBitmap * bitmap)130 void GrGradientBitmapCache::fillGradient(const SkPMColor4f* colors, const SkScalar* positions,
131 int count, SkBitmap* bitmap) {
132 SkArenaAlloc alloc(/*firstHeapAllocation=*/0);
133 SkRasterPipeline p(&alloc);
134 SkRasterPipeline_MemoryCtx ctx = { bitmap->getPixels(), 0 };
135
136 p.append(SkRasterPipelineOp::seed_shader);
137 p.append_matrix(&alloc, SkMatrix::Scale(1.0f / bitmap->width(), 1.0f));
138 SkGradientShaderBase::AppendGradientFillStages(&p, &alloc, colors, positions, count);
139 p.append_store(bitmap->colorType(), &ctx);
140 p.run(0, 0, bitmap->width(), 1);
141 }
142
getGradient(const SkPMColor4f * colors,const SkScalar * positions,int count,SkColorType colorType,SkAlphaType alphaType,SkBitmap * bitmap)143 void GrGradientBitmapCache::getGradient(const SkPMColor4f* colors,
144 const SkScalar* positions,
145 int count,
146 SkColorType colorType,
147 SkAlphaType alphaType,
148 SkBitmap* bitmap) {
149 // build our key: [numColors + colors[] + positions[] + alphaType + colorType ]
150 static_assert(sizeof(SkPMColor4f) % sizeof(int32_t) == 0, "");
151 const int colorsAsIntCount = count * sizeof(SkPMColor4f) / sizeof(int32_t);
152 SkASSERT(count > 2); // Otherwise, we should have used the single-interval colorizer
153 const int keyCount = 1 + // count
154 colorsAsIntCount + // colors
155 (count - 2) + // positions
156 1 + // alphaType
157 1; // colorType
158
159 AutoSTMalloc<64, int32_t> storage(keyCount);
160 int32_t* buffer = storage.get();
161
162 *buffer++ = count;
163 memcpy(buffer, colors, count * sizeof(SkPMColor4f));
164 buffer += colorsAsIntCount;
165 for (int i = 1; i < count - 1; i++) {
166 *buffer++ = SkFloat2Bits(positions[i]);
167 }
168 *buffer++ = static_cast<int32_t>(alphaType);
169 *buffer++ = static_cast<int32_t>(colorType);
170 SkASSERT(buffer - storage.get() == keyCount);
171
172 ///////////////////////////////////
173
174 // acquire lock for checking/adding to cache
175 SkAutoMutexExclusive ama(fMutex);
176 size_t size = keyCount * sizeof(int32_t);
177 if (!this->find(storage.get(), size, bitmap)) {
178 SkImageInfo info = SkImageInfo::Make(fResolution, 1, colorType, alphaType);
179 bitmap->allocPixels(info);
180 this->fillGradient(colors, positions, count, bitmap);
181 bitmap->setImmutable();
182 this->add(storage.get(), size, *bitmap);
183 }
184 }
185
186 ///////////////////////////////////////////////////////////////////////////////
187
188 #ifdef SK_DEBUG
189
validate() const190 void GrGradientBitmapCache::validate() const {
191 SkASSERT(fEntryCount >= 0 && fEntryCount <= fMaxEntries);
192
193 if (fEntryCount > 0) {
194 SkASSERT(nullptr == fHead->fPrev);
195 SkASSERT(nullptr == fTail->fNext);
196
197 if (fEntryCount == 1) {
198 SkASSERT(fHead == fTail);
199 } else {
200 SkASSERT(fHead != fTail);
201 }
202
203 Entry* entry = fHead;
204 int count = 0;
205 while (entry) {
206 count += 1;
207 entry = entry->fNext;
208 }
209 SkASSERT(count == fEntryCount);
210
211 entry = fTail;
212 while (entry) {
213 count -= 1;
214 entry = entry->fPrev;
215 }
216 SkASSERT(0 == count);
217 } else {
218 SkASSERT(nullptr == fHead);
219 SkASSERT(nullptr == fTail);
220 }
221 }
222
223 #endif
224