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