Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright 2017 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 "SkColorFilter.h"
      9 #include "SkColorSpacePriv.h"
     10 #include "SkColorSpaceXformer.h"
     11 #include "SkDrawLooper.h"
     12 #include "SkGradientShader.h"
     13 #include "SkImage.h"
     14 #include "SkImage_Base.h"
     15 #include "SkImageFilter.h"
     16 #include "SkImagePriv.h"
     17 #include "SkShaderBase.h"
     18 
     19 SkColorSpaceXformer::SkColorSpaceXformer(sk_sp<SkColorSpace> dst)
     20     : fDst(std::move(dst))
     21     , fFromSRGBSteps(sk_srgb_singleton(), kUnpremul_SkAlphaType,
     22                      fDst.get()         , kUnpremul_SkAlphaType)
     23     , fReentryCount(0) {
     24 
     25     SkRasterPipeline p(&fAlloc);
     26     p.append(SkRasterPipeline::load_8888, &fFromSRGBSrc);
     27     p.append(SkRasterPipeline::swap_rb);
     28     fFromSRGBSteps.apply(&p, kBGRA_8888_SkColorType);
     29     p.append(SkRasterPipeline::swap_rb);
     30     p.append(SkRasterPipeline::store_8888, &fFromSRGBDst);
     31     fFromSRGB = p.compile();
     32 }
     33 
     34 SkColorSpaceXformer::~SkColorSpaceXformer() {}
     35 
     36 std::unique_ptr<SkColorSpaceXformer> SkColorSpaceXformer::Make(sk_sp<SkColorSpace> dst) {
     37     return std::unique_ptr<SkColorSpaceXformer>(new SkColorSpaceXformer{std::move(dst)});
     38 }
     39 
     40 // So what's up with these caches?
     41 //
     42 // We want to cache transformed objects for a couple of reasons:
     43 //
     44 // 1) to avoid redundant work - the inputs are a DAG, not a tree (e.g. same SkImage drawn multiple
     45 //    times in a SkPicture), so if we blindly recurse we could end up transforming the same objects
     46 //    repeatedly.
     47 //
     48 // 2) to avoid topology changes - we want the output to remain isomorphic with the input -- this is
     49 //    particularly important for image filters (to maintain their original DAG structure in order
     50 //    to not defeat their own/internal caching), but also for avoiding unnecessary cloning
     51 //    (e.g. duplicated SkImages allocated for the example in #1 above).
     52 //
     53 // The caching scope is naturaly bound by the lifetime of the SkColorSpaceXformer object, but
     54 // clients may choose to not discard xformers immediately - in which case, caching indefinitely
     55 // is problematic.  The solution is to limit the cache scope to the top level apply() call
     56 // (i.e. we only keep cached objects alive while transforming).
     57 
     58 class SkColorSpaceXformer::AutoCachePurge {
     59 public:
     60     AutoCachePurge(SkColorSpaceXformer* xformer)
     61         : fXformer(xformer) {
     62         fXformer->fReentryCount++;
     63     }
     64 
     65     ~AutoCachePurge() {
     66         SkASSERT(fXformer->fReentryCount > 0);
     67         if (--fXformer->fReentryCount == 0) {
     68             fXformer->purgeCaches();
     69         }
     70     }
     71 
     72 private:
     73     SkColorSpaceXformer* fXformer;
     74 };
     75 
     76 template <typename T>
     77 sk_sp<T> SkColorSpaceXformer::cachedApply(const T* src, Cache<T>* cache,
     78                                           sk_sp<T> (*applyFunc)(const T*, SkColorSpaceXformer*)) {
     79     if (!src) {
     80         return nullptr;
     81     }
     82 
     83     auto key = sk_ref_sp(const_cast<T*>(src));
     84     if (auto* xformed = cache->find(key)) {
     85         return sk_ref_sp(xformed->get());
     86     }
     87 
     88     auto xformed = applyFunc(src, this);
     89     cache->set(std::move(key), xformed);
     90 
     91     return xformed;
     92 }
     93 
     94 void SkColorSpaceXformer::purgeCaches() {
     95     fImageCache.reset();
     96     fColorFilterCache.reset();
     97     fImageFilterCache.reset();
     98 }
     99 
    100 sk_sp<SkImage> SkColorSpaceXformer::apply(const SkImage* src) {
    101     const AutoCachePurge autoPurge(this);
    102     return this->cachedApply<SkImage>(src, &fImageCache,
    103         [](const SkImage* img, SkColorSpaceXformer* xformer) {
    104             return img->makeColorSpace(xformer->fDst);
    105         });
    106 }
    107 
    108 sk_sp<SkImage> SkColorSpaceXformer::apply(const SkBitmap& src) {
    109     const AutoCachePurge autoPurge(this);
    110     sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(src, kNever_SkCopyPixelsMode);
    111     if (!image) {
    112         return nullptr;
    113     }
    114 
    115     sk_sp<SkImage> xformed = image->makeColorSpace(fDst);
    116     // We want to be sure we don't let the kNever_SkCopyPixelsMode image escape this stack frame.
    117     SkASSERT(xformed != image);
    118     return xformed;
    119 }
    120 
    121 sk_sp<SkColorFilter> SkColorSpaceXformer::apply(const SkColorFilter* colorFilter) {
    122     const AutoCachePurge autoPurge(this);
    123     return this->cachedApply<SkColorFilter>(colorFilter, &fColorFilterCache,
    124         [](const SkColorFilter* f, SkColorSpaceXformer* xformer) {
    125             return f->makeColorSpace(xformer);
    126         });
    127 }
    128 
    129 sk_sp<SkImageFilter> SkColorSpaceXformer::apply(const SkImageFilter* imageFilter) {
    130     const AutoCachePurge autoPurge(this);
    131     return this->cachedApply<SkImageFilter>(imageFilter, &fImageFilterCache,
    132         [](const SkImageFilter* f, SkColorSpaceXformer* xformer) {
    133             return f->makeColorSpace(xformer);
    134         });
    135 }
    136 
    137 sk_sp<SkShader> SkColorSpaceXformer::apply(const SkShader* shader) {
    138     const AutoCachePurge autoPurge(this);
    139     return as_SB(shader)->makeColorSpace(this);
    140 }
    141 
    142 void SkColorSpaceXformer::apply(SkColor* xformed, const SkColor* srgb, int n) {
    143     fFromSRGBSrc.pixels = const_cast<SkColor*>(srgb);
    144     fFromSRGBDst.pixels = xformed;
    145     fFromSRGB(0,0,n,1);
    146 }
    147 
    148 SkColor SkColorSpaceXformer::apply(SkColor srgb) {
    149     SkColor xformed;
    150     this->apply(&xformed, &srgb, 1);
    151     return xformed;
    152 }
    153 
    154 SkPaint SkColorSpaceXformer::apply(const SkPaint& src) {
    155     const AutoCachePurge autoPurge(this);
    156 
    157     SkPaint dst = src;
    158 
    159     // All SkColorSpaces have the same black point.
    160     if (src.getColor() & 0xffffff) {
    161         dst.setColor(this->apply(src.getColor()));
    162     }
    163 
    164     if (auto shader = src.getShader()) {
    165         dst.setShader(this->apply(shader));
    166     }
    167 
    168     if (auto cf = src.getColorFilter()) {
    169         dst.setColorFilter(this->apply(cf));
    170     }
    171 
    172     if (auto looper = src.getDrawLooper()) {
    173         dst.setDrawLooper(looper->makeColorSpace(this));
    174     }
    175 
    176     if (auto imageFilter = src.getImageFilter()) {
    177         dst.setImageFilter(this->apply(imageFilter));
    178     }
    179 
    180     return dst;
    181 }
    182 
    183 SkCanvas::Lattice SkColorSpaceXformer::apply(const SkCanvas::Lattice& lattice,
    184                                              SkColor* colorBuffer, int count) {
    185     if (count) {
    186         this->apply(colorBuffer, lattice.fColors, count);
    187         return {lattice.fXDivs, lattice.fYDivs, lattice.fRectTypes,
    188                 lattice.fXCount, lattice.fYCount, lattice.fBounds, colorBuffer};
    189     }
    190 
    191     return lattice;
    192 }
    193