1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2018 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci
9cb93a386Sopenharmony_ci#include "src/gpu/gradients/GrGradientBitmapCache.h"
10cb93a386Sopenharmony_ci
11cb93a386Sopenharmony_ci#include "include/private/SkFloatBits.h"
12cb93a386Sopenharmony_ci#include "include/private/SkHalf.h"
13cb93a386Sopenharmony_ci#include "include/private/SkMalloc.h"
14cb93a386Sopenharmony_ci#include "include/private/SkTemplates.h"
15cb93a386Sopenharmony_ci
16cb93a386Sopenharmony_ci#include <functional>
17cb93a386Sopenharmony_ci
18cb93a386Sopenharmony_cistruct GrGradientBitmapCache::Entry {
19cb93a386Sopenharmony_ci    Entry*      fPrev;
20cb93a386Sopenharmony_ci    Entry*      fNext;
21cb93a386Sopenharmony_ci
22cb93a386Sopenharmony_ci    void*       fBuffer;
23cb93a386Sopenharmony_ci    size_t      fSize;
24cb93a386Sopenharmony_ci    SkBitmap    fBitmap;
25cb93a386Sopenharmony_ci
26cb93a386Sopenharmony_ci    Entry(const void* buffer, size_t size, const SkBitmap& bm)
27cb93a386Sopenharmony_ci            : fPrev(nullptr),
28cb93a386Sopenharmony_ci              fNext(nullptr),
29cb93a386Sopenharmony_ci              fBitmap(bm) {
30cb93a386Sopenharmony_ci        fBuffer = sk_malloc_throw(size);
31cb93a386Sopenharmony_ci        fSize = size;
32cb93a386Sopenharmony_ci        memcpy(fBuffer, buffer, size);
33cb93a386Sopenharmony_ci    }
34cb93a386Sopenharmony_ci
35cb93a386Sopenharmony_ci    ~Entry() { sk_free(fBuffer); }
36cb93a386Sopenharmony_ci
37cb93a386Sopenharmony_ci    bool equals(const void* buffer, size_t size) const {
38cb93a386Sopenharmony_ci        return (fSize == size) && !memcmp(fBuffer, buffer, size);
39cb93a386Sopenharmony_ci    }
40cb93a386Sopenharmony_ci};
41cb93a386Sopenharmony_ci
42cb93a386Sopenharmony_ciGrGradientBitmapCache::GrGradientBitmapCache(int max, int res)
43cb93a386Sopenharmony_ci        : fMaxEntries(max)
44cb93a386Sopenharmony_ci        , fResolution(res) {
45cb93a386Sopenharmony_ci    fEntryCount = 0;
46cb93a386Sopenharmony_ci    fHead = fTail = nullptr;
47cb93a386Sopenharmony_ci
48cb93a386Sopenharmony_ci    this->validate();
49cb93a386Sopenharmony_ci}
50cb93a386Sopenharmony_ci
51cb93a386Sopenharmony_ciGrGradientBitmapCache::~GrGradientBitmapCache() {
52cb93a386Sopenharmony_ci    this->validate();
53cb93a386Sopenharmony_ci
54cb93a386Sopenharmony_ci    Entry* entry = fHead;
55cb93a386Sopenharmony_ci    while (entry) {
56cb93a386Sopenharmony_ci        Entry* next = entry->fNext;
57cb93a386Sopenharmony_ci        delete entry;
58cb93a386Sopenharmony_ci        entry = next;
59cb93a386Sopenharmony_ci    }
60cb93a386Sopenharmony_ci}
61cb93a386Sopenharmony_ci
62cb93a386Sopenharmony_ciGrGradientBitmapCache::Entry* GrGradientBitmapCache::release(Entry* entry) const {
63cb93a386Sopenharmony_ci    if (entry->fPrev) {
64cb93a386Sopenharmony_ci        SkASSERT(fHead != entry);
65cb93a386Sopenharmony_ci        entry->fPrev->fNext = entry->fNext;
66cb93a386Sopenharmony_ci    } else {
67cb93a386Sopenharmony_ci        SkASSERT(fHead == entry);
68cb93a386Sopenharmony_ci        fHead = entry->fNext;
69cb93a386Sopenharmony_ci    }
70cb93a386Sopenharmony_ci    if (entry->fNext) {
71cb93a386Sopenharmony_ci        SkASSERT(fTail != entry);
72cb93a386Sopenharmony_ci        entry->fNext->fPrev = entry->fPrev;
73cb93a386Sopenharmony_ci    } else {
74cb93a386Sopenharmony_ci        SkASSERT(fTail == entry);
75cb93a386Sopenharmony_ci        fTail = entry->fPrev;
76cb93a386Sopenharmony_ci    }
77cb93a386Sopenharmony_ci    return entry;
78cb93a386Sopenharmony_ci}
79cb93a386Sopenharmony_ci
80cb93a386Sopenharmony_civoid GrGradientBitmapCache::attachToHead(Entry* entry) const {
81cb93a386Sopenharmony_ci    entry->fPrev = nullptr;
82cb93a386Sopenharmony_ci    entry->fNext = fHead;
83cb93a386Sopenharmony_ci    if (fHead) {
84cb93a386Sopenharmony_ci        fHead->fPrev = entry;
85cb93a386Sopenharmony_ci    } else {
86cb93a386Sopenharmony_ci        fTail = entry;
87cb93a386Sopenharmony_ci    }
88cb93a386Sopenharmony_ci    fHead = entry;
89cb93a386Sopenharmony_ci}
90cb93a386Sopenharmony_ci
91cb93a386Sopenharmony_cibool GrGradientBitmapCache::find(const void* buffer, size_t size, SkBitmap* bm) const {
92cb93a386Sopenharmony_ci    AutoValidate av(this);
93cb93a386Sopenharmony_ci
94cb93a386Sopenharmony_ci    Entry* entry = fHead;
95cb93a386Sopenharmony_ci    while (entry) {
96cb93a386Sopenharmony_ci        if (entry->equals(buffer, size)) {
97cb93a386Sopenharmony_ci            if (bm) {
98cb93a386Sopenharmony_ci                *bm = entry->fBitmap;
99cb93a386Sopenharmony_ci            }
100cb93a386Sopenharmony_ci            // move to the head of our list, so we purge it last
101cb93a386Sopenharmony_ci            this->release(entry);
102cb93a386Sopenharmony_ci            this->attachToHead(entry);
103cb93a386Sopenharmony_ci            return true;
104cb93a386Sopenharmony_ci        }
105cb93a386Sopenharmony_ci        entry = entry->fNext;
106cb93a386Sopenharmony_ci    }
107cb93a386Sopenharmony_ci    return false;
108cb93a386Sopenharmony_ci}
109cb93a386Sopenharmony_ci
110cb93a386Sopenharmony_civoid GrGradientBitmapCache::add(const void* buffer, size_t len, const SkBitmap& bm) {
111cb93a386Sopenharmony_ci    AutoValidate av(this);
112cb93a386Sopenharmony_ci
113cb93a386Sopenharmony_ci    if (fEntryCount == fMaxEntries) {
114cb93a386Sopenharmony_ci        SkASSERT(fTail);
115cb93a386Sopenharmony_ci        delete this->release(fTail);
116cb93a386Sopenharmony_ci        fEntryCount -= 1;
117cb93a386Sopenharmony_ci    }
118cb93a386Sopenharmony_ci
119cb93a386Sopenharmony_ci    Entry* entry = new Entry(buffer, len, bm);
120cb93a386Sopenharmony_ci    this->attachToHead(entry);
121cb93a386Sopenharmony_ci    fEntryCount += 1;
122cb93a386Sopenharmony_ci}
123cb93a386Sopenharmony_ci
124cb93a386Sopenharmony_ci///////////////////////////////////////////////////////////////////////////////
125cb93a386Sopenharmony_ci
126cb93a386Sopenharmony_ci
127cb93a386Sopenharmony_civoid GrGradientBitmapCache::fillGradient(const SkPMColor4f* colors, const SkScalar* positions,
128cb93a386Sopenharmony_ci                                         int count, SkColorType colorType, SkBitmap* bitmap) {
129cb93a386Sopenharmony_ci    SkHalf* pixelsF16 = reinterpret_cast<SkHalf*>(bitmap->getPixels());
130cb93a386Sopenharmony_ci    uint32_t* pixels32 = reinterpret_cast<uint32_t*>(bitmap->getPixels());
131cb93a386Sopenharmony_ci
132cb93a386Sopenharmony_ci    typedef std::function<void(const Sk4f&, int)> pixelWriteFn_t;
133cb93a386Sopenharmony_ci
134cb93a386Sopenharmony_ci    pixelWriteFn_t writeF16Pixel = [&](const Sk4f& x, int index) {
135cb93a386Sopenharmony_ci        Sk4h c = SkFloatToHalf_finite_ftz(x);
136cb93a386Sopenharmony_ci        pixelsF16[4*index+0] = c[0];
137cb93a386Sopenharmony_ci        pixelsF16[4*index+1] = c[1];
138cb93a386Sopenharmony_ci        pixelsF16[4*index+2] = c[2];
139cb93a386Sopenharmony_ci        pixelsF16[4*index+3] = c[3];
140cb93a386Sopenharmony_ci    };
141cb93a386Sopenharmony_ci    pixelWriteFn_t write8888Pixel = [&](const Sk4f& c, int index) {
142cb93a386Sopenharmony_ci        pixels32[index] = Sk4f_toL32(c);
143cb93a386Sopenharmony_ci    };
144cb93a386Sopenharmony_ci
145cb93a386Sopenharmony_ci    pixelWriteFn_t writePixel =
146cb93a386Sopenharmony_ci            (colorType == kRGBA_F16_SkColorType) ? writeF16Pixel : write8888Pixel;
147cb93a386Sopenharmony_ci
148cb93a386Sopenharmony_ci    int prevIndex = 0;
149cb93a386Sopenharmony_ci    for (int i = 1; i < count; i++) {
150cb93a386Sopenharmony_ci        // Historically, stops have been mapped to [0, 256], with 256 then nudged to the next
151cb93a386Sopenharmony_ci        // smaller value, then truncate for the texture index. This seems to produce the best
152cb93a386Sopenharmony_ci        // results for some common distributions, so we preserve the behavior.
153cb93a386Sopenharmony_ci        int nextIndex = std::min(positions[i] * fResolution,
154cb93a386Sopenharmony_ci                               SkIntToScalar(fResolution - 1));
155cb93a386Sopenharmony_ci
156cb93a386Sopenharmony_ci        if (nextIndex > prevIndex) {
157cb93a386Sopenharmony_ci            Sk4f          c0 = Sk4f::Load(colors[i - 1].vec()),
158cb93a386Sopenharmony_ci                          c1 = Sk4f::Load(colors[i    ].vec());
159cb93a386Sopenharmony_ci
160cb93a386Sopenharmony_ci            Sk4f step = Sk4f(1.0f / static_cast<float>(nextIndex - prevIndex));
161cb93a386Sopenharmony_ci            Sk4f delta = (c1 - c0) * step;
162cb93a386Sopenharmony_ci
163cb93a386Sopenharmony_ci            for (int curIndex = prevIndex; curIndex <= nextIndex; ++curIndex) {
164cb93a386Sopenharmony_ci                writePixel(c0, curIndex);
165cb93a386Sopenharmony_ci                c0 += delta;
166cb93a386Sopenharmony_ci            }
167cb93a386Sopenharmony_ci        }
168cb93a386Sopenharmony_ci        prevIndex = nextIndex;
169cb93a386Sopenharmony_ci    }
170cb93a386Sopenharmony_ci    SkASSERT(prevIndex == fResolution - 1);
171cb93a386Sopenharmony_ci}
172cb93a386Sopenharmony_ci
173cb93a386Sopenharmony_civoid GrGradientBitmapCache::getGradient(const SkPMColor4f* colors, const SkScalar* positions,
174cb93a386Sopenharmony_ci        int count, SkColorType colorType, SkAlphaType alphaType, SkBitmap* bitmap) {
175cb93a386Sopenharmony_ci    // build our key: [numColors + colors[] + positions[] + alphaType + colorType ]
176cb93a386Sopenharmony_ci    static_assert(sizeof(SkPMColor4f) % sizeof(int32_t) == 0, "");
177cb93a386Sopenharmony_ci    const int colorsAsIntCount = count * sizeof(SkPMColor4f) / sizeof(int32_t);
178cb93a386Sopenharmony_ci    int keyCount = 1 + colorsAsIntCount + 1 + 1;
179cb93a386Sopenharmony_ci    if (count > 2) {
180cb93a386Sopenharmony_ci        keyCount += count - 1;
181cb93a386Sopenharmony_ci    }
182cb93a386Sopenharmony_ci
183cb93a386Sopenharmony_ci    SkAutoSTMalloc<64, int32_t> storage(keyCount);
184cb93a386Sopenharmony_ci    int32_t* buffer = storage.get();
185cb93a386Sopenharmony_ci
186cb93a386Sopenharmony_ci    *buffer++ = count;
187cb93a386Sopenharmony_ci    memcpy(buffer, colors, count * sizeof(SkPMColor4f));
188cb93a386Sopenharmony_ci    buffer += colorsAsIntCount;
189cb93a386Sopenharmony_ci    if (count > 2) {
190cb93a386Sopenharmony_ci        for (int i = 1; i < count; i++) {
191cb93a386Sopenharmony_ci            *buffer++ = SkFloat2Bits(positions[i]);
192cb93a386Sopenharmony_ci        }
193cb93a386Sopenharmony_ci    }
194cb93a386Sopenharmony_ci    *buffer++ = static_cast<int32_t>(alphaType);
195cb93a386Sopenharmony_ci    *buffer++ = static_cast<int32_t>(colorType);
196cb93a386Sopenharmony_ci    SkASSERT(buffer - storage.get() == keyCount);
197cb93a386Sopenharmony_ci
198cb93a386Sopenharmony_ci    ///////////////////////////////////
199cb93a386Sopenharmony_ci
200cb93a386Sopenharmony_ci    // acquire lock for checking/adding to cache
201cb93a386Sopenharmony_ci    SkAutoMutexExclusive ama(fMutex);
202cb93a386Sopenharmony_ci    size_t size = keyCount * sizeof(int32_t);
203cb93a386Sopenharmony_ci    if (!this->find(storage.get(), size, bitmap)) {
204cb93a386Sopenharmony_ci        SkImageInfo info = SkImageInfo::Make(fResolution, 1, colorType, alphaType);
205cb93a386Sopenharmony_ci        bitmap->allocPixels(info);
206cb93a386Sopenharmony_ci        GrGradientBitmapCache::fillGradient(colors, positions, count, colorType, bitmap);
207cb93a386Sopenharmony_ci        bitmap->setImmutable();
208cb93a386Sopenharmony_ci        this->add(storage.get(), size, *bitmap);
209cb93a386Sopenharmony_ci    }
210cb93a386Sopenharmony_ci}
211cb93a386Sopenharmony_ci
212cb93a386Sopenharmony_ci///////////////////////////////////////////////////////////////////////////////
213cb93a386Sopenharmony_ci
214cb93a386Sopenharmony_ci#ifdef SK_DEBUG
215cb93a386Sopenharmony_ci
216cb93a386Sopenharmony_civoid GrGradientBitmapCache::validate() const {
217cb93a386Sopenharmony_ci    SkASSERT(fEntryCount >= 0 && fEntryCount <= fMaxEntries);
218cb93a386Sopenharmony_ci
219cb93a386Sopenharmony_ci    if (fEntryCount > 0) {
220cb93a386Sopenharmony_ci        SkASSERT(nullptr == fHead->fPrev);
221cb93a386Sopenharmony_ci        SkASSERT(nullptr == fTail->fNext);
222cb93a386Sopenharmony_ci
223cb93a386Sopenharmony_ci        if (fEntryCount == 1) {
224cb93a386Sopenharmony_ci            SkASSERT(fHead == fTail);
225cb93a386Sopenharmony_ci        } else {
226cb93a386Sopenharmony_ci            SkASSERT(fHead != fTail);
227cb93a386Sopenharmony_ci        }
228cb93a386Sopenharmony_ci
229cb93a386Sopenharmony_ci        Entry* entry = fHead;
230cb93a386Sopenharmony_ci        int count = 0;
231cb93a386Sopenharmony_ci        while (entry) {
232cb93a386Sopenharmony_ci            count += 1;
233cb93a386Sopenharmony_ci            entry = entry->fNext;
234cb93a386Sopenharmony_ci        }
235cb93a386Sopenharmony_ci        SkASSERT(count == fEntryCount);
236cb93a386Sopenharmony_ci
237cb93a386Sopenharmony_ci        entry = fTail;
238cb93a386Sopenharmony_ci        while (entry) {
239cb93a386Sopenharmony_ci            count -= 1;
240cb93a386Sopenharmony_ci            entry = entry->fPrev;
241cb93a386Sopenharmony_ci        }
242cb93a386Sopenharmony_ci        SkASSERT(0 == count);
243cb93a386Sopenharmony_ci    } else {
244cb93a386Sopenharmony_ci        SkASSERT(nullptr == fHead);
245cb93a386Sopenharmony_ci        SkASSERT(nullptr == fTail);
246cb93a386Sopenharmony_ci    }
247cb93a386Sopenharmony_ci}
248cb93a386Sopenharmony_ci
249cb93a386Sopenharmony_ci#endif
250