• 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/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