1 /* 2 * Copyright 2016 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 #include "GrColorSpaceXform.h" 9 #include "SkColorSpace.h" 10 #include "SkColorSpace_Base.h" 11 #include "SkMatrix44.h" 12 #include "SkSpinlock.h" 13 14 class GrColorSpaceXformCache { 15 public: 16 using NewValueFn = std::function<sk_sp<GrColorSpaceXform>(void)>; 17 18 GrColorSpaceXformCache() : fSequence(0) {} 19 20 sk_sp<GrColorSpaceXform> findOrAdd(uint64_t key, NewValueFn newValue) { 21 int oldest = 0; 22 for (int i = 0; i < kEntryCount; ++i) { 23 if (fEntries[i].fKey == key) { 24 fEntries[i].fLastUse = fSequence++; 25 return fEntries[i].fXform; 26 } 27 if (fEntries[i].fLastUse < fEntries[oldest].fLastUse) { 28 oldest = i; 29 } 30 } 31 fEntries[oldest].fKey = key; 32 fEntries[oldest].fXform = newValue(); 33 fEntries[oldest].fLastUse = fSequence++; 34 return fEntries[oldest].fXform; 35 } 36 37 private: 38 enum { kEntryCount = 32 }; 39 40 struct Entry { 41 // The default Entry is "valid". Any 64-bit key that is the same 32-bit value repeated 42 // implies no xform is necessary, so nullptr should be returned. This particular case should 43 // never happen, but by initializing all entries with this data, we can avoid special cases 44 // for the array not yet being full. 45 Entry() : fKey(0), fXform(nullptr), fLastUse(0) {} 46 47 uint64_t fKey; 48 sk_sp<GrColorSpaceXform> fXform; 49 uint64_t fLastUse; 50 }; 51 52 Entry fEntries[kEntryCount]; 53 uint64_t fSequence; 54 }; 55 56 GrColorSpaceXform::GrColorSpaceXform(const SkMatrix44& srcToDst) 57 : fSrcToDst(srcToDst) {} 58 59 static SkSpinlock gColorSpaceXformCacheSpinlock; 60 61 sk_sp<GrColorSpaceXform> GrColorSpaceXform::Make(const SkColorSpace* src, const SkColorSpace* dst) { 62 if (!src || !dst) { 63 // Invalid 64 return nullptr; 65 } 66 67 if (src == dst) { 68 // Quick equality check - no conversion needed in this case 69 return nullptr; 70 } 71 72 const SkMatrix44* toXYZD50 = as_CSB(src)->toXYZD50(); 73 const SkMatrix44* fromXYZD50 = as_CSB(dst)->fromXYZD50(); 74 if (!toXYZD50 || !fromXYZD50) { 75 // unsupported colour spaces -- cannot specify gamut as a matrix 76 return nullptr; 77 } 78 79 uint32_t srcHash = as_CSB(src)->toXYZD50Hash(); 80 uint32_t dstHash = as_CSB(dst)->toXYZD50Hash(); 81 if (srcHash == dstHash) { 82 // Identical gamut - no conversion needed in this case 83 SkASSERT(*toXYZD50 == *as_CSB(dst)->toXYZD50() && "Hash collision"); 84 return nullptr; 85 } 86 87 auto deferredResult = [fromXYZD50, toXYZD50]() { 88 SkMatrix44 srcToDst(SkMatrix44::kUninitialized_Constructor); 89 srcToDst.setConcat(*fromXYZD50, *toXYZD50); 90 return sk_make_sp<GrColorSpaceXform>(srcToDst); 91 }; 92 93 if (gColorSpaceXformCacheSpinlock.tryAcquire()) { 94 static GrColorSpaceXformCache* gCache; 95 if (nullptr == gCache) { 96 gCache = new GrColorSpaceXformCache(); 97 } 98 99 uint64_t key = static_cast<uint64_t>(srcHash) << 32 | static_cast<uint64_t>(dstHash); 100 sk_sp<GrColorSpaceXform> xform = gCache->findOrAdd(key, deferredResult); 101 gColorSpaceXformCacheSpinlock.release(); 102 return xform; 103 } else { 104 // Rather than wait for the spin lock, just bypass the cache 105 return deferredResult(); 106 } 107 } 108 109 bool GrColorSpaceXform::Equals(const GrColorSpaceXform* a, const GrColorSpaceXform* b) { 110 if (a == b) { 111 return true; 112 } 113 114 if (!a || !b) { 115 return false; 116 } 117 118 return a->fSrcToDst == b->fSrcToDst; 119 } 120 121 GrColor4f GrColorSpaceXform::apply(const GrColor4f& srcColor) { 122 GrColor4f result; 123 fSrcToDst.mapScalars(srcColor.fRGBA, result.fRGBA); 124 // We always operate on unpremul colors, so clamp to [0,1]. 125 for (int i = 0; i < 4; ++i) { 126 result.fRGBA[i] = SkTPin(result.fRGBA[i], 0.0f, 1.0f); 127 } 128 return result; 129 } 130