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