respect srgb gamma when building mips
Proposed policy:
- If the target is *legacy* (e.g. L32/PMColor) ignore gamma
- If the target is S32/F16 respect gamma
BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2029373004
Review-Url: https://codereview.chromium.org/2029373004
diff --git a/src/core/SkBitmapCache.cpp b/src/core/SkBitmapCache.cpp
index 83eec1b..153a247 100644
--- a/src/core/SkBitmapCache.cpp
+++ b/src/core/SkBitmapCache.cpp
@@ -230,18 +230,21 @@
struct MipMapKey : public SkResourceCache::Key {
public:
- MipMapKey(uint32_t genID, const SkIRect& bounds) : fGenID(genID), fBounds(bounds) {
+ MipMapKey(uint32_t genID, SkSourceGammaTreatment treatment, const SkIRect& bounds)
+ : fGenID(genID), fSrcGammaTreatment(static_cast<uint32_t>(treatment)), fBounds(bounds)
+ {
this->init(&gMipMapKeyNamespaceLabel, SkMakeResourceCacheSharedIDForBitmap(genID),
- sizeof(fGenID) + sizeof(fBounds));
+ sizeof(fGenID) + sizeof(fSrcGammaTreatment) + sizeof(fBounds));
}
uint32_t fGenID;
+ uint32_t fSrcGammaTreatment;
SkIRect fBounds;
};
struct MipMapRec : public SkResourceCache::Rec {
- MipMapRec(const SkBitmap& src, const SkMipMap* result)
- : fKey(src.getGenerationID(), get_bounds_from_bitmap(src))
+ MipMapRec(const SkBitmap& src, SkSourceGammaTreatment treatment, const SkMipMap* result)
+ : fKey(src.getGenerationID(), treatment, get_bounds_from_bitmap(src))
, fMipMap(result)
{
fMipMap->attachToCacheAndRef();
@@ -279,9 +282,10 @@
}
const SkMipMap* SkMipMapCache::FindAndRef(const SkBitmapCacheDesc& desc,
+ SkSourceGammaTreatment treatment,
SkResourceCache* localCache) {
// Note: we ignore width/height from desc, just need id and bounds
- MipMapKey key(desc.fImageID, desc.fBounds);
+ MipMapKey key(desc.fImageID, treatment, desc.fBounds);
const SkMipMap* result;
if (!CHECK_LOCAL(localCache, find, Find, key, MipMapRec::Finder, &result)) {
@@ -295,10 +299,11 @@
: SkResourceCache::GetDiscardableFactory();
}
-const SkMipMap* SkMipMapCache::AddAndRef(const SkBitmap& src, SkResourceCache* localCache) {
- SkMipMap* mipmap = SkMipMap::Build(src, get_fact(localCache));
+const SkMipMap* SkMipMapCache::AddAndRef(const SkBitmap& src, SkSourceGammaTreatment treatment,
+ SkResourceCache* localCache) {
+ SkMipMap* mipmap = SkMipMap::Build(src, treatment, get_fact(localCache));
if (mipmap) {
- MipMapRec* rec = new MipMapRec(src, mipmap);
+ MipMapRec* rec = new MipMapRec(src, treatment, mipmap);
CHECK_LOCAL(localCache, add, Add, rec);
src.pixelRef()->notifyAddedToCache();
}
diff --git a/src/core/SkBitmapCache.h b/src/core/SkBitmapCache.h
index 9371e77..76bcef0 100644
--- a/src/core/SkBitmapCache.h
+++ b/src/core/SkBitmapCache.h
@@ -8,12 +8,11 @@
#ifndef SkBitmapCache_DEFINED
#define SkBitmapCache_DEFINED
-#include "SkScalar.h"
#include "SkBitmap.h"
+#include "SkMipMap.h"
class SkImage;
class SkResourceCache;
-class SkMipMap;
uint64_t SkMakeResourceCacheSharedIDForBitmap(uint32_t bitmapGenID);
@@ -73,9 +72,10 @@
class SkMipMapCache {
public:
- static const SkMipMap* FindAndRef(const SkBitmapCacheDesc&,
+ static const SkMipMap* FindAndRef(const SkBitmapCacheDesc&, SkSourceGammaTreatment,
SkResourceCache* localCache = nullptr);
- static const SkMipMap* AddAndRef(const SkBitmap& src, SkResourceCache* localCache = nullptr);
+ static const SkMipMap* AddAndRef(const SkBitmap& src, SkSourceGammaTreatment,
+ SkResourceCache* localCache = nullptr);
};
#endif
diff --git a/src/core/SkBitmapController.cpp b/src/core/SkBitmapController.cpp
index ac1029d..f4ee0fb 100644
--- a/src/core/SkBitmapController.cpp
+++ b/src/core/SkBitmapController.cpp
@@ -44,10 +44,12 @@
class SkDefaultBitmapControllerState : public SkBitmapController::State {
public:
- SkDefaultBitmapControllerState(const SkBitmapProvider&, const SkMatrix& inv, SkFilterQuality);
+ SkDefaultBitmapControllerState(const SkBitmapProvider&, const SkMatrix& inv, SkFilterQuality,
+ SkSourceGammaTreatment);
private:
SkBitmap fResultBitmap;
+ SkSourceGammaTreatment fSrcGammaTreatment;
SkAutoTUnref<const SkMipMap> fCurrMip;
bool processHQRequest(const SkBitmapProvider&);
@@ -164,13 +166,13 @@
}
if (invScaleSize.width() > SK_Scalar1 || invScaleSize.height() > SK_Scalar1) {
- fCurrMip.reset(SkMipMapCache::FindAndRef(provider.makeCacheDesc()));
+ fCurrMip.reset(SkMipMapCache::FindAndRef(provider.makeCacheDesc(), fSrcGammaTreatment));
if (nullptr == fCurrMip.get()) {
SkBitmap orig;
if (!provider.asBitmap(&orig)) {
return false;
}
- fCurrMip.reset(SkMipMapCache::AddAndRef(orig));
+ fCurrMip.reset(SkMipMapCache::AddAndRef(orig, fSrcGammaTreatment));
if (nullptr == fCurrMip.get()) {
return false;
}
@@ -200,9 +202,11 @@
SkDefaultBitmapControllerState::SkDefaultBitmapControllerState(const SkBitmapProvider& provider,
const SkMatrix& inv,
- SkFilterQuality qual) {
+ SkFilterQuality qual,
+ SkSourceGammaTreatment treatment) {
fInvMatrix = inv;
fQuality = qual;
+ fSrcGammaTreatment = treatment;
if (this->processHQRequest(provider) || this->processMediumRequest(provider)) {
SkASSERT(fResultBitmap.getPixels());
@@ -223,5 +227,6 @@
const SkMatrix& inverse,
SkFilterQuality quality,
void* storage, size_t size) {
- return SkInPlaceNewCheck<SkDefaultBitmapControllerState>(storage, size, bm, inverse, quality);
+ return SkInPlaceNewCheck<SkDefaultBitmapControllerState>(storage, size, bm, inverse, quality,
+ fSrcGammaTreatment);
}
diff --git a/src/core/SkBitmapController.h b/src/core/SkBitmapController.h
index 86b8755..f31c8ee 100644
--- a/src/core/SkBitmapController.h
+++ b/src/core/SkBitmapController.h
@@ -53,13 +53,18 @@
///////////////////////////////////////////////////////////////////////////////////////////////////
+#include "SkMipMap.h"
+
class SkDefaultBitmapController : public SkBitmapController {
public:
- SkDefaultBitmapController() {}
+ SkDefaultBitmapController(SkSourceGammaTreatment treatment) : fSrcGammaTreatment(treatment) {}
protected:
State* onRequestBitmap(const SkBitmapProvider&, const SkMatrix& inverse, SkFilterQuality,
void* storage, size_t storageSize) override;
+
+private:
+ const SkSourceGammaTreatment fSrcGammaTreatment;
};
#endif
diff --git a/src/core/SkBitmapProcShader.cpp b/src/core/SkBitmapProcShader.cpp
index 972d731..c86dfdd 100644
--- a/src/core/SkBitmapProcShader.cpp
+++ b/src/core/SkBitmapProcShader.cpp
@@ -229,10 +229,11 @@
// Decide if we can/want to use the new linear pipeline
bool useLinearPipeline = choose_linear_pipeline(rec, provider.info());
+ SkSourceGammaTreatment treatment = SkMipMap::DeduceTreatment(rec);
if (useLinearPipeline) {
void* infoStorage = (char*)storage + sizeof(LinearPipelineContext);
- SkBitmapProcInfo* info = new (infoStorage) SkBitmapProcInfo(provider, tmx, tmy);
+ SkBitmapProcInfo* info = new (infoStorage) SkBitmapProcInfo(provider, tmx, tmy, treatment);
if (!info->init(totalInverse, *rec.fPaint)) {
info->~SkBitmapProcInfo();
return nullptr;
@@ -241,7 +242,8 @@
return new (storage) LinearPipelineContext(shader, rec, info);
} else {
void* stateStorage = (char*)storage + sizeof(BitmapProcShaderContext);
- SkBitmapProcState* state = new (stateStorage) SkBitmapProcState(provider, tmx, tmy);
+ SkBitmapProcState* state = new (stateStorage) SkBitmapProcState(provider, tmx, tmy,
+ treatment);
if (!state->setup(totalInverse, *rec.fPaint)) {
state->~SkBitmapProcState();
return nullptr;
diff --git a/src/core/SkBitmapProcState.cpp b/src/core/SkBitmapProcState.cpp
index 33dd8f5..900b367 100644
--- a/src/core/SkBitmapProcState.cpp
+++ b/src/core/SkBitmapProcState.cpp
@@ -37,18 +37,22 @@
#include "SkBitmapProcState_procs.h"
SkBitmapProcInfo::SkBitmapProcInfo(const SkBitmapProvider& provider,
- SkShader::TileMode tmx, SkShader::TileMode tmy)
+ SkShader::TileMode tmx, SkShader::TileMode tmy,
+ SkSourceGammaTreatment treatment)
: fProvider(provider)
, fTileModeX(tmx)
, fTileModeY(tmy)
+ , fSrcGammaTreatment(treatment)
, fBMState(nullptr)
{}
SkBitmapProcInfo::SkBitmapProcInfo(const SkBitmap& bm,
- SkShader::TileMode tmx, SkShader::TileMode tmy)
+ SkShader::TileMode tmx, SkShader::TileMode tmy,
+ SkSourceGammaTreatment treatment)
: fProvider(SkBitmapProvider(bm))
, fTileModeX(tmx)
, fTileModeY(tmy)
+ , fSrcGammaTreatment(treatment)
, fBMState(nullptr)
{}
@@ -129,7 +133,7 @@
allow_ignore_fractional_translate = false;
}
- SkDefaultBitmapController controller;
+ SkDefaultBitmapController controller(fSrcGammaTreatment);
fBMState = controller.requestBitmap(fProvider, inv, paint.getFilterQuality(),
fBMStateStorage.get(), fBMStateStorage.size());
// Note : we allow the controller to return an empty (zero-dimension) result. Should we?
diff --git a/src/core/SkBitmapProcState.h b/src/core/SkBitmapProcState.h
index 40dc31a..e2e4f96 100644
--- a/src/core/SkBitmapProcState.h
+++ b/src/core/SkBitmapProcState.h
@@ -28,8 +28,10 @@
class SkPaint;
struct SkBitmapProcInfo {
- SkBitmapProcInfo(const SkBitmapProvider&, SkShader::TileMode tmx, SkShader::TileMode tmy);
- SkBitmapProcInfo(const SkBitmap&, SkShader::TileMode tmx, SkShader::TileMode tmy);
+ SkBitmapProcInfo(const SkBitmapProvider&, SkShader::TileMode tmx, SkShader::TileMode tmy,
+ SkSourceGammaTreatment);
+ SkBitmapProcInfo(const SkBitmap&, SkShader::TileMode tmx, SkShader::TileMode tmy,
+ SkSourceGammaTreatment);
~SkBitmapProcInfo();
const SkBitmapProvider fProvider;
@@ -43,6 +45,7 @@
SkShader::TileMode fTileModeY;
SkFilterQuality fFilterQuality;
SkMatrix::TypeMask fInvType;
+ SkSourceGammaTreatment fSrcGammaTreatment;
bool init(const SkMatrix& inverse, const SkPaint&);
@@ -55,10 +58,12 @@
};
struct SkBitmapProcState : public SkBitmapProcInfo {
- SkBitmapProcState(const SkBitmapProvider& prov, SkShader::TileMode tmx, SkShader::TileMode tmy)
- : SkBitmapProcInfo(prov, tmx, tmy) {}
- SkBitmapProcState(const SkBitmap& bitmap, SkShader::TileMode tmx, SkShader::TileMode tmy)
- : SkBitmapProcInfo(bitmap, tmx, tmy) {}
+ SkBitmapProcState(const SkBitmapProvider& prov, SkShader::TileMode tmx, SkShader::TileMode tmy,
+ SkSourceGammaTreatment treatment)
+ : SkBitmapProcInfo(prov, tmx, tmy, treatment) {}
+ SkBitmapProcState(const SkBitmap& bitmap, SkShader::TileMode tmx, SkShader::TileMode tmy,
+ SkSourceGammaTreatment treatment)
+ : SkBitmapProcInfo(bitmap, tmx, tmy, treatment) {}
bool setup(const SkMatrix& inv, const SkPaint& paint) {
return this->init(inv, paint) && this->chooseProcs();
diff --git a/src/core/SkLightingShader.cpp b/src/core/SkLightingShader.cpp
index a2ce52f..aaf29fc 100644
--- a/src/core/SkLightingShader.cpp
+++ b/src/core/SkLightingShader.cpp
@@ -677,7 +677,8 @@
void* diffuseStateStorage = (char*)storage + sizeof(LightingShaderContext);
SkBitmapProcState* diffuseState = new (diffuseStateStorage) SkBitmapProcState(fDiffuseMap,
- SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
+ SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
+ SkMipMap::DeduceTreatment(rec));
SkASSERT(diffuseState);
if (!diffuseState->setup(diffTotalInv, *rec.fPaint)) {
diffuseState->~SkBitmapProcState();
@@ -686,7 +687,8 @@
void* normalStateStorage = (char*)storage + sizeof(LightingShaderContext) + sizeof(SkBitmapProcState);
SkBitmapProcState* normalState = new (normalStateStorage) SkBitmapProcState(fNormalMap,
- SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
+ SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
+ SkMipMap::DeduceTreatment(rec));
SkASSERT(normalState);
if (!normalState->setup(normTotalInv, *rec.fPaint)) {
diffuseState->~SkBitmapProcState();
diff --git a/src/core/SkMipMap.cpp b/src/core/SkMipMap.cpp
index 355c1ed..69c8466 100644
--- a/src/core/SkMipMap.cpp
+++ b/src/core/SkMipMap.cpp
@@ -11,6 +11,7 @@
#include "SkHalf.h"
#include "SkMathPriv.h"
#include "SkNx.h"
+#include "SkPM4fPriv.h"
#include "SkTypes.h"
//
@@ -41,6 +42,16 @@
#endif
};
+struct ColorTypeFilter_S32 {
+ typedef uint32_t Type;
+ static Sk4f Expand(uint32_t x) {
+ return Sk4f_fromS32(x);
+ }
+ static uint32_t Compact(const Sk4f& x) {
+ return Sk4f_toS32(x);
+ }
+};
+
struct ColorTypeFilter_565 {
typedef uint16_t Type;
static uint32_t Expand(uint16_t x) {
@@ -293,7 +304,16 @@
return sk_64_asS32(size);
}
-SkMipMap* SkMipMap::Build(const SkPixmap& src, SkDiscardableFactoryProc fact) {
+static bool treat_like_srgb(const SkImageInfo& info) {
+ if (info.colorSpace()) {
+ return SkColorSpace::k2Dot2Curve_GammaNamed == info.colorSpace()->gammaNamed();
+ } else {
+ return kSRGB_SkColorProfileType == info.profileType();
+ }
+}
+
+SkMipMap* SkMipMap::Build(const SkPixmap& src, SkSourceGammaTreatment treatment,
+ SkDiscardableFactoryProc fact) {
typedef void FilterProc(void*, const void* srcPtr, size_t srcRB, int count);
FilterProc* proc_1_2 = nullptr;
@@ -307,17 +327,31 @@
const SkColorType ct = src.colorType();
const SkAlphaType at = src.alphaType();
+ const bool srgbGamma = (SkSourceGammaTreatment::kRespect == treatment)
+ && treat_like_srgb(src.info());
+
switch (ct) {
case kRGBA_8888_SkColorType:
case kBGRA_8888_SkColorType:
- proc_1_2 = downsample_1_2<ColorTypeFilter_8888>;
- proc_1_3 = downsample_1_3<ColorTypeFilter_8888>;
- proc_2_1 = downsample_2_1<ColorTypeFilter_8888>;
- proc_2_2 = downsample_2_2<ColorTypeFilter_8888>;
- proc_2_3 = downsample_2_3<ColorTypeFilter_8888>;
- proc_3_1 = downsample_3_1<ColorTypeFilter_8888>;
- proc_3_2 = downsample_3_2<ColorTypeFilter_8888>;
- proc_3_3 = downsample_3_3<ColorTypeFilter_8888>;
+ if (srgbGamma) {
+ proc_1_2 = downsample_1_2<ColorTypeFilter_S32>;
+ proc_1_3 = downsample_1_3<ColorTypeFilter_S32>;
+ proc_2_1 = downsample_2_1<ColorTypeFilter_S32>;
+ proc_2_2 = downsample_2_2<ColorTypeFilter_S32>;
+ proc_2_3 = downsample_2_3<ColorTypeFilter_S32>;
+ proc_3_1 = downsample_3_1<ColorTypeFilter_S32>;
+ proc_3_2 = downsample_3_2<ColorTypeFilter_S32>;
+ proc_3_3 = downsample_3_3<ColorTypeFilter_S32>;
+ } else {
+ proc_1_2 = downsample_1_2<ColorTypeFilter_8888>;
+ proc_1_3 = downsample_1_3<ColorTypeFilter_8888>;
+ proc_2_1 = downsample_2_1<ColorTypeFilter_8888>;
+ proc_2_2 = downsample_2_2<ColorTypeFilter_8888>;
+ proc_2_3 = downsample_2_3<ColorTypeFilter_8888>;
+ proc_3_1 = downsample_3_1<ColorTypeFilter_8888>;
+ proc_3_2 = downsample_3_2<ColorTypeFilter_8888>;
+ proc_3_3 = downsample_3_3<ColorTypeFilter_8888>;
+ }
break;
case kRGB_565_SkColorType:
proc_1_2 = downsample_1_2<ColorTypeFilter_565>;
@@ -394,8 +428,10 @@
}
// init
+ mipmap->fCS = sk_ref_sp(src.info().colorSpace());
mipmap->fCount = countLevels;
mipmap->fLevels = (Level*)mipmap->writable_data();
+ SkASSERT(mipmap->fLevels);
Level* levels = mipmap->fLevels;
uint8_t* baseAddr = (uint8_t*)&levels[countLevels];
@@ -440,6 +476,9 @@
height = SkTMax(1, height >> 1);
rowBytes = SkToU32(SkColorTypeMinRowBytes(ct, width));
+ // We make the Info w/o any colorspace, since that storage is not under our control, and
+ // will not be deleted in a controlled fashion. When the caller is given the pixmap for
+ // a given level, we augment this pixmap with fCS (which we do manage).
new (&levels[i].fPixmap) SkPixmap(SkImageInfo::Make(width, height, ct, at), addr, rowBytes);
levels[i].fScale = SkSize::Make(SkIntToScalar(width) / src.width(),
SkIntToScalar(height) / src.height());
@@ -459,6 +498,7 @@
}
SkASSERT(addr == baseAddr + size);
+ SkASSERT(mipmap->fLevels);
return mipmap;
}
@@ -547,9 +587,7 @@
return false;
}
SkASSERT(L >= 0);
-// int rndLevel = SkScalarRoundToInt(L);
int level = SkScalarFloorToInt(L);
-// SkDebugf("mipmap scale=%g L=%g level=%d rndLevel=%d\n", scale, L, level, rndLevel);
SkASSERT(level >= 0);
if (level <= 0) {
@@ -561,13 +599,16 @@
}
if (levelPtr) {
*levelPtr = fLevels[level - 1];
+ // need to augment with our colorspace
+ levelPtr->fPixmap.setColorSpace(fCS);
}
return true;
}
// Helper which extracts a pixmap from the src bitmap
//
-SkMipMap* SkMipMap::Build(const SkBitmap& src, SkDiscardableFactoryProc fact) {
+SkMipMap* SkMipMap::Build(const SkBitmap& src, SkSourceGammaTreatment treatment,
+ SkDiscardableFactoryProc fact) {
SkAutoPixmapUnlock srcUnlocker;
if (!src.requestLock(&srcUnlocker)) {
return nullptr;
@@ -577,7 +618,7 @@
if (nullptr == srcPixmap.addr()) {
sk_throw();
}
- return Build(srcPixmap, fact);
+ return Build(srcPixmap, treatment, fact);
}
int SkMipMap::countLevels() const {
diff --git a/src/core/SkMipMap.h b/src/core/SkMipMap.h
index 7c798ef..0f31a9f 100644
--- a/src/core/SkMipMap.h
+++ b/src/core/SkMipMap.h
@@ -12,6 +12,7 @@
#include "SkPixmap.h"
#include "SkScalar.h"
#include "SkSize.h"
+#include "SkShader.h"
class SkBitmap;
class SkDiscardableMemory;
@@ -27,8 +28,13 @@
*/
class SkMipMap : public SkCachedData {
public:
- static SkMipMap* Build(const SkPixmap& src, SkDiscardableFactoryProc);
- static SkMipMap* Build(const SkBitmap& src, SkDiscardableFactoryProc);
+ static SkMipMap* Build(const SkPixmap& src, SkSourceGammaTreatment, SkDiscardableFactoryProc);
+ static SkMipMap* Build(const SkBitmap& src, SkSourceGammaTreatment, SkDiscardableFactoryProc);
+
+ static SkSourceGammaTreatment DeduceTreatment(const SkShader::ContextRec& rec) {
+ return (SkShader::ContextRec::kPMColor_DstType == rec.fPreferredDstType) ?
+ SkSourceGammaTreatment::kIgnore : SkSourceGammaTreatment::kRespect;
+ }
// Determines how many levels a SkMipMap will have without creating that mipmap.
// This does not include the base mipmap level that the user provided when
@@ -61,10 +67,10 @@
}
private:
- Level* fLevels;
- int fCount;
+ sk_sp<SkColorSpace> fCS;
+ Level* fLevels; // managed by the baseclass, may be null due to onDataChanged.
+ int fCount;
- // we take ownership of levels, and will free it with sk_free()
SkMipMap(void* malloc, size_t size) : INHERITED(malloc, size) {}
SkMipMap(size_t size, SkDiscardableMemory* dm) : INHERITED(size, dm) {}
diff --git a/src/core/SkPM4fPriv.h b/src/core/SkPM4fPriv.h
index bf5ff5a..12bf7d0 100644
--- a/src/core/SkPM4fPriv.h
+++ b/src/core/SkPM4fPriv.h
@@ -52,6 +52,48 @@
return sqrtf(x);
}
+static void assert_unit(float x) {
+ SkASSERT(x >= 0 && x <= 1);
+}
+
+static inline float exact_srgb_to_linear(float x) {
+ assert_unit(x);
+ float linear;
+ if (x <= 0.04045) {
+ linear = x / 12.92f;
+ } else {
+ linear = powf((x + 0.055f) / 1.055f, 2.4f);
+ }
+ assert_unit(linear);
+ return linear;
+}
+
+static inline float exact_linear_to_srgb(float x) {
+ assert_unit(x);
+ float srgb;
+ if (x <= 0.0031308f) {
+ srgb = x * 12.92f;
+ } else {
+ srgb = 1.055f * powf(x, 0.41666667f) - 0.055f;
+ }
+ assert_unit(srgb);
+ return srgb;
+}
+
+static inline Sk4f exact_srgb_to_linear(const Sk4f& x) {
+ Sk4f linear(exact_srgb_to_linear(x[0]),
+ exact_srgb_to_linear(x[1]),
+ exact_srgb_to_linear(x[2]), 1);
+ return set_alpha(linear, get_alpha(x));
+}
+
+static inline Sk4f exact_linear_to_srgb(const Sk4f& x) {
+ Sk4f srgb(exact_linear_to_srgb(x[0]),
+ exact_linear_to_srgb(x[1]),
+ exact_linear_to_srgb(x[2]), 1);
+ return set_alpha(srgb, get_alpha(x));
+}
+
///////////////////////////////////////////////////////////////////////////////////////////////////
static inline Sk4f Sk4f_fromL32(uint32_t src) {
@@ -77,4 +119,11 @@
return to_4b(linear_to_srgb(x4) * Sk4f(255) + Sk4f(0.5f));
}
+static inline Sk4f exact_Sk4f_fromS32(uint32_t src) {
+ return exact_srgb_to_linear(to_4f(src) * Sk4f(1.0f/255));
+}
+static inline uint32_t exact_Sk4f_toS32(const Sk4f& x4) {
+ return to_4b(exact_linear_to_srgb(x4) * Sk4f(255) + Sk4f(0.5f));
+}
+
#endif