/*
* Copyright 2012 Google Inc .
*
* Use of this source code is governed by a BSD - style license that can be
* found in the LICENSE file .
*/
#ifndef SkMaskGamma_DEFINED
#define SkMaskGamma_DEFINED
#include "include/core/SkColor.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkScalar.h"
#include "include/core/SkTypes.h"
#include "include/private/base/SkCPUTypes.h"
#include "include/private/base/SkNoncopyable.h"
#include "include/private/base/SkTo.h"
#include "src/core/SkColorData.h"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <memory>
/**
* SkColorSpaceLuminance is used to convert luminances to and from linear and
* perceptual color spaces .
*
* Luma is used to specify a linear luminance value [ 0 . 0 , 1 . 0 ] .
* Luminance is used to specify a luminance value in an arbitrary color space [ 0 . 0 , 1 . 0 ] .
*/
class SkColorSpaceLuminance : SkNoncopyable {
public:
virtual ~SkColorSpaceLuminance() { }
/** Converts a color component luminance in the color space to a linear luma. */
virtual SkScalar toLuma(SkScalar gamma, SkScalar luminance) const = 0 ;
/** Converts a linear luma to a color component luminance in the color space. */
virtual SkScalar fromLuma(SkScalar gamma, SkScalar luma) const = 0 ;
/** Converts a color to a luminance value. */
static U8CPU computeLuminance(SkScalar gamma, SkColor c) {
const SkColorSpaceLuminance& luminance = Fetch(gamma);
SkScalar r = luminance.toLuma(gamma, SkIntToScalar(SkColorGetR(c)) / 255 );
SkScalar g = luminance.toLuma(gamma, SkIntToScalar(SkColorGetG(c)) / 255 );
SkScalar b = luminance.toLuma(gamma, SkIntToScalar(SkColorGetB(c)) / 255 );
SkScalar luma = r * SK_LUM_COEFF_R +
g * SK_LUM_COEFF_G +
b * SK_LUM_COEFF_B;
SkASSERT(luma <= SK_Scalar1);
return SkScalarRoundToInt(luminance.fromLuma(gamma, luma) * 255 );
}
/** Retrieves the SkColorSpaceLuminance for the given gamma. */
static const SkColorSpaceLuminance& Fetch(SkScalar gamma);
};
///@{
/**
* Scales base < = 2 ^ N - 1 to 2 ^ 8 - 1
* @ param N [ 1 , 8 ] the number of bits used by base .
* @ param base the number to be scaled to [ 0 , 255 ] .
*/
template <U8CPU N> static inline U8CPU sk_t_scale255(U8CPU base) {
base <<= (8 - N);
U8CPU lum = base;
for (unsigned int i = N; i < 8 ; i += N) {
lum |= base >> i;
}
return lum;
}
template <> /*static*/ inline U8CPU sk_t_scale255<1>(U8CPU base) {
return base * 0 xFF;
}
template <> /*static*/ inline U8CPU sk_t_scale255<2>(U8CPU base) {
return base * 0 x55;
}
template <> /*static*/ inline U8CPU sk_t_scale255<4>(U8CPU base) {
return base * 0 x11;
}
template <> /*static*/ inline U8CPU sk_t_scale255<8>(U8CPU base) {
return base;
}
///@}
template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> class SkTMaskPreBlend;
void SkTMaskGamma_build_correcting_lut(uint8_t* table, U8CPU srcI, SkScalar contrast,
const SkColorSpaceLuminance& dstConvert, SkScalar dstGamma);
/**
* A regular mask contains linear alpha values . A gamma correcting mask
* contains non - linear alpha values in an attempt to create gamma correct blits
* in the presence of a gamma incorrect ( linear ) blend in the blitter .
*
* SkMaskGamma creates and maintains tables which convert linear alpha values
* to gamma correcting alpha values .
* @ param R The number of luminance bits to use [ 1 , 8 ] from the red channel .
* @ param G The number of luminance bits to use [ 1 , 8 ] from the green channel .
* @ param B The number of luminance bits to use [ 1 , 8 ] from the blue channel .
*/
template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> class SkTMaskGamma : public SkRefCnt {
public:
/** Creates a linear SkTMaskGamma. */
constexpr SkTMaskGamma() {}
/**
* Creates tables to convert linear alpha values to gamma correcting alpha
* values .
*
* @ param contrast A value in the range [ 0 . 0 , 1 . 0 ] which indicates the
* amount of artificial contrast to add .
* @ param device The color space of the target device .
*/
SkTMaskGamma(SkScalar contrast, SkScalar deviceGamma)
: fGammaTables(std::make_unique<uint8_t[]>(kTableNumElements))
{
const SkColorSpaceLuminance& deviceConvert = SkColorSpaceLuminance::Fetch(deviceGamma);
for (U8CPU i = 0 ; i < kNumTables; ++i) {
U8CPU lum = sk_t_scale255<kMaxLumBits>(i);
SkTMaskGamma_build_correcting_lut(&fGammaTables[i * kTableWidth], lum, contrast,
deviceConvert, deviceGamma);
}
}
/** Given a color, returns the closest canonical color. */
static SkColor CanonicalColor(SkColor color) {
return SkColorSetRGB(
sk_t_scale255<R_LUM_BITS>(SkColorGetR(color) >> (8 - R_LUM_BITS)),
sk_t_scale255<G_LUM_BITS>(SkColorGetG(color) >> (8 - G_LUM_BITS)),
sk_t_scale255<B_LUM_BITS>(SkColorGetB(color) >> (8 - B_LUM_BITS)));
}
/** The type of the mask pre-blend which will be returned from preBlend(SkColor). */
typedef SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS> PreBlend;
/**
* Provides access to the tables appropriate for converting linear alpha
* values into gamma correcting alpha values when drawing the given color
* through the mask . The destination color will be approximated .
*/
PreBlend preBlend(SkColor color) const ;
/**
* Get dimensions for the full table set , so it can be allocated as a block . Linear
* tables should report the full table size .
*/
void getGammaTableDimensions(int * tableWidth, int * numTables) const {
*tableWidth = kTableWidth;
*numTables = kNumTables;
}
/**
* Returns the size for the full table set in bytes , so it can be allocated as a block .
* Linear tables should report the full table size .
*/
constexpr size_t getGammaTableSizeInBytes() const {
return kTableNumElements * sizeof (uint8_t);
}
/**
* Provides direct access to the full table set , so it can be uploaded
* into a texture or analyzed in other ways .
* Returns nullptr if fGammaTables hasn ' t been initialized .
*/
const uint8_t* getGammaTables() const {
return fGammaTables.get();
}
private :
static constexpr int kMaxLumBits = std::max({B_LUM_BITS, R_LUM_BITS, G_LUM_BITS});
static constexpr size_t kNumTables = 1 << kMaxLumBits;
static constexpr size_t kTableWidth = 256 ;
static constexpr size_t kTableNumElements = kNumTables * kTableWidth;
constexpr bool isLinear() const {
return fGammaTables == nullptr;
}
/**
* fGammaTables is a flattened 2 - D array . Accessing rows requires accounting
* for the width dimension ( via kTableWidth ) .
*/
std::unique_ptr<uint8_t[]> fGammaTables;
using INHERITED = SkRefCnt;
};
/**
* SkTMaskPreBlend is a tear - off of SkTMaskGamma . It provides the tables to
* convert a linear alpha value for a given channel to a gamma correcting alpha
* value for that channel . This class is immutable .
*
* If fR , fG , or fB is nullptr , all of them will be . This indicates that no mask
* pre blend should be applied . SkTMaskPreBlend : : isApplicable ( ) is provided as
* a convenience function to test for the absence of this case .
*/
template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> class SkTMaskPreBlend {
private :
SkTMaskPreBlend(sk_sp<const SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>> parent,
const uint8_t* r, const uint8_t* g, const uint8_t* b)
: fParent(std::move(parent)), fR(r), fG(g), fB(b) { }
sk_sp<const SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>> fParent;
friend class SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>;
public:
/** Creates a non applicable SkTMaskPreBlend. */
SkTMaskPreBlend() : fParent(), fR(nullptr), fG(nullptr), fB(nullptr) { }
/**
* This copy contructor exists for correctness , but should never be called
* when return value optimization is enabled .
*/
SkTMaskPreBlend(const SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>& that)
: fParent(that.fParent), fR(that.fR), fG(that.fG), fB(that.fB) { }
~SkTMaskPreBlend() { }
/** True if this PreBlend should be applied. When false, fR, fG, and fB are nullptr. */
bool isApplicable() const { return SkToBool(this->fG); }
const uint8_t* fR;
const uint8_t* fG;
const uint8_t* fB;
};
template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS>
SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>
SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>::preBlend(SkColor color) const {
if (isLinear()) {
return SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>();
}
constexpr size_t lum_shift = 8 - kMaxLumBits;
const size_t r_index = (SkColorGetR(color) >> lum_shift) * kTableWidth;
const size_t g_index = (SkColorGetG(color) >> lum_shift) * kTableWidth;
const size_t b_index = (SkColorGetB(color) >> lum_shift) * kTableWidth;
SkASSERT(r_index < kTableNumElements &&
g_index < kTableNumElements &&
b_index < kTableNumElements);
return SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>(sk_ref_sp(this),
&fGammaTables[r_index],
&fGammaTables[g_index],
&fGammaTables[b_index]);
}
///@{
/**
* If APPLY_LUT is false , returns component unchanged .
* If APPLY_LUT is true , returns lut [ component ] .
* @ param APPLY_LUT whether or not the look - up table should be applied to component .
* @ component the initial component .
* @ lut a look - up table which transforms the component .
*/
template <bool APPLY_LUT> static inline U8CPU sk_apply_lut_if(U8CPU component, const uint8_t*) {
return component;
}
template <> /*static*/ inline U8CPU sk_apply_lut_if<true>(U8CPU component, const uint8_t* lut) {
return lut[component];
}
///@}
#endif
Messung V0.5 in Prozent C=100 H=99 G=99
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-08-26)
¤
*© Formatika GbR, Deutschland