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