xref: /aosp_15_r20/external/skia/src/gpu/ganesh/gradients/GrGradientBitmapCache.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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