Home | History | Annotate | Download | only in gpu
      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