• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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