Bug 1958191 - Update Skia to m136. r=aosmond
This looks like a fairly mild update with some of our local patches being upstreamed and otherwise reasonably small amounts of code churn. Differential Revision: https://phabricator.services.mozilla.com/D244290
This commit is contained in:
@@ -219,6 +219,12 @@ def generate_separated_sources(platform_sources):
|
||||
|
||||
separated = defaultdict(set, {
|
||||
'common': {
|
||||
'skia/src/codec/SkCodec.cpp',
|
||||
'skia/src/codec/SkCodecImageGenerator.cpp',
|
||||
'skia/src/codec/SkColorPalette.cpp',
|
||||
'skia/src/codec/SkImageGenerator_FromEncoded.cpp',
|
||||
'skia/src/codec/SkPixmapUtils.cpp',
|
||||
'skia/src/codec/SkSampler.cpp',
|
||||
'skia/src/effects/imagefilters/SkBlendImageFilter.cpp',
|
||||
'skia/src/effects/imagefilters/SkBlurImageFilter.cpp',
|
||||
'skia/src/effects/imagefilters/SkComposeImageFilter.cpp',
|
||||
|
||||
@@ -49,6 +49,12 @@ UNIFIED_SOURCES += [
|
||||
'skia/src/base/SkTSearch.cpp',
|
||||
'skia/src/base/SkUTF.cpp',
|
||||
'skia/src/base/SkUtils.cpp',
|
||||
'skia/src/codec/SkCodec.cpp',
|
||||
'skia/src/codec/SkCodecImageGenerator.cpp',
|
||||
'skia/src/codec/SkColorPalette.cpp',
|
||||
'skia/src/codec/SkImageGenerator_FromEncoded.cpp',
|
||||
'skia/src/codec/SkPixmapUtils.cpp',
|
||||
'skia/src/codec/SkSampler.cpp',
|
||||
'skia/src/core/SkAAClip.cpp',
|
||||
'skia/src/core/SkAlphaRuns.cpp',
|
||||
'skia/src/core/SkAnalyticEdge.cpp',
|
||||
@@ -127,6 +133,7 @@ UNIFIED_SOURCES += [
|
||||
'skia/src/core/SkMaskBlurFilter.cpp',
|
||||
'skia/src/core/SkMaskCache.cpp',
|
||||
'skia/src/core/SkMaskFilter.cpp',
|
||||
'skia/src/core/SkMaskFilterBase.cpp',
|
||||
'skia/src/core/SkMaskGamma.cpp',
|
||||
'skia/src/core/SkMasks.cpp',
|
||||
'skia/src/core/SkMatrixInvert.cpp',
|
||||
|
||||
@@ -412,6 +412,9 @@ public:
|
||||
* If a scanline decode is in progress, scanline mode will end, requiring the client to call
|
||||
* startScanlineDecode() in order to return to decoding scanlines.
|
||||
*
|
||||
* For certain codecs, reading into a smaller bitmap than the original dimensions may not
|
||||
* produce correct results (e.g. animated webp).
|
||||
*
|
||||
* @return Result kSuccess, or another value explaining the type of failure.
|
||||
*/
|
||||
Result getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, const Options*);
|
||||
@@ -781,12 +784,46 @@ public:
|
||||
*
|
||||
* As such, future decoding calls may require a rewind.
|
||||
*
|
||||
* For still (non-animated) image codecs, this will return 0.
|
||||
* `getRepetitionCount` will return `0` in two cases:
|
||||
* 1. Still (non-animated) images.
|
||||
* 2. Animated images that only play the animation once (i.e. that don't
|
||||
* repeat the animation)
|
||||
* `isAnimated` can be used to disambiguate between these two cases.
|
||||
*/
|
||||
int getRepetitionCount() {
|
||||
return this->onGetRepetitionCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* `isAnimated` returns whether the full input is expected to contain an
|
||||
* animated image (i.e. more than 1 image frame). This can be used to
|
||||
* disambiguate the meaning of `getRepetitionCount` returning `0` (see
|
||||
* `getRepetitionCount`'s doc comment for more details).
|
||||
*
|
||||
* Note that in some codecs `getFrameCount()` only returns the number of
|
||||
* frames for which all the metadata has been already successfully decoded.
|
||||
* Therefore for a partial input `isAnimated()` may return "yes", even
|
||||
* though `getFrameCount()` may temporarily return `1` until more of the
|
||||
* input is available.
|
||||
*
|
||||
* When handling partial input, some codecs may not know until later (e.g.
|
||||
* until encountering additional image frames) whether the given image has
|
||||
* more than one frame. Such codecs may initially return
|
||||
* `IsAnimated::kUnknown` and only later give a definitive "yes" or "no"
|
||||
* answer. GIF format is one example where this may happen.
|
||||
*
|
||||
* Other codecs may be able to decode the information from the metadata
|
||||
* present before the first image frame. Such codecs should be able to give
|
||||
* a definitive "yes" or "no" answer as soon as they are constructed. PNG
|
||||
* format is one example where this happens.
|
||||
*/
|
||||
enum class IsAnimated {
|
||||
kYes,
|
||||
kNo,
|
||||
kUnknown,
|
||||
};
|
||||
IsAnimated isAnimated() { return this->onIsAnimated(); }
|
||||
|
||||
// Register a decoder at runtime by passing two function pointers:
|
||||
// - peek() to return true if the span of bytes appears to be your encoded format;
|
||||
// - make() to attempt to create an SkCodec from the given stream.
|
||||
@@ -812,6 +849,7 @@ protected:
|
||||
}
|
||||
|
||||
virtual bool onGetGainmapCodec(SkGainmapInfo*, std::unique_ptr<SkCodec>*) { return false; }
|
||||
virtual bool onGetGainmapInfo(SkGainmapInfo*) { return false; }
|
||||
|
||||
// TODO(issues.skia.org/363544350): This API only works for JPEG images. Remove this API once
|
||||
// it is no longer used.
|
||||
@@ -933,6 +971,10 @@ protected:
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual IsAnimated onIsAnimated() {
|
||||
return IsAnimated::kNo;
|
||||
}
|
||||
|
||||
private:
|
||||
const SkEncodedInfo fEncodedInfo;
|
||||
XformFormat fSrcXformFormat;
|
||||
@@ -1053,8 +1095,9 @@ private:
|
||||
friend class PNGCodecGM; // for fillIncompleteImage
|
||||
friend class SkSampledCodec;
|
||||
friend class SkIcoCodec;
|
||||
friend class SkAndroidCodec; // for fEncodedInfo
|
||||
friend class SkPDFBitmap; // for fEncodedInfo
|
||||
friend class SkPngCodec; // for onGetGainmapCodec
|
||||
friend class SkAndroidCodec; // for handleFrameIndex
|
||||
friend class SkCodecPriv; // for fEncodedInfo
|
||||
};
|
||||
|
||||
namespace SkCodecs {
|
||||
|
||||
@@ -74,15 +74,11 @@
|
||||
*/
|
||||
//#define SK_R32_SHIFT 16
|
||||
|
||||
/* Determines whether to build code that supports the Ganesh GPU backend. Some classes
|
||||
that are not GPU-specific, such as SkShader subclasses, have optional code
|
||||
that is used allows them to interact with this GPU backend. If you'd like to
|
||||
include this code, include -DSK_GANESH in your cflags or uncomment below.
|
||||
Defaults to not set (No Ganesh GPU backend).
|
||||
This define affects the ABI of Skia, so make sure it matches the client which uses
|
||||
the compiled version of Skia.
|
||||
/* This controls how much space should be pre-allocated in an SkCanvas object
|
||||
to store the SkMatrix and clip via calls to SkCanvas::save() (and balanced with
|
||||
SkCanvas::restore()).
|
||||
*/
|
||||
//#define SK_GANESH
|
||||
//#define SK_CANVAS_SAVE_RESTORE_PREALLOC_COUNT 32
|
||||
|
||||
/* Skia makes use of histogram logging macros to trace the frequency of
|
||||
events. By default, Skia provides no-op versions of these macros.
|
||||
@@ -93,8 +89,14 @@
|
||||
//#define SK_HISTOGRAM_ENUMERATION(name, sampleEnum, enumSize)
|
||||
//#define SK_HISTOGRAM_EXACT_LINEAR(name, sample, valueMax)
|
||||
//#define SK_HISTOGRAM_MEMORY_KB(name, sample)
|
||||
//#define SK_HISTOGRAM_CUSTOM_COUNTS(name, sample, countMin, countMax, bucketCount)
|
||||
//#define SK_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(name, sampleUSec, minUSec, maxUSec, bucketCount)
|
||||
|
||||
/*
|
||||
* Skia can provide extensive logging of Graphite Pipeline lifetimes.
|
||||
*/
|
||||
//#define SK_PIPELINE_LIFETIME_LOGGING
|
||||
|
||||
// To use smaller but slower mipmap builder
|
||||
//#define SK_USE_DRAWING_MIPMAP_DOWNSAMPLER
|
||||
|
||||
@@ -141,6 +143,8 @@
|
||||
|
||||
#define SK_USE_FREETYPE_EMBOLDEN
|
||||
|
||||
#define SK_DISABLE_DIRECTWRITE_COLRv1 1
|
||||
|
||||
#ifndef MOZ_IMPLICIT
|
||||
# ifdef MOZ_CLANG_PLUGIN
|
||||
# define MOZ_IMPLICIT __attribute__((annotate("moz_implicit")))
|
||||
@@ -151,6 +155,4 @@
|
||||
|
||||
#define SK_DISABLE_LEGACY_IMAGE_READBUFFER
|
||||
|
||||
#define SK_DISABLE_DIRECTWRITE_COLRv1 1
|
||||
|
||||
#endif
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "include/private/base/SkDeque.h"
|
||||
#include "include/private/base/SkTArray.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
@@ -53,6 +54,7 @@ class GrRecordingContext;
|
||||
|
||||
class SkBitmap;
|
||||
class SkBlender;
|
||||
class SkBlurMaskFilterImpl;
|
||||
class SkColorSpace;
|
||||
class SkData;
|
||||
class SkDevice;
|
||||
@@ -2502,15 +2504,18 @@ private:
|
||||
void reset(SkDevice* device);
|
||||
};
|
||||
|
||||
// the first N recs that can fit here mean we won't call malloc
|
||||
static constexpr int kMCRecSize = 96; // most recent measurement
|
||||
#if defined(SK_CANVAS_SAVE_RESTORE_PREALLOC_COUNT)
|
||||
static constexpr int kMCRecCount = SK_CANVAS_SAVE_RESTORE_PREALLOC_COUNT;
|
||||
#else
|
||||
static constexpr int kMCRecCount = 32; // common depth for save/restores
|
||||
#endif
|
||||
|
||||
intptr_t fMCRecStorage[kMCRecSize * kMCRecCount / sizeof(intptr_t)];
|
||||
// This stack allocation of memory will be used to house the first kMCRecCount
|
||||
// layers without need to call malloc.
|
||||
alignas(MCRec) std::byte fMCRecStorage[sizeof(MCRec) * kMCRecCount];
|
||||
|
||||
SkDeque fMCStack;
|
||||
// points to top of stack
|
||||
MCRec* fMCRec;
|
||||
SkDeque fMCStack; // uses the stack memory
|
||||
MCRec* fMCRec; // points to top of stack for convenience
|
||||
|
||||
// Installed via init()
|
||||
sk_sp<SkDevice> fRootDevice;
|
||||
@@ -2675,11 +2680,14 @@ private:
|
||||
// into the canvas' global space.
|
||||
SkRect computeDeviceClipBounds(bool outsetForAA=true) const;
|
||||
|
||||
// Attempt to draw a rrect with an analytic blur. If the paint does not contain a blur, or the
|
||||
// geometry can't be drawn with an analytic blur by the device, a layer is returned for a
|
||||
// regular draw. If the draw succeeds or predrawNotify fails, nullopt is returned indicating
|
||||
// that nothing further should be drawn.
|
||||
// Returns the paint's mask filter if it can be used to draw an rrect with an analytic blur, and
|
||||
// returns null otherwise.
|
||||
const SkBlurMaskFilterImpl* canAttemptBlurredRRectDraw(const SkPaint&) const;
|
||||
|
||||
// Attempt to draw a rrect with an analytic blur. If the draw succeeds or predrawNotify fails,
|
||||
// nullopt is returned indicating that nothing further should be drawn.
|
||||
std::optional<AutoLayerForImageFilter> attemptBlurredRRectDraw(const SkRRect&,
|
||||
const SkBlurMaskFilterImpl*,
|
||||
const SkPaint&,
|
||||
SkEnumBitMask<PredrawFlags>);
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "include/core/SkScalar.h"
|
||||
#include "include/core/SkTypes.h"
|
||||
#include "include/private/base/SkCPUTypes.h"
|
||||
#include "include/private/base/SkTPin.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
@@ -415,6 +416,15 @@ struct SkRGBA4f {
|
||||
SkRGBA4f makeOpaque() const {
|
||||
return { fR, fG, fB, 1.0f };
|
||||
}
|
||||
|
||||
/**
|
||||
Returns a copy of the SkRGBA4f but with the alpha component pinned to [0, 1].
|
||||
|
||||
@return color with pinned alpha
|
||||
*/
|
||||
SkRGBA4f pinAlpha() const {
|
||||
return { fR, fG, fB, SkTPin(fA, 0.f, 1.f) };
|
||||
}
|
||||
};
|
||||
|
||||
/** \struct SkColor4f
|
||||
|
||||
@@ -39,6 +39,85 @@ struct SK_API SkColorSpacePrimaries {
|
||||
bool toXYZD50(skcms_Matrix3x3* toXYZD50) const;
|
||||
};
|
||||
|
||||
namespace SkNamedPrimaries {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Color primaries defined by ITU-T H.273, table 2. Names are given by the first
|
||||
// specification referenced in the value's row.
|
||||
|
||||
// Rec. ITU-R BT.709-6, value 1.
|
||||
static constexpr SkColorSpacePrimaries kRec709 = {
|
||||
0.64f, 0.33f, 0.3f, 0.6f, 0.15f, 0.06f, 0.3127f, 0.329f};
|
||||
|
||||
// Rec. ITU-R BT.470-6 System M (historical), value 4.
|
||||
static constexpr SkColorSpacePrimaries kRec470SystemM = {
|
||||
0.67f, 0.33f, 0.21f, 0.71f, 0.14f, 0.08f, 0.31f, 0.316f};
|
||||
|
||||
// Rec. ITU-R BT.470-6 System B, G (historical), value 5.
|
||||
static constexpr SkColorSpacePrimaries kRec470SystemBG = {
|
||||
0.64f, 0.33f, 0.29f, 0.60f, 0.15f, 0.06f, 0.3127f, 0.3290f};
|
||||
|
||||
// Rec. ITU-R BT.601-7 525, value 6.
|
||||
static constexpr SkColorSpacePrimaries kRec601 = {
|
||||
0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f, 0.3127f, 0.3290f};
|
||||
|
||||
// SMPTE ST 240, value 7 (functionally the same as value 6).
|
||||
static constexpr SkColorSpacePrimaries kSMPTE_ST_240 = kRec601;
|
||||
|
||||
// Generic film (colour filters using Illuminant C), value 8.
|
||||
static constexpr SkColorSpacePrimaries kGenericFilm = {
|
||||
0.681f, 0.319f, 0.243f, 0.692f, 0.145f, 0.049f, 0.310f, 0.316f};
|
||||
|
||||
// Rec. ITU-R BT.2020-2, value 9.
|
||||
static constexpr SkColorSpacePrimaries kRec2020{
|
||||
0.708f, 0.292f, 0.170f, 0.797f, 0.131f, 0.046f, 0.3127f, 0.3290f};
|
||||
|
||||
// SMPTE ST 428-1, value 10.
|
||||
static constexpr SkColorSpacePrimaries kSMPTE_ST_428_1 = {
|
||||
1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f / 3.f, 1.f / 3.f};
|
||||
|
||||
// SMPTE RP 431-2, value 11.
|
||||
static constexpr SkColorSpacePrimaries kSMPTE_RP_431_2 = {
|
||||
0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f, 0.314f, 0.351f};
|
||||
|
||||
// SMPTE EG 432-1, value 12.
|
||||
static constexpr SkColorSpacePrimaries kSMPTE_EG_432_1 = {
|
||||
0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f, 0.3127f, 0.3290f};
|
||||
|
||||
// No corresponding industry specification identified, value 22.
|
||||
// This is sometimes referred to as EBU 3213-E, but that document doesn't
|
||||
// specify these values.
|
||||
static constexpr SkColorSpacePrimaries kITU_T_H273_Value22 = {
|
||||
0.630f, 0.340f, 0.295f, 0.605f, 0.155f, 0.077f, 0.3127f, 0.3290f};
|
||||
|
||||
// Mapping between names of color primaries and the number of the corresponding
|
||||
// row in ITU-T H.273, table 2. As above, the constants are named based on the
|
||||
// first specification referenced in the value's row.
|
||||
enum class CicpId : uint8_t {
|
||||
// Value 0 is reserved.
|
||||
kRec709 = 1,
|
||||
// Value 2 is unspecified.
|
||||
// Value 3 is reserved.
|
||||
kRec470SystemM = 4,
|
||||
kRec470SystemBG = 5,
|
||||
kRec601 = 6,
|
||||
kSMPTE_ST_240 = 7,
|
||||
kGenericFilm = 8,
|
||||
kRec2020 = 9,
|
||||
kSMPTE_ST_428_1 = 10,
|
||||
kSMPTE_RP_431_2 = 11,
|
||||
kSMPTE_EG_432_1 = 12,
|
||||
// Values 13-21 are reserved.
|
||||
kITU_T_H273_Value22 = 22,
|
||||
// Values 23-255 are reserved.
|
||||
};
|
||||
|
||||
// https://www.w3.org/TR/css-color-4/#predefined-prophoto-rgb
|
||||
static constexpr SkColorSpacePrimaries kProPhotoRGB = {
|
||||
0.7347f, 0.2653f, 0.1596f, 0.8404f, 0.0366f, 0.0001f, 0.34567f, 0.35850f};
|
||||
|
||||
} // namespace SkNamedPrimaries
|
||||
|
||||
namespace SkNamedTransferFn {
|
||||
|
||||
// Like SkNamedGamut::kSRGB, keeping this bitwise exactly the same as skcms makes things fastest.
|
||||
@@ -48,18 +127,98 @@ static constexpr skcms_TransferFunction kSRGB =
|
||||
static constexpr skcms_TransferFunction k2Dot2 =
|
||||
{ 2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
|
||||
static constexpr skcms_TransferFunction kRec2020 = {
|
||||
2.22222f, 0.909672f, 0.0903276f, 0.222222f, 0.0812429f, 0, 0};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Color primaries defined by ITU-T H.273, table 3. Names are given by the first
|
||||
// specification referenced in the value's row.
|
||||
|
||||
// Rec. ITU-R BT.709-6, value 1.
|
||||
static constexpr skcms_TransferFunction kRec709 = {2.222222222222f,
|
||||
0.909672415686f,
|
||||
0.090327584314f,
|
||||
0.222222222222f,
|
||||
0.081242858299f,
|
||||
0.f,
|
||||
0.f};
|
||||
|
||||
// Rec. ITU-R BT.470-6 System M (historical) assumed display gamma 2.2, value 4.
|
||||
static constexpr skcms_TransferFunction kRec470SystemM = {2.2f, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f};
|
||||
|
||||
// Rec. ITU-R BT.470-6 System B, G (historical) assumed display gamma 2.8,
|
||||
// value 5.
|
||||
static constexpr skcms_TransferFunction kRec470SystemBG = {2.8f, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f};
|
||||
|
||||
// Rec. ITU-R BT.601-7, same as kRec709, value 6.
|
||||
static constexpr skcms_TransferFunction kRec601 = kRec709;
|
||||
|
||||
// SMPTE ST 240, value 7.
|
||||
static constexpr skcms_TransferFunction kSMPTE_ST_240 = {
|
||||
2.222222222222f, 0.899626676224f, 0.100373323776f, 0.25f, 0.091286342118f, 0.f, 0.f};
|
||||
|
||||
// Linear, value 8
|
||||
static constexpr skcms_TransferFunction kLinear =
|
||||
{ 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
|
||||
static constexpr skcms_TransferFunction kRec2020 =
|
||||
{2.22222f, 0.909672f, 0.0903276f, 0.222222f, 0.0812429f, 0, 0};
|
||||
// IEC 61966-2-4, value 11, same as kRec709 (but is explicitly extended).
|
||||
static constexpr skcms_TransferFunction kIEC61966_2_4 = kRec709;
|
||||
|
||||
// IEC 61966-2-1 sRGB, value 13.
|
||||
static constexpr skcms_TransferFunction kIEC61966_2_1 = kSRGB;
|
||||
|
||||
// Rec. ITU-R BT.2020-2 (10-bit system), value 14.
|
||||
static constexpr skcms_TransferFunction kRec2020_10bit = kRec709;
|
||||
|
||||
// Rec. ITU-R BT.2020-2 (12-bit system), value 15.
|
||||
static constexpr skcms_TransferFunction kRec2020_12bit = kRec709;
|
||||
|
||||
// Rec. ITU-R BT.2100-2 perceptual quantization (PQ) system, value 16.
|
||||
static constexpr skcms_TransferFunction kPQ =
|
||||
{-2.0f, -107/128.0f, 1.0f, 32/2523.0f, 2413/128.0f, -2392/128.0f, 8192/1305.0f };
|
||||
|
||||
// SMPTE ST 428-1, value 17.
|
||||
static constexpr skcms_TransferFunction kSMPTE_ST_428_1 = {
|
||||
2.6f, 1.034080527699f, 0.f, 0.f, 0.f, 0.f, 0.f};
|
||||
|
||||
// Rec. ITU-R BT.2100-2 hybrid log-gamma (HLG) system, value 18.
|
||||
static constexpr skcms_TransferFunction kHLG =
|
||||
{-3.0f, 2.0f, 2.0f, 1/0.17883277f, 0.28466892f, 0.55991073f, 0.0f };
|
||||
|
||||
// Mapping between transfer function names and the number of the corresponding
|
||||
// row in ITU-T H.273, table 3. As above, the constants are named based on the
|
||||
// first specification referenced in the value's row.
|
||||
enum class CicpId : uint8_t {
|
||||
// Value 0 is reserved.
|
||||
kRec709 = 1,
|
||||
// Value 2 is unspecified.
|
||||
// Value 3 is reserved.
|
||||
kRec470SystemM = 4,
|
||||
kRec470SystemBG = 5,
|
||||
kRec601 = 6,
|
||||
kSMPTE_ST_240 = 7,
|
||||
kLinear = 8,
|
||||
// Value 9 is not supported by `SkColorSpace::MakeCICP`.
|
||||
// Value 10 is not supported by `SkColorSpace::MakeCICP`.
|
||||
kIEC61966_2_4 = 11,
|
||||
// Value 12 is not supported by `SkColorSpace::MakeCICP`.
|
||||
kIEC61966_2_1 = 13,
|
||||
kSRGB = kIEC61966_2_1,
|
||||
kRec2020_10bit = 14,
|
||||
kRec2020_12bit = 15,
|
||||
kPQ = 16,
|
||||
kSMPTE_ST_428_1 = 17,
|
||||
kHLG = 18,
|
||||
// Values 19-255 are reserved.
|
||||
};
|
||||
|
||||
// https://w3.org/TR/css-color-4/#valdef-color-prophoto-rgb
|
||||
// "The transfer curve is a gamma function with a value of 1/1.8"
|
||||
static constexpr skcms_TransferFunction kProPhotoRGB = {1.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
|
||||
|
||||
// https://www.w3.org/TR/css-color-4/#predefined-a98-rgb
|
||||
static constexpr skcms_TransferFunction kA98RGB = k2Dot2;
|
||||
|
||||
} // namespace SkNamedTransferFn
|
||||
|
||||
namespace SkNamedGamut {
|
||||
@@ -122,6 +281,29 @@ public:
|
||||
static sk_sp<SkColorSpace> MakeRGB(const skcms_TransferFunction& transferFn,
|
||||
const skcms_Matrix3x3& toXYZ);
|
||||
|
||||
/**
|
||||
* Create an SkColorSpace from code points specified in Rec. ITU-T H.273.
|
||||
* Null will be returned for invalid or unsupported combination of code
|
||||
* points.
|
||||
*
|
||||
* Parameters:
|
||||
*
|
||||
* - `color_primaries` identifies an entry in Rec. ITU-T H.273, Table 2.
|
||||
* - `transfer_characteristics` identifies an entry in Rec. ITU-T H.273, Table 3.
|
||||
*
|
||||
* `SkColorSpace` (and the underlying `skcms_ICCProfile`) only supports RGB
|
||||
* color spaces and therefore this function does not take a
|
||||
* `matrix_coefficients` parameter - the caller is expected to verify that
|
||||
* `matrix_coefficients` is `0`.
|
||||
*
|
||||
* Narrow range images are extremely rare - see
|
||||
* https://github.com/w3c/png/issues/312#issuecomment-2327349614. Therefore
|
||||
* this function doesn't take a `video_full_range_flag` - the caller is
|
||||
* expected to verify that it is `1` (indicating a full range image).
|
||||
*/
|
||||
static sk_sp<SkColorSpace> MakeCICP(SkNamedPrimaries::CicpId color_primaries,
|
||||
SkNamedTransferFn::CicpId transfer_characteristics);
|
||||
|
||||
/**
|
||||
* Create an SkColorSpace from a parsed (skcms) ICC profile.
|
||||
*/
|
||||
|
||||
@@ -15,43 +15,125 @@
|
||||
|
||||
kN32_SkColorType selects the native 32-bit ARGB format for the current configuration. This can
|
||||
lead to inconsistent results across platforms, so use with caution.
|
||||
|
||||
By default, Skia operates with the assumption of a little-Endian system. The names of each
|
||||
SkColorType implicitly define the channel ordering and size in memory. Due to historical reasons
|
||||
the names do not follow 100% identical convention, but are typically labeled from least
|
||||
significant to most significant. To help clarify when the actual data layout differs from the
|
||||
default convention, every SkColorType's comment includes a bit-labeled description of a pixel
|
||||
in that color type on a LE system.
|
||||
|
||||
Unless specified otherwise, a channel's value is treated as an unsigned integer with a range of
|
||||
of [0, 2^N-1] and this is mapped uniformly to a floating point value of [0.0, 1.0]. Some color
|
||||
types instead store data directly in 32-bit floating point (assumed to be IEEE), or in 16-bit
|
||||
"half" floating point values. A half float, or F16/float16, is interpreted as FP 1-5-10 or
|
||||
Bits: [sign:15 exp:14..10 man:9..0]
|
||||
*/
|
||||
enum SkColorType : int {
|
||||
kUnknown_SkColorType, //!< uninitialized
|
||||
kAlpha_8_SkColorType, //!< pixel with alpha in 8-bit byte
|
||||
kRGB_565_SkColorType, //!< pixel with 5 bits red, 6 bits green, 5 bits blue, in 16-bit word
|
||||
kARGB_4444_SkColorType, //!< pixel with 4 bits for alpha, red, green, blue; in 16-bit word
|
||||
kRGBA_8888_SkColorType, //!< pixel with 8 bits for red, green, blue, alpha; in 32-bit word
|
||||
kRGB_888x_SkColorType, //!< pixel with 8 bits each for red, green, blue; in 32-bit word
|
||||
kBGRA_8888_SkColorType, //!< pixel with 8 bits for blue, green, red, alpha; in 32-bit word
|
||||
kRGBA_1010102_SkColorType, //!< 10 bits for red, green, blue; 2 bits for alpha; in 32-bit word
|
||||
kBGRA_1010102_SkColorType, //!< 10 bits for blue, green, red; 2 bits for alpha; in 32-bit word
|
||||
kRGB_101010x_SkColorType, //!< pixel with 10 bits each for red, green, blue; in 32-bit word
|
||||
kBGR_101010x_SkColorType, //!< pixel with 10 bits each for blue, green, red; in 32-bit word
|
||||
kBGR_101010x_XR_SkColorType, //!< pixel with 10 bits each for blue, green, red; in 32-bit word, extended range
|
||||
kBGRA_10101010_XR_SkColorType, //!< pixel with 10 bits each for blue, green, red, alpha; in 64-bit word, extended range
|
||||
kRGBA_10x6_SkColorType, //!< pixel with 10 used bits (most significant) followed by 6 unused
|
||||
// bits for red, green, blue, alpha; in 64-bit word
|
||||
kGray_8_SkColorType, //!< pixel with grayscale level in 8-bit byte
|
||||
kRGBA_F16Norm_SkColorType, //!< pixel with half floats in [0,1] for red, green, blue, alpha;
|
||||
// in 64-bit word
|
||||
kRGBA_F16_SkColorType, //!< pixel with half floats for red, green, blue, alpha;
|
||||
// in 64-bit word
|
||||
kRGB_F16F16F16x_SkColorType, //!< pixel with half floats for red, green, blue; in 64-bit word
|
||||
kRGBA_F32_SkColorType, //!< pixel using C float for red, green, blue, alpha; in 128-bit word
|
||||
// Unknown or unrepresentable as an SkColorType.
|
||||
kUnknown_SkColorType,
|
||||
// Single channel data (8-bit) interpreted as an alpha value. RGB are 0.
|
||||
// Bits: [A:7..0]
|
||||
kAlpha_8_SkColorType,
|
||||
// Three channel BGR data (5 bits red, 6 bits green, 5 bits blue) packed into a LE 16-bit word.
|
||||
// NOTE: The name of this enum value does not match the standard convention for SkColorType.
|
||||
// Bits: [R:15..11 G:10..5 B:4..0]
|
||||
kRGB_565_SkColorType,
|
||||
// Four channel ABGR data (4 bits per channel) packed into a LE 16-bit word.
|
||||
// NOTE: The name of this enum value does not match the standard convention for SkColorType.
|
||||
// Bits: [R:15..12 G:11..8 B:7..4 A:3..0]
|
||||
kARGB_4444_SkColorType,
|
||||
// Four channel RGBA data (8 bits per channel) packed into a LE 32-bit word.
|
||||
// Bits: [A:31..24 B:23..16 G:15..8 R:7..0]
|
||||
kRGBA_8888_SkColorType,
|
||||
// Three channel RGB data (8 bits per channel) packed into a LE 32-bit word. The remaining bits
|
||||
// are ignored and alpha is forced to opaque.
|
||||
// Bits: [x:31..24 B:23..16 G:15..8 R:7..0]
|
||||
kRGB_888x_SkColorType,
|
||||
// Four channel BGRA data (8 bits per channel) packed into a LE 32-bit word. R and B are swapped
|
||||
// relative to kRGBA_8888.
|
||||
// Bits: [A:31..24 R:23..16 G:15..8 B:7..0]
|
||||
kBGRA_8888_SkColorType,
|
||||
// Four channel RGBA data (10 bits per color, 2 bits for alpha) packed into a LE 32-bit word.
|
||||
// Bits: [A:31..30 B:29..20 G:19..10 R:9..0]
|
||||
kRGBA_1010102_SkColorType,
|
||||
// Four channel BGRA data (10 bits per color, 2 bits for alpha) packed into a LE 32-bit word.
|
||||
// R and B are swapped relative to kRGBA_1010102.
|
||||
// Bits: [A:31..30 R:29..20 G:19..10 B:9..0]
|
||||
kBGRA_1010102_SkColorType,
|
||||
// Three channel RGB data (10 bits per channel) packed into a LE 32-bit word. The remaining bits
|
||||
// are ignored and alpha is forced to opaque.
|
||||
// Bits: [x:31..30 B:29..20 G:19..10 R:9..0]
|
||||
kRGB_101010x_SkColorType,
|
||||
// Three channel BGR data (10 bits per channel) packed into a LE 32-bit word. The remaining bits
|
||||
// are ignored and alpha is forced to opaque. R and B are swapped relative to kRGB_101010x.
|
||||
// Bits: [x:31..30 R:29..20 G:19..10 B:9..0]
|
||||
kBGR_101010x_SkColorType,
|
||||
// Three channel BGR data (10 bits per channel) packed into a LE 32-bit word. The remaining bits
|
||||
// are ignored and alpha is forced to opaque. Instead of normalizing [0, 1023] to [0.0, 1.0] the
|
||||
// color channels map to an extended range of [-0.752941, 1.25098], compatible with
|
||||
// MTLPixelFormatBGR10_XR.
|
||||
// Bits: [x:31..30 R:29..20 G:19..10 B:9..0]
|
||||
kBGR_101010x_XR_SkColorType,
|
||||
// Four channel BGRA data (10 bits per channel) packed into a LE 64-bit word. Each channel is
|
||||
// preceded by 6 bits of padding. Instead of normalizing [0, 1023] to [0.0, 1.0] the color and
|
||||
// alpha channels map to an extended range of [-0.752941, 1.25098], compatible with
|
||||
// MTLPixelFormatBGRA10_XR.
|
||||
// Bits: [A:63..54 x:53..48 R:47..38 x:37..32 G:31..22 x:21..16 B:15..6 x:5..0]
|
||||
kBGRA_10101010_XR_SkColorType,
|
||||
// Four channel RGBA data (10 bits per channel) packed into a LE 64-bit word. Each channel is
|
||||
// preceded by 6 bits of padding.
|
||||
// Bits: [A:63..54 x:53..48 B:47..38 x:37..32 G:31..22 x:21..16 R:15..6 x:5..0]
|
||||
kRGBA_10x6_SkColorType,
|
||||
// Single channel data (8-bit) interpreted as a grayscale value (e.g. replicated to RGB).
|
||||
// Bits: [G:7..0]
|
||||
kGray_8_SkColorType,
|
||||
// Four channel RGBA data (16-bit half-float per channel) packed into a LE 64-bit word. Values
|
||||
// are assumed to be in [0.0,1.0] range, unlike kRGBA_F16.
|
||||
// Bits: [A:63..48 B:47..32 G:31..16 R:15..0]
|
||||
kRGBA_F16Norm_SkColorType,
|
||||
// Four channel RGBA data (16-bit half-float per channel) packed into a LE 64-bit word.
|
||||
// This has extended range compared to kRGBA_F16Norm.
|
||||
// Bits: [A:63..48 B:47..32 G:31..16 R:15..0]
|
||||
kRGBA_F16_SkColorType,
|
||||
// Three channel RGB data (16-bit half-float per channel) packed into a LE 64-bit word. The last
|
||||
// 16 bits are ignored and alpha is forced to opaque.
|
||||
// Bits: [x:63..48 B:47..32 G:31..16 R:15..0]
|
||||
kRGB_F16F16F16x_SkColorType,
|
||||
// Four channel RGBA data (32-bit float per channel) packed into a LE 128-bit word.
|
||||
// Bits: [A:127..96 B:95..64 G:63..32 R:31..0]
|
||||
kRGBA_F32_SkColorType,
|
||||
|
||||
// The following 6 colortypes are just for reading from - not for rendering to
|
||||
kR8G8_unorm_SkColorType, //!< pixel with a uint8_t for red and green
|
||||
|
||||
kA16_float_SkColorType, //!< pixel with a half float for alpha
|
||||
kR16G16_float_SkColorType, //!< pixel with a half float for red and green
|
||||
|
||||
kA16_unorm_SkColorType, //!< pixel with a little endian uint16_t for alpha
|
||||
kR16G16_unorm_SkColorType, //!< pixel with a little endian uint16_t for red and green
|
||||
kR16G16B16A16_unorm_SkColorType, //!< pixel with a little endian uint16_t for red, green, blue
|
||||
// and alpha
|
||||
// The following 8 colortypes are just for reading from - not for rendering to
|
||||
|
||||
// Two channel RG data (8 bits per channel). Blue is forced to 0, alpha is forced to opaque.
|
||||
// Bits: [G:15..8 R:7..0]
|
||||
kR8G8_unorm_SkColorType,
|
||||
// Single channel data (16-bit half-float) interpreted as alpha. RGB are 0.
|
||||
// Bits: [A:15..0]
|
||||
kA16_float_SkColorType,
|
||||
// Two channel RG data (16-bit half-float per channel) packed into a LE 32-bit word.
|
||||
// Blue is forced to 0, alpha is forced to opaque.
|
||||
// Bits: [G:31..16 R:15..0]
|
||||
kR16G16_float_SkColorType,
|
||||
// Single channel data (16 bits) interpreted as alpha. RGB are 0.
|
||||
// Bits: [A:15..0]
|
||||
kA16_unorm_SkColorType,
|
||||
// Two channel RG data (16 bits per channel) packed into a LE 32-bit word. B is forced to 0,
|
||||
// alpha is forced to opaque.
|
||||
// Bits: [G:31..16 R:15..0]
|
||||
kR16G16_unorm_SkColorType,
|
||||
// Four channel RGBA data (16 bits per channel) packed into a LE 64-bit word.
|
||||
// Bits: [A:63..48 B:47..32 G:31..16 R:15..0]
|
||||
kR16G16B16A16_unorm_SkColorType,
|
||||
// Four channel RGBA data (8 bits per channel) packed into a LE 32-bit word. The RGB values are
|
||||
// assumed to be encoded with the sRGB transfer function, which can be decoded automatically
|
||||
// by GPU hardware with certain texture formats.
|
||||
// Bits: [A:31..24 B:23..16 G:15..8 R:7..0]
|
||||
kSRGBA_8888_SkColorType,
|
||||
// Single channel data (8 bits) interpreted as red. G and B are forced to 0, alpha is forced to
|
||||
// opaque.
|
||||
// Bits: [R:7..0]
|
||||
kR8_unorm_SkColorType,
|
||||
|
||||
kLastEnum_SkColorType = kR8_unorm_SkColorType, //!< last valid value
|
||||
|
||||
@@ -91,13 +91,13 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const ForwardVerbIterator& other) {
|
||||
bool operator==(const ForwardVerbIterator& other) const {
|
||||
SkASSERT(fSegments.data() != other.fSegments.data() ||
|
||||
fSegments.size() == other.fSegments.size());
|
||||
return fSegments.data() == other.fSegments.data();
|
||||
}
|
||||
|
||||
bool operator!=(const ForwardVerbIterator& other) {
|
||||
bool operator!=(const ForwardVerbIterator& other) const {
|
||||
return !((*this) == other);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#define SKFONTSCANNER_H_
|
||||
|
||||
#include "include/core/SkFontArguments.h"
|
||||
#include "include/core/SkFontParameters.h"
|
||||
#include "include/core/SkRefCnt.h"
|
||||
#include "include/core/SkTypes.h"
|
||||
#include "include/private/base/SkFixed.h"
|
||||
@@ -22,13 +23,8 @@ class SkTypeface;
|
||||
class SkFontScanner : public SkNoncopyable {
|
||||
public:
|
||||
virtual ~SkFontScanner() = default;
|
||||
struct AxisDefinition {
|
||||
SkFourByteTag fTag;
|
||||
SkScalar fMinimum;
|
||||
SkScalar fDefault;
|
||||
SkScalar fMaximum;
|
||||
};
|
||||
typedef skia_private::STArray<4, AxisDefinition, true> AxisDefinitions;
|
||||
using AxisDefinitions = skia_private::STArray<4, SkFontParameters::Variation::Axis, true>;
|
||||
using VariationPosition = skia_private::STArray<4, SkFontArguments::VariationPosition::Coordinate, true>;
|
||||
|
||||
virtual bool scanFile(SkStreamAsset* stream, int* numFaces) const = 0;
|
||||
virtual bool scanFace(SkStreamAsset* stream, int faceIndex, int* numInstances) const = 0;
|
||||
@@ -39,7 +35,8 @@ public:
|
||||
SkString* name,
|
||||
SkFontStyle* style,
|
||||
bool* isFixedPitch,
|
||||
AxisDefinitions* axes) const = 0;
|
||||
AxisDefinitions* axes,
|
||||
VariationPosition* position) const = 0;
|
||||
virtual sk_sp<SkTypeface> MakeFromStream(std::unique_ptr<SkStreamAsset> stream,
|
||||
const SkFontArguments& args) const = 0;
|
||||
virtual SkFourByteTag getFactoryId() const = 0;
|
||||
|
||||
@@ -41,7 +41,7 @@ public:
|
||||
kUltraExpanded_Width = 9,
|
||||
};
|
||||
|
||||
enum Slant {
|
||||
enum Slant : uint8_t {
|
||||
kUpright_Slant,
|
||||
kItalic_Slant,
|
||||
kOblique_Slant,
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
|
||||
enum SkBlurStyle : int;
|
||||
struct SkDeserialProcs;
|
||||
struct SkRect;
|
||||
|
||||
/** \class SkMaskFilter
|
||||
|
||||
@@ -35,13 +34,6 @@ public:
|
||||
static sk_sp<SkMaskFilter> MakeBlur(SkBlurStyle style, SkScalar sigma,
|
||||
bool respectCTM = true);
|
||||
|
||||
/**
|
||||
* Returns the approximate bounds that would result from filtering the src rect.
|
||||
* The actual result may be different, but it should be contained within the
|
||||
* returned bounds.
|
||||
*/
|
||||
SkRect approximateFilteredBounds(const SkRect& src) const;
|
||||
|
||||
static sk_sp<SkMaskFilter> Deserialize(const void* data, size_t size,
|
||||
const SkDeserialProcs* procs = nullptr);
|
||||
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#ifndef SK_MILESTONE
|
||||
#define SK_MILESTONE 132
|
||||
#define SK_MILESTONE 136
|
||||
#endif
|
||||
|
||||
@@ -317,15 +317,18 @@ public:
|
||||
*/
|
||||
SkScalar getStrokeMiter() const { return fMiterLimit; }
|
||||
|
||||
/** Sets the limit at which a sharp corner is drawn beveled.
|
||||
Valid values are zero and greater.
|
||||
Has no effect if miter is less than zero.
|
||||
/** When stroking a small joinAngle with miter, the miterLength may be very long.
|
||||
When miterLength > maxMiterLength (or joinAngle < minJoinAngle) the join will become bevel.
|
||||
miterLimit = maxMiterLength / strokeWidth or miterLimit = 1 / sin(minJoinAngle / 2).
|
||||
|
||||
@param miter zero and greater miter limit
|
||||
This call has no effect if the miterLimit passed is less than zero.
|
||||
Values less than one will be treated as bevel.
|
||||
|
||||
@param miterLimit zero and greater miter limit
|
||||
|
||||
example: https://fiddle.skia.org/c/@Paint_setStrokeMiter
|
||||
*/
|
||||
void setStrokeMiter(SkScalar miter);
|
||||
void setStrokeMiter(SkScalar miterLimit);
|
||||
|
||||
/** \enum SkPaint::Cap
|
||||
Cap draws at the beginning and end of an open path contour.
|
||||
|
||||
@@ -1909,7 +1909,7 @@ private:
|
||||
void shrinkToFit();
|
||||
|
||||
// Creates a new Path after the supplied arguments have been validated by
|
||||
// sk_path_analyze_verbs().
|
||||
// SkPathPriv::AnalyzeVerbs().
|
||||
static SkPath MakeInternal(const SkPathVerbAnalysis& analsis,
|
||||
const SkPoint points[],
|
||||
const uint8_t verbs[],
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "include/core/SkScalar.h"
|
||||
#include "include/core/SkSpan.h"
|
||||
#include "include/core/SkTypes.h"
|
||||
#include "include/private/base/SkMacros.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
@@ -33,6 +34,7 @@ class SkString;
|
||||
If either axis radii is zero or less: radii are stored as zero; corner is square.
|
||||
If corner curves overlap, radii are proportionally reduced to fit within bounds.
|
||||
*/
|
||||
SK_BEGIN_REQUIRE_DENSE
|
||||
class SK_API SkRRect {
|
||||
public:
|
||||
|
||||
@@ -511,11 +513,11 @@ private:
|
||||
SkVector fRadii[4] = {{0, 0}, {0, 0}, {0,0}, {0,0}};
|
||||
// use an explicitly sized type so we're sure the class is dense (no uninitialized bytes)
|
||||
int32_t fType = kEmpty_Type;
|
||||
// TODO: add padding so we can use memcpy for flattening and not copy uninitialized data
|
||||
|
||||
// to access fRadii directly
|
||||
friend class SkPath;
|
||||
friend class SkRRectPriv;
|
||||
};
|
||||
SK_END_REQUIRE_DENSE
|
||||
|
||||
#endif
|
||||
|
||||
@@ -25,9 +25,9 @@ struct SkSamplingOptions;
|
||||
|
||||
/** \class SkShader
|
||||
*
|
||||
* Shaders specify the source color(s) for what is being drawn. If a paint
|
||||
* has no shader, then the paint's color is used. If the paint has a
|
||||
* shader, then the shader's color(s) are use instead, but they are
|
||||
* Shaders specify the premultiplied source color(s) for what is being drawn.
|
||||
* If a paint has no shader, then the paint's color is used. If the paint has a
|
||||
* shader, then the shader's color(s) are used instead, but they are
|
||||
* modulated by the paint's alpha. This makes it easy to create a shader
|
||||
* once (e.g. bitmap tiling or gradient) and then change its transparency
|
||||
* w/o having to modify the original shader... only the paint's alpha needs
|
||||
|
||||
@@ -79,10 +79,12 @@ public:
|
||||
[[nodiscard]] bool readS8(int8_t*);
|
||||
[[nodiscard]] bool readS16(int16_t*);
|
||||
[[nodiscard]] bool readS32(int32_t*);
|
||||
[[nodiscard]] bool readS64(int64_t*);
|
||||
|
||||
[[nodiscard]] bool readU8(uint8_t* i) { return this->readS8((int8_t*)i); }
|
||||
[[nodiscard]] bool readU16(uint16_t* i) { return this->readS16((int16_t*)i); }
|
||||
[[nodiscard]] bool readU32(uint32_t* i) { return this->readS32((int32_t*)i); }
|
||||
[[nodiscard]] bool readU64(uint64_t* i) { return this->readS64((int64_t*)i); }
|
||||
|
||||
[[nodiscard]] bool readBool(bool* b) {
|
||||
uint8_t i;
|
||||
@@ -240,8 +242,11 @@ public:
|
||||
uint16_t v = SkToU16(value);
|
||||
return this->write(&v, 2);
|
||||
}
|
||||
bool write32(uint32_t v) {
|
||||
return this->write(&v, 4);
|
||||
bool write32(uint32_t value) {
|
||||
return this->write(&value, 4);
|
||||
}
|
||||
bool write64(uint64_t value) {
|
||||
return this->write(&value, 8);
|
||||
}
|
||||
|
||||
bool writeText(const char text[]) {
|
||||
|
||||
@@ -28,13 +28,13 @@ static inline bool SkStrStartsWith(const char string[], const char prefixStr[])
|
||||
SkASSERT(prefixStr);
|
||||
return !strncmp(string, prefixStr, strlen(prefixStr));
|
||||
}
|
||||
static inline bool SkStrStartsWith(const char string[], const char prefixChar) {
|
||||
static inline bool SkStrStartsWith(const char string[], char prefixChar) {
|
||||
SkASSERT(string);
|
||||
return (prefixChar == *string);
|
||||
}
|
||||
|
||||
bool SkStrEndsWith(const char string[], const char suffixStr[]);
|
||||
bool SkStrEndsWith(const char string[], const char suffixChar);
|
||||
bool SkStrEndsWith(const char string[], char suffixChar);
|
||||
|
||||
int SkStrStartsWithOneOf(const char string[], const char prefixes[]);
|
||||
|
||||
@@ -44,7 +44,7 @@ static inline int SkStrFind(const char string[], const char substring[]) {
|
||||
return SkToInt(first - &string[0]);
|
||||
}
|
||||
|
||||
static inline int SkStrFindLastOf(const char string[], const char subchar) {
|
||||
static inline int SkStrFindLastOf(const char string[], char subchar) {
|
||||
const char* last = strrchr(string, subchar);
|
||||
if (nullptr == last) return -1;
|
||||
return SkToInt(last - &string[0]);
|
||||
@@ -55,7 +55,7 @@ static inline bool SkStrContains(const char string[], const char substring[]) {
|
||||
SkASSERT(substring);
|
||||
return (-1 != SkStrFind(string, substring));
|
||||
}
|
||||
static inline bool SkStrContains(const char string[], const char subchar) {
|
||||
static inline bool SkStrContains(const char string[], char subchar) {
|
||||
SkASSERT(string);
|
||||
char tmp[2];
|
||||
tmp[0] = subchar;
|
||||
@@ -142,25 +142,25 @@ public:
|
||||
bool startsWith(const char prefixStr[]) const {
|
||||
return SkStrStartsWith(fRec->data(), prefixStr);
|
||||
}
|
||||
bool startsWith(const char prefixChar) const {
|
||||
bool startsWith(char prefixChar) const {
|
||||
return SkStrStartsWith(fRec->data(), prefixChar);
|
||||
}
|
||||
bool endsWith(const char suffixStr[]) const {
|
||||
return SkStrEndsWith(fRec->data(), suffixStr);
|
||||
}
|
||||
bool endsWith(const char suffixChar) const {
|
||||
bool endsWith(char suffixChar) const {
|
||||
return SkStrEndsWith(fRec->data(), suffixChar);
|
||||
}
|
||||
bool contains(const char substring[]) const {
|
||||
return SkStrContains(fRec->data(), substring);
|
||||
}
|
||||
bool contains(const char subchar) const {
|
||||
bool contains(char subchar) const {
|
||||
return SkStrContains(fRec->data(), subchar);
|
||||
}
|
||||
int find(const char substring[]) const {
|
||||
return SkStrFind(fRec->data(), substring);
|
||||
}
|
||||
int findLastOf(const char subchar) const {
|
||||
int findLastOf(char subchar) const {
|
||||
return SkStrFindLastOf(fRec->data(), subchar);
|
||||
}
|
||||
|
||||
|
||||
@@ -330,6 +330,20 @@ public:
|
||||
*/
|
||||
sk_sp<SkImage> makeImageSnapshot(const SkIRect& bounds);
|
||||
|
||||
/** Returns an SkImage capturing the current SkSurface contents. However, the contents of the
|
||||
SkImage are only valid as long as no other writes to the SkSurface occur. If writes to the
|
||||
original SkSurface happen then contents of the SkImage are undefined. However, continued use
|
||||
of the SkImage should not cause crashes or similar fatal behavior.
|
||||
|
||||
This API is useful for cases where the client either immediately destroys the SkSurface
|
||||
after the SkImage is created or knows they will destroy the SkImage before writing to the
|
||||
SkSurface again.
|
||||
|
||||
This API can be more performant than makeImageSnapshot as it never does an internal copy
|
||||
of the data assuming the user frees either the SkImage or SkSurface as described above.
|
||||
*/
|
||||
sk_sp<SkImage> makeTemporaryImage();
|
||||
|
||||
/** Draws SkSurface contents to canvas, with its top-left corner at (x, y).
|
||||
|
||||
If SkPaint paint is not nullptr, apply SkColorFilter, alpha, SkImageFilter, and SkBlendMode.
|
||||
|
||||
@@ -211,7 +211,7 @@ public:
|
||||
struct Run {
|
||||
SkTypeface* fTypeface;
|
||||
int fGlyphCount;
|
||||
const uint16_t* fGlyphIndices;
|
||||
const SkGlyphID* fGlyphIndices;
|
||||
#ifdef SK_UNTIL_CRBUG_1187654_IS_FIXED
|
||||
const uint32_t* fClusterIndex_forTest;
|
||||
int fUtf8Size_forTest;
|
||||
@@ -231,7 +231,7 @@ public:
|
||||
struct ExperimentalRun {
|
||||
SkFont font;
|
||||
int count;
|
||||
const uint16_t* glyphs;
|
||||
const SkGlyphID* glyphs;
|
||||
const SkPoint* positions;
|
||||
};
|
||||
bool experimentalNext(ExperimentalRun*);
|
||||
|
||||
@@ -53,20 +53,18 @@ typedef uint32_t SkFontTableTag;
|
||||
class SK_API SkTypeface : public SkWeakRefCnt {
|
||||
public:
|
||||
/** Returns the typeface's intrinsic style attributes. */
|
||||
SkFontStyle fontStyle() const {
|
||||
return fStyle;
|
||||
}
|
||||
SkFontStyle fontStyle() const;
|
||||
|
||||
/** Returns true if style() has the kBold bit set. */
|
||||
bool isBold() const { return fStyle.weight() >= SkFontStyle::kSemiBold_Weight; }
|
||||
bool isBold() const;
|
||||
|
||||
/** Returns true if style() has the kItalic bit set. */
|
||||
bool isItalic() const { return fStyle.slant() != SkFontStyle::kUpright_Slant; }
|
||||
bool isItalic() const;
|
||||
|
||||
/** Returns true if the typeface claims to be fixed-pitch.
|
||||
* This is a style bit, advance widths may vary even if this returns true.
|
||||
*/
|
||||
bool isFixedPitch() const { return fIsFixedPitch; }
|
||||
bool isFixedPitch() const;
|
||||
|
||||
/** Copy into 'coordinates' (allocated by the caller) the design variation coordinates.
|
||||
*
|
||||
@@ -287,6 +285,20 @@ public:
|
||||
*/
|
||||
bool getPostScriptName(SkString* name) const;
|
||||
|
||||
/**
|
||||
* If the primary resource backing this typeface has a name (like a file
|
||||
* path or URL) representable by unicode code points, the `resourceName`
|
||||
* will be set. The primary purpose is as a user facing indication about
|
||||
* where the data was obtained (which font file was used).
|
||||
*
|
||||
* Returns the number of resources backing this typeface.
|
||||
*
|
||||
* For local font collections resource name will often be a file path. The
|
||||
* file path may or may not exist. If it does exist, using it to create an
|
||||
* SkTypeface may or may not create a similar SkTypeface to this one.
|
||||
*/
|
||||
int getResourceName(SkString* resourceName) const;
|
||||
|
||||
/**
|
||||
* Return a stream for the contents of the font data, or NULL on failure.
|
||||
* If ttcIndex is not null, it is set to the TrueTypeCollection index
|
||||
@@ -355,13 +367,15 @@ protected:
|
||||
/** Sets the font style. If used, must be called in the constructor. */
|
||||
void setFontStyle(SkFontStyle style) { fStyle = style; }
|
||||
|
||||
virtual SkFontStyle onGetFontStyle() const; // TODO: = 0;
|
||||
|
||||
virtual bool onGetFixedPitch() const; // TODO: = 0;
|
||||
|
||||
// Must return a valid scaler context. It can not return nullptr.
|
||||
virtual std::unique_ptr<SkScalerContext> onCreateScalerContext(const SkScalerContextEffects&,
|
||||
const SkDescriptor*) const = 0;
|
||||
virtual std::unique_ptr<SkScalerContext> onCreateScalerContext(
|
||||
const SkScalerContextEffects&, const SkDescriptor*) const = 0;
|
||||
virtual std::unique_ptr<SkScalerContext> onCreateScalerContextAsProxyTypeface
|
||||
(const SkScalerContextEffects&,
|
||||
const SkDescriptor*,
|
||||
sk_sp<SkTypeface>) const;
|
||||
(const SkScalerContextEffects&, const SkDescriptor*, SkTypeface* proxyTypeface) const;
|
||||
virtual void onFilterRec(SkScalerContextRec*) const = 0;
|
||||
friend class SkScalerContext; // onFilterRec
|
||||
|
||||
@@ -404,6 +418,7 @@ protected:
|
||||
*/
|
||||
virtual void onGetFamilyName(SkString* familyName) const = 0;
|
||||
virtual bool onGetPostScriptName(SkString*) const = 0;
|
||||
virtual int onGetResourceName(SkString* resourceName) const; // TODO: = 0;
|
||||
|
||||
/** Returns an iterator over the family names in the font. */
|
||||
virtual LocalizedStrings* onCreateFamilyNameIterator() const = 0;
|
||||
@@ -430,9 +445,9 @@ private:
|
||||
std::unique_ptr<SkAdvancedTypefaceMetrics> getAdvancedMetrics() const;
|
||||
friend class SkRandomTypeface; // getAdvancedMetrics
|
||||
friend class SkPDFFont; // getAdvancedMetrics
|
||||
friend class SkTypeface_fontconfig;
|
||||
|
||||
friend class SkTypeface_proxy;
|
||||
friend class SkFontPriv; // getGlyphToUnicodeMap
|
||||
friend void TestSkTypefaceGlyphToUnicodeMap(SkTypeface&, SkUnichar*);
|
||||
|
||||
private:
|
||||
SkTypefaceID fUniqueID;
|
||||
|
||||
@@ -97,6 +97,7 @@
|
||||
defined(SK_HISTOGRAM_BOOLEAN) || \
|
||||
defined(SK_HISTOGRAM_EXACT_LINEAR) || \
|
||||
defined(SK_HISTOGRAM_MEMORY_KB) || \
|
||||
defined(SK_HISTOGRAM_CUSTOM_COUNTS)|| \
|
||||
defined(SK_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES)
|
||||
# define SK_HISTOGRAMS_ENABLED 1
|
||||
#else
|
||||
@@ -119,6 +120,10 @@
|
||||
# define SK_HISTOGRAM_MEMORY_KB(name, sample)
|
||||
#endif
|
||||
|
||||
#ifndef SK_HISTOGRAM_CUSTOM_COUNTS
|
||||
# define SK_HISTOGRAM_CUSTOM_COUNTS(name, sample, countMin, countMax, bucketCount)
|
||||
#endif
|
||||
|
||||
#ifndef SK_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES
|
||||
# define SK_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(name, sampleUSec, minUSec, maxUSec, bucketCount)
|
||||
#endif
|
||||
|
||||
@@ -16,9 +16,12 @@
|
||||
#include <vector>
|
||||
|
||||
class SkCanvas;
|
||||
class SkCodec;
|
||||
class SkData;
|
||||
class SkExecutor;
|
||||
class SkPDFArray;
|
||||
class SkPDFStructTree;
|
||||
class SkPixmap;
|
||||
class SkWStream;
|
||||
|
||||
#define SKPDF_STRING(X) SKPDF_STRING_IMPL(X)
|
||||
@@ -80,6 +83,9 @@ struct DateTime {
|
||||
void toISO8601(SkString* dst) const;
|
||||
};
|
||||
|
||||
using DecodeJpegCallback = std::unique_ptr<SkCodec> (*)(sk_sp<SkData>);
|
||||
using EncodeJpegCallback = bool (*)(SkWStream* dst, const SkPixmap& src, int quality);
|
||||
|
||||
/** Optional metadata to be passed into the PDF factory function.
|
||||
*/
|
||||
struct Metadata {
|
||||
@@ -157,6 +163,7 @@ struct Metadata {
|
||||
enum class Outline : int {
|
||||
None = 0,
|
||||
StructureElementHeaders = 1,
|
||||
StructureElements = 2,
|
||||
} fOutline = Outline::None;
|
||||
|
||||
/** Executor to handle threaded work within PDF Backend. If this is nullptr,
|
||||
@@ -186,14 +193,50 @@ struct Metadata {
|
||||
enum Subsetter {
|
||||
kHarfbuzz_Subsetter,
|
||||
} fSubsetter = kHarfbuzz_Subsetter;
|
||||
|
||||
/** Clients can provide a way to decode jpeg. To use Skia's JPEG decoder, pass in
|
||||
SkJpegDecoder::Decode. If not supplied, all images will need to be re-encoded
|
||||
as jpegs or deflated images before embedding. If supplied, Skia may be able to
|
||||
skip the re-encoding step.
|
||||
Skia's JPEG decoder can be used here.
|
||||
*/
|
||||
SkPDF::DecodeJpegCallback jpegDecoder = nullptr;
|
||||
|
||||
/** Clients can provide a way to encode jpeg. If not supplied, images will be embedded
|
||||
as a deflated image, potentially making them much larger. If clients provide
|
||||
their own implementation, JPEGs should be encoded to RGB (not YUV) otherwise they
|
||||
will have the wrong surrounding metadata provided by Skia.
|
||||
Skia's JPEG encoder can be used here.
|
||||
*/
|
||||
SkPDF::EncodeJpegCallback jpegEncoder = nullptr;
|
||||
|
||||
// Skia's PDF support depends on having both a jpeg encoder and decoder for writing
|
||||
// compact PDFs. It will technically work, but produce larger than optimal PDFs
|
||||
// if either the decoder or encoder are left as nullptr. If clients will be creating
|
||||
// PDFs that don't use images or otherwise want to incur this cost (with the upside
|
||||
// of not having a jpeg library), they should set this to true to avoid an internal
|
||||
// assert from firing.
|
||||
bool allowNoJpegs = false;
|
||||
};
|
||||
|
||||
namespace NodeID {
|
||||
static const constexpr int Nothing = 0;
|
||||
static const constexpr int OtherArtifact = -1;
|
||||
static const constexpr int PaginationArtifact = -2;
|
||||
static const constexpr int PaginationHeaderArtifact = -3;
|
||||
static const constexpr int PaginationFooterArtifact = -4;
|
||||
static const constexpr int PaginationWatermarkArtifact = -5;
|
||||
static const constexpr int LayoutArtifact = -6;
|
||||
static const constexpr int PageArtifact = -7;
|
||||
static const constexpr int BackgroundArtifact = -8;
|
||||
} // namespace NodeID
|
||||
|
||||
/** Associate a node ID with subsequent drawing commands in an
|
||||
SkCanvas. The same node ID can appear in a StructureElementNode
|
||||
in order to associate a document's structure element tree with
|
||||
its content.
|
||||
|
||||
A node ID of zero indicates no node ID.
|
||||
A node ID of zero indicates no node ID. Negative node IDs are reserved.
|
||||
|
||||
@param canvas The canvas used to draw to the PDF.
|
||||
@param nodeId The node ID for subsequent drawing commands.
|
||||
@@ -207,15 +250,17 @@ SK_API void SetNodeId(SkCanvas* dst, int nodeID);
|
||||
@param stream A PDF document will be written to this stream. The document may write
|
||||
to the stream at anytime during its lifetime, until either close() is
|
||||
called or the document is deleted.
|
||||
@param metadata a PDFmetadata object. Any fields may be left empty.
|
||||
@param metadata a PDFmetadata object. Some fields may be left empty.
|
||||
|
||||
@returns NULL if there is an error, otherwise a newly created PDF-backed SkDocument.
|
||||
*/
|
||||
SK_API sk_sp<SkDocument> MakeDocument(SkWStream* stream, const Metadata& metadata);
|
||||
|
||||
#if !defined(SK_DISABLE_LEGACY_PDF_JPEG)
|
||||
static inline sk_sp<SkDocument> MakeDocument(SkWStream* stream) {
|
||||
return MakeDocument(stream, Metadata());
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace SkPDF
|
||||
|
||||
|
||||
41
gfx/skia/skia/include/docs/SkPDFJpegHelpers.h
Normal file
41
gfx/skia/skia/include/docs/SkPDFJpegHelpers.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#ifndef SkPDFJPEGHelpers_DEFINED
|
||||
#define SkPDFJPEGHelpers_DEFINED
|
||||
|
||||
#include "include/codec/SkJpegDecoder.h"
|
||||
#include "include/core/SkData.h"
|
||||
#include "include/core/SkRefCnt.h"
|
||||
#include "include/docs/SkPDFDocument.h"
|
||||
#include "include/encode/SkJpegEncoder.h"
|
||||
|
||||
class SkPixmap;
|
||||
class SkWStream;
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace SkPDF::JPEG {
|
||||
inline std::unique_ptr<SkCodec> Decode(sk_sp<SkData> data) {
|
||||
return SkJpegDecoder::Decode(data, nullptr, nullptr);
|
||||
}
|
||||
|
||||
inline bool Encode(SkWStream* dst, const SkPixmap& src, int quality) {
|
||||
SkJpegEncoder::Options jOpts;
|
||||
jOpts.fQuality = quality;
|
||||
return SkJpegEncoder::Encode(dst, src, jOpts);
|
||||
}
|
||||
|
||||
inline SkPDF::Metadata MetadataWithCallbacks() {
|
||||
SkPDF::Metadata m;
|
||||
m.jpegDecoder = SkPDF::JPEG::Decode;
|
||||
m.jpegEncoder = SkPDF::JPEG::Encode;
|
||||
return m;
|
||||
}
|
||||
|
||||
} // namespace SkPDF::JPEG
|
||||
|
||||
#endif
|
||||
@@ -94,7 +94,12 @@ public:
|
||||
kHSL,
|
||||
kHWB,
|
||||
|
||||
kLastColorSpace = kHWB,
|
||||
kDisplayP3,
|
||||
kRec2020,
|
||||
kProphotoRGB,
|
||||
kA98RGB,
|
||||
|
||||
kLastColorSpace = kA98RGB,
|
||||
};
|
||||
static constexpr int kColorSpaceCount = static_cast<int>(ColorSpace::kLastColorSpace) + 1;
|
||||
|
||||
|
||||
@@ -126,6 +126,8 @@ public:
|
||||
// don't run the inliner directly, but they still get an inlining pass once they are
|
||||
// painted.)
|
||||
bool forceUnoptimized = false;
|
||||
// When possible this name will be used to identify the created runtime effect.
|
||||
std::string_view fName;
|
||||
|
||||
private:
|
||||
friend class SkRuntimeEffect;
|
||||
@@ -170,6 +172,7 @@ public:
|
||||
|
||||
// Shader SkSL requires an entry point that looks like:
|
||||
// vec4 main(vec2 inCoords) { ... }
|
||||
// The color that is returned should be premultiplied.
|
||||
static Result MakeForShader(SkString sksl, const Options&);
|
||||
static Result MakeForShader(SkString sksl) {
|
||||
return MakeForShader(std::move(sksl), Options{});
|
||||
@@ -316,7 +319,9 @@ private:
|
||||
friend class SkRuntimeEffectPriv;
|
||||
|
||||
uint32_t fHash;
|
||||
uint32_t fStableKey;
|
||||
// When not 0, this field holds a StableKey value or a user-defined stable key
|
||||
uint32_t fStableKey = 0;
|
||||
SkString fName;
|
||||
|
||||
std::unique_ptr<SkSL::Program> fBaseProgram;
|
||||
std::unique_ptr<SkSL::RP::Program> fRPProgram;
|
||||
|
||||
@@ -38,6 +38,10 @@ public:
|
||||
static SkMaskFilter* CreateClip(uint8_t min, uint8_t max);
|
||||
|
||||
SkTableMaskFilter() = delete;
|
||||
|
||||
private:
|
||||
static void RegisterFlattenables();
|
||||
friend class SkFlattenable;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -23,6 +23,7 @@ class SkImage;
|
||||
class SkPixmap;
|
||||
class SkWStream;
|
||||
struct skcms_ICCProfile;
|
||||
struct SkGainmapInfo;
|
||||
|
||||
namespace SkPngEncoder {
|
||||
|
||||
@@ -80,6 +81,19 @@ struct Options {
|
||||
*/
|
||||
const skcms_ICCProfile* fICCProfile = nullptr;
|
||||
const char* fICCProfileDescription = nullptr;
|
||||
|
||||
/**
|
||||
* If non-null, then a gainmap and its metadata will be encoded as png chunks.
|
||||
* The gainmap will be encoded in a gmAP chunk as a full PNG container. The
|
||||
* gainmap info will be encoded in a gdAT chunk inside of the gmAP chunk.
|
||||
* This effectively is Option B proposed in this discussion for adding gainmaps
|
||||
* into PNG: https://github.com/w3c/png/issues/380#issuecomment-2325163149.
|
||||
*
|
||||
* Note that if fGainmapInfo is null, then fGainmap will fail to encode, as the
|
||||
* gainmap metadata is required to correctly interpret the encoded gainmap.
|
||||
*/
|
||||
const SkPixmap* fGainmap = nullptr;
|
||||
const SkGainmapInfo* fGainmapInfo = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -79,6 +79,15 @@ enum class Origin : unsigned {
|
||||
kBottomLeft,
|
||||
};
|
||||
|
||||
enum class GpuStatsFlags : uint32_t {
|
||||
kNone = 0b00,
|
||||
kElapsedTime = 0b01,
|
||||
};
|
||||
|
||||
struct GpuStats {
|
||||
uint64_t elapsedTime = 0;
|
||||
};
|
||||
|
||||
} // namespace skgpu
|
||||
|
||||
|
||||
|
||||
@@ -75,8 +75,55 @@ struct SK_API GrContextOptions {
|
||||
|
||||
GrContextOptions() {}
|
||||
|
||||
// Suppress prints for the GrContext.
|
||||
bool fSuppressPrints = false;
|
||||
/**
|
||||
* If Skia is creating a default VMA allocator for the Vulkan backend this value will be used
|
||||
* for the preferredLargeHeapBlockSize. If the value is not set, then Skia will use an
|
||||
* inernally defined default size.
|
||||
*
|
||||
* However, it is highly discouraged to have Skia make a default allocator (and support for
|
||||
* doing so will be removed soon, b/321962001). Instead clients should create their own
|
||||
* allocator to pass into Skia where they can fine tune this value themeselves.
|
||||
*/
|
||||
std::optional<uint64_t> fVulkanVMALargeHeapBlockSize;
|
||||
|
||||
/**
|
||||
* Optional callback that can be passed into the GrDirectContext which will be called when the
|
||||
* GrDirectContext is about to be destroyed. When this call is made, it will be safe for the
|
||||
* client to delete the GPU backend context that is backing the GrDirectContext. The
|
||||
* GrDirectContextDestroyedContext will be passed back to the client in the callback.
|
||||
*/
|
||||
GrDirectContextDestroyedContext fContextDeleteContext = nullptr;
|
||||
GrDirectContextDestroyedProc fContextDeleteProc = nullptr;
|
||||
|
||||
/**
|
||||
* Executor to handle threaded work within Ganesh. If this is nullptr, then all work will be
|
||||
* done serially on the main thread. To have worker threads assist with various tasks, set this
|
||||
* to a valid SkExecutor instance. Currently, used for software path rendering, but may be used
|
||||
* for other tasks.
|
||||
*/
|
||||
SkExecutor* fExecutor = nullptr;
|
||||
|
||||
/**
|
||||
* Cache in which to store compiled shader binaries between runs.
|
||||
*/
|
||||
PersistentCache* fPersistentCache = nullptr;
|
||||
|
||||
/**
|
||||
* If present, use this object to report shader compilation failures. If not, report failures
|
||||
* via SkDebugf and assert.
|
||||
*/
|
||||
ShaderErrorHandler* fShaderErrorHandler = nullptr;
|
||||
|
||||
/** Default minimum size to use when allocating buffers for uploading data to textures. The
|
||||
larger the value the more uploads can be packed into one buffer, but at the cost of
|
||||
more gpu memory allocated that may not be used. Uploads larger than the minimum will still
|
||||
work by allocating a dedicated buffer. */
|
||||
size_t fMinimumStagingBufferSize = 64 * 1024;
|
||||
|
||||
/**
|
||||
* The maximum size of cache textures used for Skia's Glyph cache.
|
||||
*/
|
||||
size_t fGlyphCacheTextureMaximumBytes = 2048 * 1024 * 4;
|
||||
|
||||
/**
|
||||
* Controls whether we check for GL errors after functions that allocate resources (e.g.
|
||||
@@ -86,6 +133,33 @@ struct SK_API GrContextOptions {
|
||||
*/
|
||||
Enable fSkipGLErrorChecks = Enable::kDefault;
|
||||
|
||||
/**
|
||||
* Can the glyph atlas use multiple textures. If allowed, the each texture's size is bound by
|
||||
* fGlypheCacheTextureMaximumBytes.
|
||||
*/
|
||||
Enable fAllowMultipleGlyphCacheTextures = Enable::kDefault;
|
||||
|
||||
/**
|
||||
* Enables driver workaround to use draws instead of HW clears, e.g. glClear on the GL backend.
|
||||
*/
|
||||
Enable fUseDrawInsteadOfClear = Enable::kDefault;
|
||||
|
||||
/**
|
||||
* Allow Ganesh to more aggressively reorder operations to reduce the number of render passes.
|
||||
* Offscreen draws will be done upfront instead of interrupting the main render pass when
|
||||
* possible. May increase VRAM usage, but still observes the resource cache limit.
|
||||
* Enabled by default.
|
||||
*/
|
||||
Enable fReduceOpsTaskSplitting = Enable::kDefault;
|
||||
|
||||
/**
|
||||
* This affects the usage of the PersistentCache. We can cache SkSL, backend source (GLSL), or
|
||||
* backend binaries (GL program binaries). By default we cache binaries, but if the driver's
|
||||
* binary loading/storing is believed to have bugs, this can be limited to caching GLSL.
|
||||
* Caching GLSL strings still saves CPU work when a GL program is created.
|
||||
*/
|
||||
ShaderCacheStrategy fShaderCacheStrategy = ShaderCacheStrategy::kBackendBinary;
|
||||
|
||||
/** Overrides: These options override feature detection using backend API queries. These
|
||||
overrides can only reduce the feature set or limits, never increase them beyond the
|
||||
detected values. */
|
||||
@@ -97,19 +171,48 @@ struct SK_API GrContextOptions {
|
||||
deduce the optimal value for this platform. */
|
||||
int fBufferMapThreshold = -1;
|
||||
|
||||
/** Default minimum size to use when allocating buffers for uploading data to textures. The
|
||||
larger the value the more uploads can be packed into one buffer, but at the cost of
|
||||
more gpu memory allocated that may not be used. Uploads larger than the minimum will still
|
||||
work by allocating a dedicated buffer. */
|
||||
size_t fMinimumStagingBufferSize = 64 * 1024;
|
||||
/**
|
||||
* Maximum number of GPU programs or pipelines to keep active in the runtime cache.
|
||||
*/
|
||||
int fRuntimeProgramCacheSize = 256;
|
||||
|
||||
/**
|
||||
* Executor to handle threaded work within Ganesh. If this is nullptr, then all work will be
|
||||
* done serially on the main thread. To have worker threads assist with various tasks, set this
|
||||
* to a valid SkExecutor instance. Currently, used for software path rendering, but may be used
|
||||
* for other tasks.
|
||||
* Specifies the number of samples Ganesh should use when performing internal draws with MSAA
|
||||
* (hardware capabilities permitting).
|
||||
*
|
||||
* If 0, Ganesh will disable internal code paths that use multisampling.
|
||||
*/
|
||||
SkExecutor* fExecutor = nullptr;
|
||||
int fInternalMultisampleCount = 4;
|
||||
|
||||
/**
|
||||
* In Skia's vulkan backend a single GrContext submit equates to the submission of a single
|
||||
* primary command buffer to the VkQueue. This value specifies how many vulkan secondary command
|
||||
* buffers we will cache for reuse on a given primary command buffer. A single submit may use
|
||||
* more than this many secondary command buffers, but after the primary command buffer is
|
||||
* finished on the GPU it will only hold on to this many secondary command buffers for reuse.
|
||||
*
|
||||
* A value of -1 means we will pick a limit value internally.
|
||||
*/
|
||||
int fMaxCachedVulkanSecondaryCommandBuffers = -1;
|
||||
|
||||
/**
|
||||
* Below this threshold size in device space distance field fonts won't be used. Distance field
|
||||
* fonts don't support hinting which is more important at smaller sizes.
|
||||
*/
|
||||
float fMinDistanceFieldFontSize = 18;
|
||||
|
||||
/**
|
||||
* Above this threshold size in device space glyphs are drawn as individual paths.
|
||||
*/
|
||||
#if defined(SK_BUILD_FOR_ANDROID)
|
||||
float fGlyphsAsPathsFontSize = 384;
|
||||
#elif defined(SK_BUILD_FOR_MAC)
|
||||
float fGlyphsAsPathsFontSize = 256;
|
||||
#else
|
||||
float fGlyphsAsPathsFontSize = 324;
|
||||
#endif
|
||||
|
||||
GrDriverBugWorkarounds fDriverBugWorkarounds;
|
||||
|
||||
/** Construct mipmaps manually, via repeated downsampling draw-calls. This is used when
|
||||
the driver's implementation (glGenerateMipmap) contains bugs. This requires mipmap
|
||||
@@ -142,34 +245,6 @@ struct SK_API GrContextOptions {
|
||||
*/
|
||||
bool fDisableGpuYUVConversion = false;
|
||||
|
||||
/**
|
||||
* The maximum size of cache textures used for Skia's Glyph cache.
|
||||
*/
|
||||
size_t fGlyphCacheTextureMaximumBytes = 2048 * 1024 * 4;
|
||||
|
||||
/**
|
||||
* Below this threshold size in device space distance field fonts won't be used. Distance field
|
||||
* fonts don't support hinting which is more important at smaller sizes.
|
||||
*/
|
||||
float fMinDistanceFieldFontSize = 18;
|
||||
|
||||
/**
|
||||
* Above this threshold size in device space glyphs are drawn as individual paths.
|
||||
*/
|
||||
#if defined(SK_BUILD_FOR_ANDROID)
|
||||
float fGlyphsAsPathsFontSize = 384;
|
||||
#elif defined(SK_BUILD_FOR_MAC)
|
||||
float fGlyphsAsPathsFontSize = 256;
|
||||
#else
|
||||
float fGlyphsAsPathsFontSize = 324;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Can the glyph atlas use multiple textures. If allowed, the each texture's size is bound by
|
||||
* fGlypheCacheTextureMaximumBytes.
|
||||
*/
|
||||
Enable fAllowMultipleGlyphCacheTextures = Enable::kDefault;
|
||||
|
||||
/**
|
||||
* Bugs on certain drivers cause stencil buffers to leak. This flag causes Skia to avoid
|
||||
* allocating stencil buffers and use alternate rasterization paths, avoiding the leak.
|
||||
@@ -183,19 +258,6 @@ struct SK_API GrContextOptions {
|
||||
*/
|
||||
bool fSharpenMipmappedTextures = true;
|
||||
|
||||
/**
|
||||
* Enables driver workaround to use draws instead of HW clears, e.g. glClear on the GL backend.
|
||||
*/
|
||||
Enable fUseDrawInsteadOfClear = Enable::kDefault;
|
||||
|
||||
/**
|
||||
* Allow Ganesh to more aggressively reorder operations to reduce the number of render passes.
|
||||
* Offscreen draws will be done upfront instead of interrupting the main render pass when
|
||||
* possible. May increase VRAM usage, but still observes the resource cache limit.
|
||||
* Enabled by default.
|
||||
*/
|
||||
Enable fReduceOpsTaskSplitting = Enable::kDefault;
|
||||
|
||||
/**
|
||||
* Some ES3 contexts report the ES2 external image extension, but not the ES3 version.
|
||||
* If support for external images is critical, enabling this option will cause Ganesh to limit
|
||||
@@ -210,60 +272,6 @@ struct SK_API GrContextOptions {
|
||||
*/
|
||||
bool fDisableDriverCorrectnessWorkarounds = false;
|
||||
|
||||
/**
|
||||
* Maximum number of GPU programs or pipelines to keep active in the runtime cache.
|
||||
*/
|
||||
int fRuntimeProgramCacheSize = 256;
|
||||
|
||||
/**
|
||||
* Cache in which to store compiled shader binaries between runs.
|
||||
*/
|
||||
PersistentCache* fPersistentCache = nullptr;
|
||||
|
||||
/**
|
||||
* This affects the usage of the PersistentCache. We can cache SkSL, backend source (GLSL), or
|
||||
* backend binaries (GL program binaries). By default we cache binaries, but if the driver's
|
||||
* binary loading/storing is believed to have bugs, this can be limited to caching GLSL.
|
||||
* Caching GLSL strings still saves CPU work when a GL program is created.
|
||||
*/
|
||||
ShaderCacheStrategy fShaderCacheStrategy = ShaderCacheStrategy::kBackendBinary;
|
||||
|
||||
/**
|
||||
* If present, use this object to report shader compilation failures. If not, report failures
|
||||
* via SkDebugf and assert.
|
||||
*/
|
||||
ShaderErrorHandler* fShaderErrorHandler = nullptr;
|
||||
|
||||
/**
|
||||
* Specifies the number of samples Ganesh should use when performing internal draws with MSAA
|
||||
* (hardware capabilities permitting).
|
||||
*
|
||||
* If 0, Ganesh will disable internal code paths that use multisampling.
|
||||
*/
|
||||
int fInternalMultisampleCount = 4;
|
||||
|
||||
/**
|
||||
* In Skia's vulkan backend a single GrContext submit equates to the submission of a single
|
||||
* primary command buffer to the VkQueue. This value specifies how many vulkan secondary command
|
||||
* buffers we will cache for reuse on a given primary command buffer. A single submit may use
|
||||
* more than this many secondary command buffers, but after the primary command buffer is
|
||||
* finished on the GPU it will only hold on to this many secondary command buffers for reuse.
|
||||
*
|
||||
* A value of -1 means we will pick a limit value internally.
|
||||
*/
|
||||
int fMaxCachedVulkanSecondaryCommandBuffers = -1;
|
||||
|
||||
/**
|
||||
* If Skia is creating a default VMA allocator for the Vulkan backend this value will be used
|
||||
* for the preferredLargeHeapBlockSize. If the value is not set, then Skia will use an
|
||||
* inernally defined default size.
|
||||
*
|
||||
* However, it is highly discouraged to have Skia make a default allocator (and support for
|
||||
* doing so will be removed soon, b/321962001). Instead clients should create their own
|
||||
* allocator to pass into Skia where they can fine tune this value themeselves.
|
||||
*/
|
||||
std::optional<uint64_t> fVulkanVMALargeHeapBlockSize;
|
||||
|
||||
/**
|
||||
* If true, the caps will never support mipmaps.
|
||||
*/
|
||||
@@ -307,20 +315,32 @@ struct SK_API GrContextOptions {
|
||||
*/
|
||||
bool fAlwaysUseTexStorageWhenAvailable = false;
|
||||
|
||||
/**
|
||||
* Optional callback that can be passed into the GrDirectContext which will be called when the
|
||||
* GrDirectContext is about to be destroyed. When this call is made, it will be safe for the
|
||||
* client to delete the GPU backend context that is backing the GrDirectContext. The
|
||||
* GrDirectContextDestroyedContext will be passed back to the client in the callback.
|
||||
*/
|
||||
GrDirectContextDestroyedContext fContextDeleteContext = nullptr;
|
||||
GrDirectContextDestroyedProc fContextDeleteProc = nullptr;
|
||||
// Suppress prints for the GrContext.
|
||||
bool fSuppressPrints = false;
|
||||
|
||||
#if defined(GPU_TEST_UTILS)
|
||||
/**
|
||||
* Private options that are only meant for testing within Skia's tools.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Include or exclude specific GPU path renderers.
|
||||
*/
|
||||
GpuPathRenderers fGpuPathRenderers = GpuPathRenderers::kDefault;
|
||||
|
||||
/**
|
||||
* Specify the GPU resource cache limit. Equivalent to calling `setResourceCacheLimit` on the
|
||||
* context at construction time.
|
||||
*
|
||||
* A value of -1 means use the default limit value.
|
||||
*/
|
||||
int fResourceCacheLimitOverride = -1;
|
||||
|
||||
/**
|
||||
* Maximum width and height of internal texture atlases.
|
||||
*/
|
||||
int fMaxTextureAtlasSize = 2048;
|
||||
|
||||
/**
|
||||
* Testing-only mode to exercise allocation failures in the flush-time callback objects.
|
||||
* For now it only simulates allocation failure during the preFlush callback.
|
||||
@@ -368,26 +388,8 @@ struct SK_API GrContextOptions {
|
||||
*/
|
||||
bool fDisallowWriteAndTransferPixelRowBytes = false;
|
||||
|
||||
/**
|
||||
* Include or exclude specific GPU path renderers.
|
||||
*/
|
||||
GpuPathRenderers fGpuPathRenderers = GpuPathRenderers::kDefault;
|
||||
|
||||
/**
|
||||
* Specify the GPU resource cache limit. Equivalent to calling `setResourceCacheLimit` on the
|
||||
* context at construction time.
|
||||
*
|
||||
* A value of -1 means use the default limit value.
|
||||
*/
|
||||
int fResourceCacheLimitOverride = -1;
|
||||
|
||||
/**
|
||||
* Maximum width and height of internal texture atlases.
|
||||
*/
|
||||
int fMaxTextureAtlasSize = 2048;
|
||||
#endif
|
||||
|
||||
GrDriverBugWorkarounds fDriverBugWorkarounds;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -271,6 +271,11 @@ public:
|
||||
*/
|
||||
void purgeUnlockedResources(GrPurgeResourceOptions opts);
|
||||
|
||||
/*
|
||||
* Gets the types of GPU stats supported by this Context.
|
||||
*/
|
||||
skgpu::GpuStatsFlags supportedGpuStats() const;
|
||||
|
||||
/**
|
||||
* Gets the maximum supported texture size.
|
||||
*/
|
||||
|
||||
@@ -34,8 +34,10 @@ enum GrDriverBugWorkaroundType {
|
||||
|
||||
class SK_API GrDriverBugWorkarounds {
|
||||
public:
|
||||
GrDriverBugWorkarounds();
|
||||
GrDriverBugWorkarounds() = default;
|
||||
GrDriverBugWorkarounds(const GrDriverBugWorkarounds&) = default;
|
||||
~GrDriverBugWorkarounds() = default;
|
||||
|
||||
explicit GrDriverBugWorkarounds(const std::vector<int32_t>& workarounds);
|
||||
|
||||
GrDriverBugWorkarounds& operator=(const GrDriverBugWorkarounds&) = default;
|
||||
@@ -43,8 +45,6 @@ class SK_API GrDriverBugWorkarounds {
|
||||
// Turn on any workarounds listed in |workarounds| (but don't turn any off).
|
||||
void applyOverrides(const GrDriverBugWorkarounds& workarounds);
|
||||
|
||||
~GrDriverBugWorkarounds();
|
||||
|
||||
#define GPU_OP(type, name) bool name = false;
|
||||
GPU_DRIVER_BUG_WORKAROUNDS(GPU_OP)
|
||||
#undef GPU_OP
|
||||
|
||||
@@ -9,17 +9,13 @@
|
||||
#define GrTypes_DEFINED
|
||||
|
||||
#include "include/core/SkTypes.h"
|
||||
#include "include/gpu/GpuTypes.h"
|
||||
#include "include/private/base/SkTo.h" // IWYU pragma: keep
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
class GrBackendSemaphore;
|
||||
|
||||
namespace skgpu {
|
||||
enum class Protected : bool;
|
||||
enum class Renderable : bool;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
@@ -110,6 +106,8 @@ static const uint32_t kAll_GrBackendState = 0xffffffff;
|
||||
|
||||
typedef void* GrGpuFinishedContext;
|
||||
typedef void (*GrGpuFinishedProc)(GrGpuFinishedContext finishedContext);
|
||||
typedef void (*GrGpuFinishedWithStatsProc)(GrGpuFinishedContext finishedContext,
|
||||
const skgpu::GpuStats&);
|
||||
|
||||
typedef void* GrGpuSubmittedContext;
|
||||
typedef void (*GrGpuSubmittedProc)(GrGpuSubmittedContext submittedContext, bool success);
|
||||
@@ -131,10 +129,15 @@ typedef void (*GrDirectContextDestroyedProc)(GrDirectContextDestroyedContext des
|
||||
* and returned in initialized GrBackendSemaphore objects. The GrBackendSemaphore objects
|
||||
* themselves can be deleted as soon as this function returns.
|
||||
*
|
||||
* If a finishedProc is provided, the finishedProc will be called when all work submitted to the gpu
|
||||
* from this flush call and all previous flush calls has finished on the GPU. If the flush call
|
||||
* fails due to an error and nothing ends up getting sent to the GPU, the finished proc is called
|
||||
* immediately.
|
||||
* If a finishedProc or finishedWithStatsProc is provided, the proc will be called when all work
|
||||
* submitted to the gpu from this flush call and all previous flush calls has finished on the GPU.
|
||||
* If the flush call fails due to an error and nothing ends up getting sent to the GPU, the finished
|
||||
* proc is called immediately. If both types of proc are provided then finishedWithStatsProc is
|
||||
* preferred.
|
||||
*
|
||||
* When finishedWithStatsProc is called the GpuStats passed will contain valid values for stats
|
||||
* by requested by gpuStatsFlags, assuming the stats are supported by the underlying backend GPU
|
||||
* context and the GPU work completed successfully.
|
||||
*
|
||||
* If a submittedProc is provided, the submittedProc will be called when all work from this flush
|
||||
* call is submitted to the GPU. If the flush call fails due to an error and nothing will get sent
|
||||
@@ -148,8 +151,10 @@ typedef void (*GrDirectContextDestroyedProc)(GrDirectContextDestroyedContext des
|
||||
*/
|
||||
struct GrFlushInfo {
|
||||
size_t fNumSemaphores = 0;
|
||||
skgpu::GpuStatsFlags fGpuStatsFlags = skgpu::GpuStatsFlags::kNone;
|
||||
GrBackendSemaphore* fSignalSemaphores = nullptr;
|
||||
GrGpuFinishedProc fFinishedProc = nullptr;
|
||||
GrGpuFinishedWithStatsProc fFinishedWithStatsProc = nullptr;
|
||||
GrGpuFinishedContext fFinishedContext = nullptr;
|
||||
GrGpuSubmittedProc fSubmittedProc = nullptr;
|
||||
GrGpuSubmittedContext fSubmittedContext = nullptr;
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
namespace skgpu::graphite {
|
||||
|
||||
class BackendTextureData;
|
||||
struct VulkanTextureInfo;
|
||||
|
||||
class SK_API BackendTexture {
|
||||
public:
|
||||
@@ -61,4 +60,3 @@ private:
|
||||
} // namespace skgpu::graphite
|
||||
|
||||
#endif // skgpu_graphite_BackendTexture_DEFINED
|
||||
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
#include "include/gpu/graphite/Recorder.h"
|
||||
#include "include/private/base/SingleOwner.h"
|
||||
|
||||
#if defined(GPU_TEST_UTILS)
|
||||
#include "include/private/base/SkMutex.h"
|
||||
#endif
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
@@ -33,7 +37,7 @@ class Context;
|
||||
class ContextPriv;
|
||||
class GlobalCache;
|
||||
class PaintOptions;
|
||||
class PlotUploadTracker;
|
||||
class PrecompileContext;
|
||||
class QueueManager;
|
||||
class Recording;
|
||||
class ResourceProvider;
|
||||
@@ -53,6 +57,11 @@ public:
|
||||
|
||||
std::unique_ptr<Recorder> makeRecorder(const RecorderOptions& = {});
|
||||
|
||||
/** Creates a helper object that can be moved to a different thread and used
|
||||
* for precompilation.
|
||||
*/
|
||||
std::unique_ptr<PrecompileContext> makePrecompileContext();
|
||||
|
||||
bool insertRecording(const InsertRecordingInfo&);
|
||||
bool submit(SyncToCpu = SyncToCpu::kNo);
|
||||
|
||||
@@ -218,6 +227,12 @@ public:
|
||||
*/
|
||||
size_t maxBudgetedBytes() const;
|
||||
|
||||
/**
|
||||
* Sets the size of Context's gpu memory cache budget in bytes. If the new budget is lower than
|
||||
* the current budget, the cache will try to free resources to get under the new budget.
|
||||
*/
|
||||
void setMaxBudgetedBytes(size_t bytes);
|
||||
|
||||
/**
|
||||
* Enumerates all cached GPU resources owned by the Context and dumps their memory to
|
||||
* traceMemoryDump.
|
||||
@@ -240,6 +255,11 @@ public:
|
||||
*/
|
||||
bool supportsProtectedContent() const;
|
||||
|
||||
/*
|
||||
* Gets the types of GPU stats supported by this Context.
|
||||
*/
|
||||
GpuStatsFlags supportedGpuStats() const;
|
||||
|
||||
// Provides access to functions that aren't part of the public API.
|
||||
ContextPriv priv();
|
||||
const ContextPriv priv() const; // NOLINT(readability-const-return-type)
|
||||
@@ -343,11 +363,14 @@ private:
|
||||
mutable SingleOwner fSingleOwner;
|
||||
|
||||
#if defined(GPU_TEST_UTILS)
|
||||
void deregisterRecorder(const Recorder*) SK_EXCLUDES(fTestingLock);
|
||||
|
||||
// In test builds a Recorder may track the Context that was used to create it.
|
||||
bool fStoreContextRefInRecorder = false;
|
||||
// If this tracking is on, to allow the client to safely delete this Context or its Recorders
|
||||
// in any order we must also track the Recorders created here.
|
||||
std::vector<Recorder*> fTrackedRecorders;
|
||||
SkMutex fTestingLock;
|
||||
std::vector<Recorder*> fTrackedRecorders SK_GUARDED_BY(fTestingLock);
|
||||
#endif
|
||||
|
||||
// Needed for MessageBox handling
|
||||
|
||||
@@ -8,11 +8,15 @@
|
||||
#ifndef skgpu_graphite_ContextOptions_DEFINED
|
||||
#define skgpu_graphite_ContextOptions_DEFINED
|
||||
|
||||
#include "include/core/SkRefCnt.h"
|
||||
#include "include/core/SkSpan.h"
|
||||
#include "include/private/base/SkAPI.h"
|
||||
#include "include/private/base/SkMath.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
class SkData;
|
||||
class SkRuntimeEffect;
|
||||
namespace skgpu { class ShaderErrorHandler; }
|
||||
|
||||
namespace skgpu::graphite {
|
||||
@@ -85,12 +89,12 @@ struct SK_API ContextOptions {
|
||||
bool fSupportBilerpFromGlyphAtlas = false;
|
||||
|
||||
/**
|
||||
* Disable caching of glyph uploads at the start of each Recording. These can add additional
|
||||
* overhead and are only necessary if Recordings are replayed or played out of order.
|
||||
*
|
||||
* Deprecated, now only used to set requireOrderedRecordings Caps.
|
||||
* For the moment, if Recordings are replayed in the order they are recorded, then
|
||||
* Graphite can make certain assumptions that allow for better performance. Otherwise
|
||||
* we have to flush some caches at the start of each Recording to ensure that they can
|
||||
* be played back properly.
|
||||
*/
|
||||
bool fDisableCachedGlyphUploads = false;
|
||||
bool fRequireOrderedRecordings = false;
|
||||
|
||||
static constexpr size_t kDefaultContextBudget = 256 * (1 << 20);
|
||||
/**
|
||||
@@ -118,6 +122,37 @@ struct SK_API ContextOptions {
|
||||
*/
|
||||
std::optional<uint64_t> fVulkanVMALargeHeapBlockSize;
|
||||
|
||||
/** Client-provided context that is passed to client-provided PipelineCallback. */
|
||||
using PipelineCallbackContext = void*;
|
||||
/** Client-provided callback that is called whenever Graphite encounters a new Pipeline. */
|
||||
using PipelineCallback = void (*)(PipelineCallbackContext context, sk_sp<SkData> pipelineData);
|
||||
|
||||
/**
|
||||
* These two members allow a client to register a callback that will be invoked
|
||||
* whenever Graphite encounters a new Pipeline. The callback will be passed an
|
||||
* sk_sp<SkData> that a client can take ownership of and serialize. The SkData
|
||||
* contains all the information Graphite requires to recreate the Pipeline at
|
||||
* a later date. The SkData is versioned however, so must be regenerated and
|
||||
* re-serialized when it becomes out of date.
|
||||
*/
|
||||
PipelineCallbackContext fPipelineCallbackContext = nullptr;
|
||||
PipelineCallback fPipelineCallback = nullptr;
|
||||
|
||||
/**
|
||||
* The runtime effects provided here will be registered as user-defined *known* runtime
|
||||
* effects and will be given a stable key. Such runtime effects can then be used in
|
||||
* serialized pipeline keys (c.f. PrecompileContext::precompile).
|
||||
*
|
||||
* Graphite will take a ref on the provided runtime effects and they will persist for as long
|
||||
* as the Context exists. Rather than recreating new SkRuntimeEffects using the same SkSL,
|
||||
* clients should use the existing SkRuntimeEffects provided here.
|
||||
*
|
||||
* Warning: Registering runtime effects here does obligate users to clear out their caches
|
||||
* of serialized pipeline keys if the provided runtime effects ever change in a meaningful way.
|
||||
* This includes adding, removing or reordering the effects provided here.
|
||||
*/
|
||||
SkSpan<sk_sp<SkRuntimeEffect>> fUserDefinedKnownRuntimeEffects;
|
||||
|
||||
/**
|
||||
* Private options that are only meant for testing within Skia's tools.
|
||||
*/
|
||||
|
||||
@@ -30,13 +30,23 @@ class Task;
|
||||
using GpuFinishedContext = void*;
|
||||
using GpuFinishedProc = void (*)(GpuFinishedContext finishedContext, CallbackResult);
|
||||
|
||||
using GpuFinishedWithStatsProc = void (*)(GpuFinishedContext finishedContext,
|
||||
CallbackResult,
|
||||
const GpuStats&);
|
||||
|
||||
/**
|
||||
* The fFinishedProc is called when the Recording has been submitted and finished on the GPU, or
|
||||
* when there is a failure that caused it not to be submitted. The callback will always be called
|
||||
* and the caller can use the callback to know it is safe to free any resources associated with
|
||||
* the Recording that they may be holding onto. If the Recording is successfully submitted to the
|
||||
* GPU the callback will be called with CallbackResult::kSuccess once the GPU has finished. All
|
||||
* other cases where some failure occured it will be called with CallbackResult::kFailed.
|
||||
* other cases where some failure occurred it will be called with CallbackResult::kFailed.
|
||||
*
|
||||
* Alternatively, the client can provide fFinishedProcWithStats. This provides additional
|
||||
* information about execution of the recording on the GPU. Only the stats requested using
|
||||
* fStatsFlags will be valid and only if CallbackResult is kSuccess. If both fFinishedProc
|
||||
* and fFinishedProcWithStats are provided the latter is preferred and the former won't be
|
||||
* called.
|
||||
*
|
||||
* The fTargetSurface, if provided, is used as a target for any draws recorded onto a deferred
|
||||
* canvas returned from Recorder::makeDeferredCanvas. This target surface must be provided iff
|
||||
@@ -74,8 +84,10 @@ struct InsertRecordingInfo {
|
||||
size_t fNumSignalSemaphores = 0;
|
||||
BackendSemaphore* fSignalSemaphores = nullptr;
|
||||
|
||||
GpuStatsFlags fGpuStatsFlags = GpuStatsFlags::kNone;
|
||||
GpuFinishedContext fFinishedContext = nullptr;
|
||||
GpuFinishedProc fFinishedProc = nullptr;
|
||||
GpuFinishedWithStatsProc fFinishedWithStatsProc = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -87,8 +99,15 @@ struct InsertRecordingInfo {
|
||||
* other cases where some failure occured it will be called with CallbackResult::kFailed.
|
||||
*/
|
||||
struct InsertFinishInfo {
|
||||
InsertFinishInfo() = default;
|
||||
InsertFinishInfo(GpuFinishedContext context, GpuFinishedProc proc)
|
||||
: fFinishedContext{context}, fFinishedProc{proc} {}
|
||||
InsertFinishInfo(GpuFinishedContext context, GpuFinishedWithStatsProc proc)
|
||||
: fFinishedContext{context}, fFinishedWithStatsProc{proc} {}
|
||||
GpuFinishedContext fFinishedContext = nullptr;
|
||||
GpuFinishedProc fFinishedProc = nullptr;
|
||||
GpuFinishedWithStatsProc fFinishedWithStatsProc = nullptr;
|
||||
GpuStatsFlags fGpuStatsFlags = GpuStatsFlags::kNone;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -121,44 +140,47 @@ enum class DepthStencilFlags : int {
|
||||
*/
|
||||
enum DrawTypeFlags : uint16_t {
|
||||
|
||||
kNone = 0b000000000,
|
||||
kNone = 0,
|
||||
|
||||
// kBitmapText_Mask should be used for the BitmapTextRenderStep[mask] RenderStep
|
||||
kBitmapText_Mask = 0b00000001,
|
||||
kBitmapText_Mask = 1 << 0,
|
||||
// kBitmapText_LCD should be used for the BitmapTextRenderStep[LCD] RenderStep
|
||||
kBitmapText_LCD = 0b00000010,
|
||||
kBitmapText_LCD = 1 << 1,
|
||||
// kBitmapText_Color should be used for the BitmapTextRenderStep[color] RenderStep
|
||||
kBitmapText_Color = 0b00000100,
|
||||
kBitmapText_Color = 1 << 2,
|
||||
// kSDFText should be used for the SDFTextRenderStep RenderStep
|
||||
kSDFText = 0b00001000,
|
||||
kSDFText = 1 << 3,
|
||||
// kSDFText_LCD should be used for the SDFTextLCDRenderStep RenderStep
|
||||
kSDFText_LCD = 0b00010000,
|
||||
kSDFText_LCD = 1 << 4,
|
||||
|
||||
// kDrawVertices should be used to generate Pipelines that use the following RenderSteps:
|
||||
// VerticesRenderStep[*] for:
|
||||
// [tris], [tris-texCoords], [tris-color], [tris-color-texCoords],
|
||||
// [tristrips], [tristrips-texCoords], [tristrips-color], [tristrips-color-texCoords]
|
||||
kDrawVertices = 0b00100000,
|
||||
// [Tris], [TrisTexCoords], [TrisColor], [TrisColorTexCoords],
|
||||
// [Tristrips], [TristripsTexCoords], [TristripsColor], [TristripsColorTexCoords]
|
||||
kDrawVertices = 1 << 5,
|
||||
|
||||
// kCircularArc renders filled circular arcs, with or without the center included, and
|
||||
// stroked circular arcs with butt or round caps that don't include the center point.
|
||||
// It corresponds to the CircularArcRenderStep.
|
||||
kCircularArc = 1 << 6,
|
||||
|
||||
// kSimpleShape should be used to generate Pipelines that use the following RenderSteps:
|
||||
// AnalyticBlurRenderStep
|
||||
// AnalyticRRectRenderStep
|
||||
// PerEdgeAAQuadRenderStep
|
||||
// CoverBoundsRenderStep[non-aa-fill]
|
||||
kSimpleShape = 0b01000000,
|
||||
// CoverBoundsRenderStep[NonAAFill]
|
||||
kSimpleShape = 1 << 7,
|
||||
|
||||
// kNonSimpleShape should be used to generate Pipelines that use the following RenderSteps:
|
||||
// CoverageMaskRenderStep
|
||||
// CoverBoundsRenderStep[*] for [inverse-cover], [regular-cover]
|
||||
// CoverBoundsRenderStep[*] for [InverseCover], [RegularCover]
|
||||
// TessellateStrokeRenderStep
|
||||
// TessellateWedgesRenderStep[*] for [convex], [evenodd], [winding]
|
||||
// TessellateCurvesRenderStep[*] for [even-odd], [winding]
|
||||
// MiddleOutFanRenderStep[*] for [even-odd], [winding]
|
||||
kNonSimpleShape = 0b10000000,
|
||||
// TessellateWedgesRenderStep[*] for [Convex], [EvenOdd], [Winding]
|
||||
// TessellateCurvesRenderStep[*] for [EvenOdd], [Winding]
|
||||
// MiddleOutFanRenderStep[*] for [EvenOdd], [Winding]
|
||||
kNonSimpleShape = 1 << 8,
|
||||
|
||||
kLast = kNonSimpleShape,
|
||||
};
|
||||
static constexpr int kDrawTypeFlagsCnt = static_cast<int>(DrawTypeFlags::kLast) + 1;
|
||||
|
||||
} // namespace skgpu::graphite
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "include/gpu/GpuTypes.h"
|
||||
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
|
||||
class SkYUVAInfo;
|
||||
class SkYUVAPixmaps;
|
||||
|
||||
36
gfx/skia/skia/include/gpu/graphite/LogPriority.h
Normal file
36
gfx/skia/skia/include/gpu/graphite/LogPriority.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef skgpu_graphite_LogPriority_DEFINED
|
||||
#define skgpu_graphite_LogPriority_DEFINED
|
||||
|
||||
/**
|
||||
* Note: this file may be included in clients' SkUserConfig.h files, so including any other headers
|
||||
* in this file should be avoided.
|
||||
*/
|
||||
|
||||
namespace skgpu::graphite {
|
||||
/**
|
||||
* SKGPU_GRAPHITE_LOWEST_ACTIVE_LOG_PRIORITY can be defined to one of these values (in
|
||||
* SkUserConfig.h) to control Graphite's logging behavior.
|
||||
*
|
||||
* For example:
|
||||
* ```
|
||||
* #define SKGPU_GRAPHITE_LOWEST_ACTIVE_LOG_PRIORITY skgpu::graphite::LogPriority::kWarning
|
||||
* ```
|
||||
* Would cause Graphite to log warnings, non-fatal errors, and fatal errors.
|
||||
* However, debug logs would be omitted.
|
||||
*/
|
||||
enum class LogPriority : int {
|
||||
kFatal = 0,
|
||||
kError = 1,
|
||||
kWarning = 2,
|
||||
kDebug = 3,
|
||||
};
|
||||
}; // namespace skgpu::graphite
|
||||
|
||||
#endif // skgpu_graphite_LogPriority_DEFINED
|
||||
76
gfx/skia/skia/include/gpu/graphite/PrecompileContext.h
Normal file
76
gfx/skia/skia/include/gpu/graphite/PrecompileContext.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef skgpu_graphite_PrecompileContext_DEFINED
|
||||
#define skgpu_graphite_PrecompileContext_DEFINED
|
||||
|
||||
#include "include/core/SkRefCnt.h"
|
||||
#include "include/private/base/SingleOwner.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
|
||||
class SkData;
|
||||
|
||||
namespace skgpu::graphite {
|
||||
|
||||
class SharedContext;
|
||||
class PrecompileContextPriv;
|
||||
class ResourceProvider;
|
||||
|
||||
class SK_API PrecompileContext {
|
||||
public:
|
||||
~PrecompileContext();
|
||||
|
||||
/**
|
||||
* Purge Pipelines that haven't been used in the past 'msNotUsed' milliseconds
|
||||
* regardless of whether the pipeline cache is under budget.
|
||||
*
|
||||
* @param msNotUsed Pipelines not used in these last milliseconds will be cleaned up.
|
||||
*/
|
||||
void purgePipelinesNotUsedInMs(std::chrono::milliseconds msNotUsed);
|
||||
|
||||
/**
|
||||
* Emit histograms (using the SK_HISTOGRAM* macros) for Skia's Pipeline usage.
|
||||
*/
|
||||
void reportPipelineStats();
|
||||
|
||||
/**
|
||||
* Precompile one specific Pipeline that has been previously serialized. Serialized pipeline
|
||||
* keys can be acquired via the ContextOptions::PipelineCallback.
|
||||
*
|
||||
* @param serializedPipelineKey serialized Pipeline key.
|
||||
* @return true if a Pipeline was created from the key; false otherwise
|
||||
*/
|
||||
bool precompile(sk_sp<SkData> serializedPipelineKey);
|
||||
|
||||
/**
|
||||
* Get a human-readable version of a serialized pipeline key.
|
||||
*
|
||||
* @param serializedPipelineKey serialized Pipeline key.
|
||||
* @return A human-readable version of the provided key; "" on failure.
|
||||
*/
|
||||
std::string getPipelineLabel(sk_sp<SkData> serializedPipelineKey);
|
||||
|
||||
// Provides access to functions that aren't part of the public API.
|
||||
PrecompileContextPriv priv();
|
||||
const PrecompileContextPriv priv() const; // NOLINT(readability-const-return-type)
|
||||
|
||||
private:
|
||||
friend class PrecompileContextPriv;
|
||||
friend class Context; // for ctor
|
||||
|
||||
PrecompileContext(sk_sp<SharedContext>);
|
||||
|
||||
mutable SingleOwner fSingleOwner;
|
||||
sk_sp<SharedContext> fSharedContext;
|
||||
std::unique_ptr<ResourceProvider> fResourceProvider;
|
||||
};
|
||||
|
||||
} // namespace skgpu::graphite
|
||||
|
||||
#endif // skgpu_graphite_PrecompileContext_DEFINED
|
||||
@@ -209,6 +209,12 @@ public:
|
||||
*/
|
||||
size_t maxBudgetedBytes() const;
|
||||
|
||||
/**
|
||||
* Sets the size of Recorders's gpu memory cache budget in bytes. If the new budget is lower
|
||||
* than the current budget, the cache will try to free resources to get under the new budget.
|
||||
*/
|
||||
void setMaxBudgetedBytes(size_t bytes);
|
||||
|
||||
/**
|
||||
* Enumerates all cached GPU resources owned by the Recorder and dumps their memory to
|
||||
* traceMemoryDump.
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#define skgpu_graphite_Recording_DEFINED
|
||||
|
||||
#include "include/core/SkRefCnt.h"
|
||||
#include "include/core/SkSize.h"
|
||||
#include "include/private/base/SkTArray.h"
|
||||
|
||||
#include <memory>
|
||||
@@ -21,6 +22,7 @@ class RefCntedCallback;
|
||||
|
||||
namespace skgpu::graphite {
|
||||
|
||||
class Caps;
|
||||
class CommandBuffer;
|
||||
class RecordingPriv;
|
||||
class Resource;
|
||||
@@ -44,7 +46,7 @@ private:
|
||||
// replay, and it handles the target proxy's instantiation with the provided target.
|
||||
class LazyProxyData {
|
||||
public:
|
||||
LazyProxyData(const TextureInfo&);
|
||||
LazyProxyData(const Caps*, SkISize dimensions, const TextureInfo&);
|
||||
|
||||
TextureProxy* lazyProxy();
|
||||
sk_sp<TextureProxy> refLazyProxy();
|
||||
|
||||
@@ -18,68 +18,113 @@ struct SkISize;
|
||||
|
||||
namespace skgpu::graphite {
|
||||
|
||||
class TextureInfoData;
|
||||
enum class TextureFormat : uint8_t;
|
||||
|
||||
/**
|
||||
* TextureInfo is a backend-agnostic wrapper around the properties of a texture, sans dimensions.
|
||||
* It is designed this way to be compilable w/o bringing in a specific backend's build files, and
|
||||
* without requiring heap allocations of virtual types.
|
||||
*/
|
||||
class SK_API TextureInfo {
|
||||
private:
|
||||
class Data;
|
||||
friend class MtlTextureInfo;
|
||||
friend class DawnTextureInfo;
|
||||
friend class VulkanTextureInfo;
|
||||
|
||||
// Size is the largest of the Data subclasses assuming a 64-bit compiler.
|
||||
inline constexpr static size_t kMaxSubclassSize = 112;
|
||||
using AnyTextureInfoData = SkAnySubclass<Data, kMaxSubclassSize>;
|
||||
|
||||
// Base properties for all backend-specific properties. Clients managing textures directly
|
||||
// should use the public subclasses of Data directly, e.g. MtlTextureInfo/DawnTextureInfo.
|
||||
//
|
||||
// Each backend subclass must expose to TextureInfo[Priv]:
|
||||
// static constexpr BackendApi kBackend;
|
||||
// Protected isProtected() const;
|
||||
// TextureFormat viewFormat() const;
|
||||
// bool serialize(SkWStream*) const;
|
||||
// bool deserialize(SkStream*);
|
||||
class Data {
|
||||
public:
|
||||
TextureInfo();
|
||||
~TextureInfo();
|
||||
virtual ~Data() = default;
|
||||
|
||||
Data(uint32_t sampleCount, skgpu::Mipmapped mipmapped)
|
||||
: fSampleCount(sampleCount)
|
||||
, fMipmapped(mipmapped) {}
|
||||
|
||||
Data() = default;
|
||||
Data(const Data&) = default;
|
||||
|
||||
Data& operator=(const Data&) = default;
|
||||
|
||||
// NOTE: These fields are accessible via the backend-specific subclasses.
|
||||
uint32_t fSampleCount = 1;
|
||||
Mipmapped fMipmapped = Mipmapped::kNo;
|
||||
|
||||
private:
|
||||
friend class TextureInfo;
|
||||
friend class TextureInfoPriv;
|
||||
|
||||
virtual SkString toBackendString() const = 0;
|
||||
|
||||
virtual void copyTo(AnyTextureInfoData&) const = 0;
|
||||
// Passed in TextureInfo will have data of the same backend type and subclass, and
|
||||
// base properties of Data have already been checked for equality/compatibility.
|
||||
virtual bool isCompatible(const TextureInfo& that, bool requireExact) const = 0;
|
||||
};
|
||||
|
||||
public:
|
||||
TextureInfo() = default;
|
||||
~TextureInfo() = default;
|
||||
|
||||
TextureInfo(const TextureInfo&);
|
||||
TextureInfo& operator=(const TextureInfo&);
|
||||
|
||||
bool operator==(const TextureInfo&) const;
|
||||
bool operator==(const TextureInfo& that) const {
|
||||
return this->isCompatible(that, /*requireExact=*/true);
|
||||
}
|
||||
bool operator!=(const TextureInfo& that) const { return !(*this == that); }
|
||||
|
||||
bool isValid() const { return fValid; }
|
||||
BackendApi backend() const { return fBackend; }
|
||||
|
||||
uint32_t numSamples() const { return fSampleCount; }
|
||||
Mipmapped mipmapped() const { return fMipmapped; }
|
||||
Protected isProtected() const { return fProtected; }
|
||||
SkTextureCompressionType compressionType() const;
|
||||
bool isMemoryless() const;
|
||||
|
||||
bool isCompatible(const TextureInfo& that) const;
|
||||
// Return a string containing the full description of this TextureInfo.
|
||||
SkString toString() const;
|
||||
// Return a string containing only the info relevant for its use as a RenderPass attachment.
|
||||
SkString toRPAttachmentString() const;
|
||||
|
||||
private:
|
||||
friend class TextureInfoData;
|
||||
friend class TextureInfoPriv;
|
||||
|
||||
// Size determined by looking at the TextureInfoData subclasses, then guessing-and-checking.
|
||||
// Compiler will complain if this is too small - in that case, just increase the number.
|
||||
inline constexpr static size_t kMaxSubclassSize = 112;
|
||||
using AnyTextureInfoData = SkAnySubclass<TextureInfoData, kMaxSubclassSize>;
|
||||
|
||||
template <typename SomeTextureInfoData>
|
||||
TextureInfo(BackendApi backend,
|
||||
uint32_t sampleCount,
|
||||
skgpu::Mipmapped mipped,
|
||||
skgpu::Protected isProtected,
|
||||
const SomeTextureInfoData& textureInfoData)
|
||||
: fBackend(backend)
|
||||
, fValid(true)
|
||||
, fSampleCount(sampleCount)
|
||||
, fMipmapped(mipped)
|
||||
, fProtected(isProtected) {
|
||||
fTextureInfoData.emplace<SomeTextureInfoData>(textureInfoData);
|
||||
bool isValid() const { return fData.has_value(); }
|
||||
BackendApi backend() const {
|
||||
SkASSERT(fData.has_value() || fBackend == BackendApi::kUnsupported);
|
||||
return fBackend;
|
||||
}
|
||||
|
||||
friend size_t ComputeSize(SkISize dimensions, const TextureInfo&); // for bytesPerPixel
|
||||
uint32_t numSamples() const { return fData.has_value() ? fData->fSampleCount : 1; }
|
||||
Mipmapped mipmapped() const { return fData.has_value() ? fData->fMipmapped : Mipmapped::kNo; }
|
||||
Protected isProtected() const { return fProtected; }
|
||||
|
||||
size_t bytesPerPixel() const;
|
||||
// Return true if `that` describes a texture that is compatible with this info and can validly
|
||||
// be used to fulfill a promise image that was created with this TextureInfo.
|
||||
bool canBeFulfilledBy(const TextureInfo& that) const {
|
||||
return this->isCompatible(that, /*requireExact=*/false);
|
||||
}
|
||||
|
||||
BackendApi fBackend = BackendApi::kMock;
|
||||
bool fValid = false;
|
||||
// Return a string containing the full description of this TextureInfo.
|
||||
SkString toString() const;
|
||||
|
||||
uint32_t fSampleCount = 1;
|
||||
Mipmapped fMipmapped = Mipmapped::kNo;
|
||||
private:
|
||||
friend class TextureInfoPriv;
|
||||
|
||||
template <typename BackendTextureData,
|
||||
std::enable_if_t<std::is_base_of_v<Data, BackendTextureData>, bool> = true>
|
||||
explicit TextureInfo(const BackendTextureData& data)
|
||||
: fBackend(BackendTextureData::kBackend)
|
||||
, fViewFormat(data.viewFormat())
|
||||
, fProtected(data.isProtected()) {
|
||||
fData.emplace<BackendTextureData>(data);
|
||||
}
|
||||
|
||||
bool isCompatible(const TextureInfo& that, bool requireExact) const;
|
||||
|
||||
skgpu::BackendApi fBackend = BackendApi::kUnsupported;
|
||||
AnyTextureInfoData fData;
|
||||
|
||||
// Derived properties from the backend data, cached to avoid a virtual function call
|
||||
TextureFormat fViewFormat;
|
||||
Protected fProtected = Protected::kNo;
|
||||
|
||||
AnyTextureInfoData fTextureInfoData;
|
||||
};
|
||||
|
||||
} // namespace skgpu::graphite
|
||||
|
||||
@@ -37,10 +37,15 @@ public:
|
||||
* by Mipmapped. This will produce an invalid result (return false from isValid()) if the
|
||||
* passed formats' channels don't agree with SkYUVAInfo.
|
||||
*/
|
||||
YUVABackendTextureInfo(const Recorder*,
|
||||
const SkYUVAInfo&,
|
||||
YUVABackendTextureInfo(const SkYUVAInfo&,
|
||||
SkSpan<const TextureInfo>,
|
||||
Mipmapped);
|
||||
// DEPRECATED: No more need for a Recorder to construct YUVABackendTextureInfo
|
||||
YUVABackendTextureInfo(Recorder*,
|
||||
const SkYUVAInfo& yuvaInfo,
|
||||
SkSpan<const TextureInfo> textures,
|
||||
Mipmapped mipmapped)
|
||||
: YUVABackendTextureInfo(yuvaInfo, textures, mipmapped) {}
|
||||
|
||||
bool operator==(const YUVABackendTextureInfo&) const;
|
||||
bool operator!=(const YUVABackendTextureInfo& that) const { return !(*this == that); }
|
||||
@@ -95,9 +100,13 @@ public:
|
||||
* indicated by the SkYUVAInfo. This will produce an invalid result (return false from
|
||||
* isValid()) if the passed texture formats' channels don't agree with SkYUVAInfo.
|
||||
*/
|
||||
YUVABackendTextures(const Recorder*,
|
||||
const SkYUVAInfo&,
|
||||
YUVABackendTextures(const SkYUVAInfo&,
|
||||
SkSpan<const BackendTexture>);
|
||||
// DEPRECATED: No more need for a Recorder to construct YUVABackendTextureInfo
|
||||
YUVABackendTextures(Recorder*,
|
||||
const SkYUVAInfo& yuvaInfo,
|
||||
SkSpan<const BackendTexture> textures)
|
||||
: YUVABackendTextures(yuvaInfo, textures) {}
|
||||
|
||||
SkSpan<const BackendTexture> planeTextures() const {
|
||||
return SkSpan<const BackendTexture>(fPlaneTextures);
|
||||
|
||||
@@ -11,8 +11,13 @@
|
||||
#include "include/core/SkTypes.h"
|
||||
#include "webgpu/webgpu_cpp.h" // NO_G3_REWRITE
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace skgpu::graphite {
|
||||
|
||||
class Context;
|
||||
struct ContextOptions;
|
||||
|
||||
/**
|
||||
* WebGPU needs to allow the main thread loop to run to detect GPU progress. Dawn native has a
|
||||
* function wgpu::Instance::ProcessEvents, not (currently) present in WebGPU, that can be used to
|
||||
@@ -63,6 +68,10 @@ struct SK_API DawnBackendContext {
|
||||
#endif
|
||||
};
|
||||
|
||||
namespace ContextFactory {
|
||||
SK_API std::unique_ptr<Context> MakeDawn(const DawnBackendContext&, const ContextOptions&);
|
||||
} // namespace ContextFactory
|
||||
|
||||
} // namespace skgpu::graphite
|
||||
|
||||
#endif // skgpu_graphite_DawnBackendContext_DEFINED
|
||||
|
||||
166
gfx/skia/skia/include/gpu/graphite/dawn/DawnGraphiteTypes.h
Normal file
166
gfx/skia/skia/include/gpu/graphite/dawn/DawnGraphiteTypes.h
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright 2025 Google LLC
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef skgpu_graphite_DawnTypes_DEFINED
|
||||
#define skgpu_graphite_DawnTypes_DEFINED
|
||||
|
||||
#include "include/core/SkSize.h"
|
||||
#include "include/gpu/graphite/GraphiteTypes.h"
|
||||
#include "include/gpu/graphite/TextureInfo.h"
|
||||
#include "include/private/base/SkAPI.h"
|
||||
|
||||
#include "webgpu/webgpu_cpp.h" // NO_G3_REWRITE
|
||||
|
||||
class SkStream;
|
||||
class SkWStream;
|
||||
|
||||
namespace skgpu::graphite {
|
||||
class BackendTexture;
|
||||
|
||||
class SK_API DawnTextureInfo final : public TextureInfo::Data {
|
||||
public:
|
||||
// wgpu::TextureDescriptor properties
|
||||
wgpu::TextureFormat fFormat = wgpu::TextureFormat::Undefined;
|
||||
// `fViewFormat` for multiplanar formats corresponds to the plane TextureView's format.
|
||||
wgpu::TextureFormat fViewFormat = wgpu::TextureFormat::Undefined;
|
||||
wgpu::TextureUsage fUsage = wgpu::TextureUsage::None;
|
||||
// TODO(b/308944094): Migrate aspect information to BackendTextureViews.
|
||||
wgpu::TextureAspect fAspect = wgpu::TextureAspect::All;
|
||||
uint32_t fSlice = 0;
|
||||
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
// The descriptor of the YCbCr info (if any) for this texture. Dawn's YCbCr
|
||||
// sampling will be used for this texture if this info is set. Setting the
|
||||
// info is supported only on Android and only if using Vulkan as the
|
||||
// underlying GPU driver.
|
||||
wgpu::YCbCrVkDescriptor fYcbcrVkDescriptor = {};
|
||||
#endif
|
||||
|
||||
wgpu::TextureFormat getViewFormat() const {
|
||||
return fViewFormat != wgpu::TextureFormat::Undefined ? fViewFormat : fFormat;
|
||||
}
|
||||
|
||||
DawnTextureInfo() = default;
|
||||
|
||||
DawnTextureInfo(WGPUTexture texture);
|
||||
|
||||
DawnTextureInfo(uint32_t sampleCount,
|
||||
Mipmapped mipmapped,
|
||||
wgpu::TextureFormat format,
|
||||
wgpu::TextureUsage usage,
|
||||
wgpu::TextureAspect aspect)
|
||||
: DawnTextureInfo(sampleCount,
|
||||
mipmapped,
|
||||
/*format=*/format,
|
||||
/*viewFormat=*/format,
|
||||
usage,
|
||||
aspect,
|
||||
/*slice=*/0) {}
|
||||
|
||||
DawnTextureInfo(uint32_t sampleCount,
|
||||
Mipmapped mipmapped,
|
||||
wgpu::TextureFormat format,
|
||||
wgpu::TextureFormat viewFormat,
|
||||
wgpu::TextureUsage usage,
|
||||
wgpu::TextureAspect aspect,
|
||||
uint32_t slice)
|
||||
: Data(sampleCount, mipmapped)
|
||||
, fFormat(format)
|
||||
, fViewFormat(viewFormat)
|
||||
, fUsage(usage)
|
||||
, fAspect(aspect)
|
||||
, fSlice(slice) {}
|
||||
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
DawnTextureInfo(uint32_t sampleCount,
|
||||
Mipmapped mipmapped,
|
||||
wgpu::TextureFormat format,
|
||||
wgpu::TextureFormat viewFormat,
|
||||
wgpu::TextureUsage usage,
|
||||
wgpu::TextureAspect aspect,
|
||||
uint32_t slice,
|
||||
wgpu::YCbCrVkDescriptor ycbcrVkDescriptor)
|
||||
: Data(sampleCount, mipmapped)
|
||||
, fFormat(format)
|
||||
, fViewFormat(viewFormat)
|
||||
, fUsage(usage)
|
||||
, fAspect(aspect)
|
||||
, fSlice(slice)
|
||||
, fYcbcrVkDescriptor(ycbcrVkDescriptor) {}
|
||||
#endif
|
||||
|
||||
private:
|
||||
friend class TextureInfo;
|
||||
friend class TextureInfoPriv;
|
||||
|
||||
// Non-virtual template API for TextureInfo::Data accessed directly when backend type is known.
|
||||
static constexpr skgpu::BackendApi kBackend = skgpu::BackendApi::kDawn;
|
||||
|
||||
Protected isProtected() const { return Protected::kNo; }
|
||||
TextureFormat viewFormat() const;
|
||||
|
||||
bool serialize(SkWStream*) const;
|
||||
bool deserialize(SkStream*);
|
||||
|
||||
// Virtual API when the specific backend type is not available.
|
||||
SkString toBackendString() const override;
|
||||
|
||||
void copyTo(TextureInfo::AnyTextureInfoData& dstData) const override {
|
||||
dstData.emplace<DawnTextureInfo>(*this);
|
||||
}
|
||||
bool isCompatible(const TextureInfo& that, bool requireExact) const override;
|
||||
};
|
||||
|
||||
namespace TextureInfos {
|
||||
SK_API TextureInfo MakeDawn(const DawnTextureInfo& dawnInfo);
|
||||
|
||||
SK_API bool GetDawnTextureInfo(const TextureInfo&, DawnTextureInfo*);
|
||||
} // namespace TextureInfos
|
||||
|
||||
namespace BackendTextures {
|
||||
// Create a BackendTexture from a WGPUTexture. Texture info will be queried from the texture.
|
||||
//
|
||||
// This is the recommended way of specifying a BackendTexture for Dawn. See the note below on
|
||||
// the constructor that takes a WGPUTextureView for a fuller explanation.
|
||||
//
|
||||
// The BackendTexture will not call retain or release on the passed in WGPUTexture. Thus, the
|
||||
// client must keep the WGPUTexture valid until they are no longer using the BackendTexture.
|
||||
// However, any SkImage or SkSurface that wraps the BackendTexture *will* retain and release
|
||||
// the WGPUTexture.
|
||||
SK_API BackendTexture MakeDawn(WGPUTexture);
|
||||
|
||||
// Create a BackendTexture from a WGPUTexture. Texture planeDimensions, plane aspect and
|
||||
// info have to be provided. This is intended to be used only when accessing a plane
|
||||
// of a WGPUTexture.
|
||||
//
|
||||
// The BackendTexture will not call retain or release on the passed in WGPUTexture. Thus, the
|
||||
// client must keep the WGPUTexture valid until they are no longer using the BackendTexture.
|
||||
// However, any SkImage or SkSurface that wraps the BackendTexture *will* retain and release
|
||||
// the WGPUTexture.
|
||||
SK_API BackendTexture MakeDawn(SkISize planeDimensions, const DawnTextureInfo&, WGPUTexture);
|
||||
|
||||
// Create a BackendTexture from a WGPUTextureView. Texture dimensions and
|
||||
// info have to be provided.
|
||||
//
|
||||
// Using a WGPUTextureView rather than a WGPUTexture is less effecient for operations that
|
||||
// require buffer transfers to or from the texture (e.g. methods on graphite::Context that read
|
||||
// pixels or SkSurface::writePixels). In such cases an intermediate copy to or from a
|
||||
// WGPUTexture is required. Thus, it is recommended to use this functionality only for cases
|
||||
// where a WGPUTexture is unavailable, in particular when using wgpu::SwapChain.
|
||||
//
|
||||
// The BackendTexture will not call retain or release on the passed in WGPUTextureView. Thus,
|
||||
// the client must keep the WGPUTextureView valid until they are no longer using the
|
||||
// BackendTexture. However, any SkImage or SkSurface that wraps the BackendTexture *will* retain
|
||||
// and release the WGPUTextureView.
|
||||
SK_API BackendTexture MakeDawn(SkISize dimensions,
|
||||
const DawnTextureInfo& info,
|
||||
WGPUTextureView textureView);
|
||||
} // namespace BackendTextures
|
||||
|
||||
} // namespace skgpu::graphite
|
||||
|
||||
#endif // skgpu_graphite_DawnTypes_DEFINED
|
||||
@@ -1,147 +1,9 @@
|
||||
/*
|
||||
* Copyright 2022 Google LLC.
|
||||
* Copyright 2022 Google LLC
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef skgpu_graphite_DawnTypes_DEFINED
|
||||
#define skgpu_graphite_DawnTypes_DEFINED
|
||||
|
||||
#include "include/core/SkSize.h"
|
||||
#include "include/gpu/graphite/GraphiteTypes.h"
|
||||
#include "include/private/base/SkAPI.h"
|
||||
|
||||
#include "webgpu/webgpu_cpp.h" // NO_G3_REWRITE
|
||||
|
||||
namespace skgpu::graphite {
|
||||
class BackendTexture;
|
||||
class TextureInfo;
|
||||
|
||||
struct DawnTextureInfo {
|
||||
uint32_t fSampleCount = 1;
|
||||
Mipmapped fMipmapped = Mipmapped::kNo;
|
||||
|
||||
// wgpu::TextureDescriptor properties
|
||||
wgpu::TextureFormat fFormat = wgpu::TextureFormat::Undefined;
|
||||
// `fViewFormat` for multiplanar formats corresponds to the plane TextureView's format.
|
||||
wgpu::TextureFormat fViewFormat = wgpu::TextureFormat::Undefined;
|
||||
wgpu::TextureUsage fUsage = wgpu::TextureUsage::None;
|
||||
// TODO(b/308944094): Migrate aspect information to BackendTextureViews.
|
||||
wgpu::TextureAspect fAspect = wgpu::TextureAspect::All;
|
||||
uint32_t fSlice = 0;
|
||||
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
// The descriptor of the YCbCr info (if any) for this texture. Dawn's YCbCr
|
||||
// sampling will be used for this texture if this info is set. Setting the
|
||||
// info is supported only on Android and only if using Vulkan as the
|
||||
// underlying GPU driver.
|
||||
wgpu::YCbCrVkDescriptor fYcbcrVkDescriptor = {};
|
||||
#endif
|
||||
|
||||
wgpu::TextureFormat getViewFormat() const {
|
||||
return fViewFormat != wgpu::TextureFormat::Undefined ? fViewFormat : fFormat;
|
||||
}
|
||||
|
||||
DawnTextureInfo() = default;
|
||||
|
||||
DawnTextureInfo(uint32_t sampleCount,
|
||||
Mipmapped mipmapped,
|
||||
wgpu::TextureFormat format,
|
||||
wgpu::TextureUsage usage,
|
||||
wgpu::TextureAspect aspect)
|
||||
: DawnTextureInfo(sampleCount,
|
||||
mipmapped,
|
||||
/*format=*/format,
|
||||
/*viewFormat=*/format,
|
||||
usage,
|
||||
aspect,
|
||||
/*slice=*/0) {}
|
||||
|
||||
DawnTextureInfo(uint32_t sampleCount,
|
||||
Mipmapped mipmapped,
|
||||
wgpu::TextureFormat format,
|
||||
wgpu::TextureFormat viewFormat,
|
||||
wgpu::TextureUsage usage,
|
||||
wgpu::TextureAspect aspect,
|
||||
uint32_t slice)
|
||||
: fSampleCount(sampleCount)
|
||||
, fMipmapped(mipmapped)
|
||||
, fFormat(format)
|
||||
, fViewFormat(viewFormat)
|
||||
, fUsage(usage)
|
||||
, fAspect(aspect)
|
||||
, fSlice(slice) {}
|
||||
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
DawnTextureInfo(uint32_t sampleCount,
|
||||
Mipmapped mipmapped,
|
||||
wgpu::TextureFormat format,
|
||||
wgpu::TextureFormat viewFormat,
|
||||
wgpu::TextureUsage usage,
|
||||
wgpu::TextureAspect aspect,
|
||||
uint32_t slice,
|
||||
wgpu::YCbCrVkDescriptor ycbcrVkDescriptor)
|
||||
: fSampleCount(sampleCount)
|
||||
, fMipmapped(mipmapped)
|
||||
, fFormat(format)
|
||||
, fViewFormat(viewFormat)
|
||||
, fUsage(usage)
|
||||
, fAspect(aspect)
|
||||
, fSlice(slice)
|
||||
, fYcbcrVkDescriptor(ycbcrVkDescriptor) {}
|
||||
#endif
|
||||
};
|
||||
|
||||
namespace TextureInfos {
|
||||
SK_API TextureInfo MakeDawn(const DawnTextureInfo& dawnInfo);
|
||||
|
||||
SK_API bool GetDawnTextureInfo(const TextureInfo&, DawnTextureInfo*);
|
||||
} // namespace TextureInfos
|
||||
|
||||
namespace BackendTextures {
|
||||
// Create a BackendTexture from a WGPUTexture. Texture info will be queried from the texture.
|
||||
//
|
||||
// This is the recommended way of specifying a BackendTexture for Dawn. See the note below on
|
||||
// the constructor that takes a WGPUTextureView for a fuller explanation.
|
||||
//
|
||||
// The BackendTexture will not call retain or release on the passed in WGPUTexture. Thus, the
|
||||
// client must keep the WGPUTexture valid until they are no longer using the BackendTexture.
|
||||
// However, any SkImage or SkSurface that wraps the BackendTexture *will* retain and release
|
||||
// the WGPUTexture.
|
||||
SK_API BackendTexture MakeDawn(WGPUTexture);
|
||||
|
||||
// Create a BackendTexture from a WGPUTexture. Texture planeDimensions, plane aspect and
|
||||
// info have to be provided. This is intended to be used only when accessing a plane
|
||||
// of a WGPUTexture.
|
||||
//
|
||||
// The BackendTexture will not call retain or release on the passed in WGPUTexture. Thus, the
|
||||
// client must keep the WGPUTexture valid until they are no longer using the BackendTexture.
|
||||
// However, any SkImage or SkSurface that wraps the BackendTexture *will* retain and release
|
||||
// the WGPUTexture.
|
||||
SK_API BackendTexture MakeDawn(SkISize planeDimensions, const DawnTextureInfo&, WGPUTexture);
|
||||
|
||||
// Create a BackendTexture from a WGPUTextureView. Texture dimensions and
|
||||
// info have to be provided.
|
||||
//
|
||||
// Using a WGPUTextureView rather than a WGPUTexture is less effecient for operations that
|
||||
// require buffer transfers to or from the texture (e.g. methods on graphite::Context that read
|
||||
// pixels or SkSurface::writePixels). In such cases an intermediate copy to or from a
|
||||
// WGPUTexture is required. Thus, it is recommended to use this functionality only for cases
|
||||
// where a WGPUTexture is unavailable, in particular when using wgpu::SwapChain.
|
||||
//
|
||||
// The BackendTexture will not call retain or release on the passed in WGPUTextureView. Thus,
|
||||
// the client must keep the WGPUTextureView valid until they are no longer using the
|
||||
// BackendTexture. However, any SkImage or SkSurface that wraps the BackendTexture *will* retain
|
||||
// and release the WGPUTextureView.
|
||||
SK_API BackendTexture MakeDawn(SkISize dimensions,
|
||||
const DawnTextureInfo& info,
|
||||
WGPUTextureView textureView);
|
||||
|
||||
} // namespace BackendTextures
|
||||
|
||||
} // namespace skgpu::graphite
|
||||
|
||||
#endif // skgpu_graphite_DawnTypes_DEFINED
|
||||
|
||||
|
||||
// DEPRECRATED: DawnTypes.h will be removed in the future, please include DawnGraphiteTypes.h
|
||||
#include "include/gpu/graphite/dawn/DawnGraphiteTypes.h"
|
||||
|
||||
@@ -5,24 +5,5 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef skgpu_graphite_DawnUtils_DEFINED
|
||||
#define skgpu_graphite_DawnUtils_DEFINED
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "include/private/base/SkAPI.h"
|
||||
|
||||
namespace skgpu::graphite {
|
||||
|
||||
class Context;
|
||||
struct ContextOptions;
|
||||
struct DawnBackendContext;
|
||||
|
||||
namespace ContextFactory {
|
||||
SK_API std::unique_ptr<Context> MakeDawn(const DawnBackendContext&, const ContextOptions&);
|
||||
} // namespace ContextFactory
|
||||
|
||||
} // namespace skgpu::graphite
|
||||
|
||||
|
||||
#endif // skgpu_graphite_DawnUtils_DEFINED
|
||||
// DEPRECRATED: DawnUtils.h will be removed in the future, please include DawnBackendContext.h
|
||||
#include "include/gpu/graphite/dawn/DawnBackendContext.h"
|
||||
|
||||
@@ -23,6 +23,10 @@ struct SK_API MtlBackendContext {
|
||||
sk_cfp<CFTypeRef> fQueue;
|
||||
};
|
||||
|
||||
namespace ContextFactory {
|
||||
SK_API std::unique_ptr<Context> MakeMetal(const MtlBackendContext&, const ContextOptions&);
|
||||
} // namespace ContextFactory
|
||||
|
||||
} // namespace skgpu::graphite
|
||||
|
||||
#endif // skgpu_graphite_MtlBackendContext_DEFINED
|
||||
|
||||
@@ -8,31 +8,26 @@
|
||||
#ifndef skgpu_graphite_MtlGraphiteTypes_DEFINED
|
||||
#define skgpu_graphite_MtlGraphiteTypes_DEFINED
|
||||
|
||||
#if __OBJC__ // <Metal/Metal.h> only works when compiled for Objective C
|
||||
|
||||
#include "include/core/SkTypes.h"
|
||||
|
||||
#if __OBJC__ // <Metal/Metal.h> only works when compiled for Objective C
|
||||
#include "include/gpu/graphite/BackendTexture.h"
|
||||
#include "include/gpu/graphite/GraphiteTypes.h"
|
||||
#include "include/gpu/graphite/TextureInfo.h"
|
||||
#include "include/ports/SkCFObject.h"
|
||||
#include "include/gpu/graphite/mtl/MtlGraphiteTypes_cpp.h"
|
||||
#include "include/private/base/SkAPI.h"
|
||||
|
||||
#import <CoreFoundation/CoreFoundation.h>
|
||||
#import <Metal/Metal.h>
|
||||
#import <TargetConditionals.h>
|
||||
|
||||
#if TARGET_OS_SIMULATOR
|
||||
#define SK_API_AVAILABLE_CA_METAL_LAYER SK_API_AVAILABLE(macos(10.11), ios(13.0), tvos(13.0))
|
||||
#else // TARGET_OS_SIMULATOR
|
||||
#define SK_API_AVAILABLE_CA_METAL_LAYER SK_API_AVAILABLE(macos(10.11), ios(8.0), tvos(9.0))
|
||||
#endif // TARGET_OS_SIMULATOR
|
||||
class SkStream;
|
||||
class SkWStream;
|
||||
|
||||
namespace skgpu::graphite {
|
||||
|
||||
struct SK_API MtlTextureInfo {
|
||||
uint32_t fSampleCount = 1;
|
||||
skgpu::Mipmapped fMipmapped = skgpu::Mipmapped::kNo;
|
||||
|
||||
class SK_API MtlTextureInfo final : public TextureInfo::Data {
|
||||
public:
|
||||
MTLPixelFormat fFormat = MTLPixelFormatInvalid;
|
||||
MTLTextureUsage fUsage = MTLTextureUsageUnknown;
|
||||
MTLStorageMode fStorageMode = MTLStorageModeShared;
|
||||
@@ -46,37 +41,34 @@ struct SK_API MtlTextureInfo {
|
||||
MTLTextureUsage usage,
|
||||
MTLStorageMode storageMode,
|
||||
bool framebufferOnly)
|
||||
: fSampleCount(sampleCount)
|
||||
, fMipmapped(mipmapped)
|
||||
: Data(sampleCount, mipmapped)
|
||||
, fFormat(format)
|
||||
, fUsage(usage)
|
||||
, fStorageMode(storageMode)
|
||||
, fFramebufferOnly(framebufferOnly) {}
|
||||
|
||||
private:
|
||||
friend class TextureInfo;
|
||||
friend class TextureInfoPriv;
|
||||
|
||||
// Non-virtual template API for TextureInfo::Data accessed directly when backend type is known.
|
||||
static constexpr skgpu::BackendApi kBackend = skgpu::BackendApi::kMetal;
|
||||
|
||||
Protected isProtected() const { return Protected::kNo; }
|
||||
TextureFormat viewFormat() const;
|
||||
|
||||
bool serialize(SkWStream*) const;
|
||||
bool deserialize(SkStream*);
|
||||
|
||||
// Virtual API when the specific backend type is not available.
|
||||
SkString toBackendString() const override;
|
||||
|
||||
void copyTo(TextureInfo::AnyTextureInfoData& dstData) const override {
|
||||
dstData.emplace<MtlTextureInfo>(*this);
|
||||
}
|
||||
bool isCompatible(const TextureInfo& that, bool requireExact) const override;
|
||||
};
|
||||
|
||||
namespace TextureInfos {
|
||||
SK_API TextureInfo MakeMetal(const MtlTextureInfo&);
|
||||
SK_API TextureInfo MakeMetal(CFTypeRef mtlTexture);
|
||||
|
||||
SK_API bool GetMtlTextureInfo(const TextureInfo&, MtlTextureInfo*);
|
||||
} // namespace TextureInfos
|
||||
|
||||
namespace BackendTextures {
|
||||
// The BackendTexture will not call retain or release on the passed in CFTypeRef. Thus the
|
||||
// client must keep the CFTypeRef valid until they are no longer using the BackendTexture.
|
||||
SK_API BackendTexture MakeMetal(SkISize dimensions, CFTypeRef mtlTexture);
|
||||
|
||||
SK_API CFTypeRef GetMtlTexture(const BackendTexture&);
|
||||
} // namespace BackendTextures
|
||||
|
||||
namespace BackendSemaphores {
|
||||
// TODO(b/286088355) Determine creator's responsibility for setting refcnt.
|
||||
SK_API BackendSemaphore MakeMetal(CFTypeRef mtlEvent, uint64_t value);
|
||||
|
||||
SK_API CFTypeRef GetMtlEvent(const BackendSemaphore&);
|
||||
SK_API uint64_t GetMtlValue(const BackendSemaphore&);
|
||||
} // namespace BackendSemaphores
|
||||
|
||||
} // namespace skgpu::graphite
|
||||
|
||||
#endif // __OBJC__
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Copyright 2022 Google LLC
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
// DEPRECRATED: MtlGraphiteTypesUtils.h will be removed in the future, please include
|
||||
// MtlGraphiteTypes_cpp.h
|
||||
#include "include/gpu/graphite/mtl/MtlGraphiteTypes_cpp.h"
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef skgpu_graphite_MtlGraphiteTypesUtils_DEFINED
|
||||
#define skgpu_graphite_MtlGraphiteTypesUtils_DEFINED
|
||||
|
||||
#include "include/core/SkTypes.h"
|
||||
|
||||
#include "include/gpu/graphite/BackendTexture.h"
|
||||
#include "include/gpu/graphite/TextureInfo.h"
|
||||
#include "include/private/base/SkAPI.h"
|
||||
|
||||
#import <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
namespace skgpu::graphite {
|
||||
|
||||
// MtlTextureInfo requires compiling with Objective-C, so this header is split to allow invoking
|
||||
// the various backend wrapping APIs from a C++-only compilation unit.
|
||||
class SK_API MtlTextureInfo;
|
||||
|
||||
namespace TextureInfos {
|
||||
SK_API TextureInfo MakeMetal(const MtlTextureInfo&);
|
||||
SK_API TextureInfo MakeMetal(CFTypeRef mtlTexture);
|
||||
|
||||
SK_API bool GetMtlTextureInfo(const TextureInfo&, MtlTextureInfo*);
|
||||
} // namespace TextureInfos
|
||||
|
||||
namespace BackendTextures {
|
||||
// The BackendTexture will not call retain or release on the passed in CFTypeRef. Thus the
|
||||
// client must keep the CFTypeRef valid until they are no longer using the BackendTexture.
|
||||
SK_API BackendTexture MakeMetal(SkISize dimensions, CFTypeRef mtlTexture);
|
||||
|
||||
SK_API CFTypeRef GetMtlTexture(const BackendTexture&);
|
||||
} // namespace BackendTextures
|
||||
|
||||
namespace BackendSemaphores {
|
||||
// TODO(b/286088355) Determine creator's responsibility for setting refcnt.
|
||||
SK_API BackendSemaphore MakeMetal(CFTypeRef mtlEvent, uint64_t value);
|
||||
|
||||
SK_API CFTypeRef GetMtlEvent(const BackendSemaphore&);
|
||||
SK_API uint64_t GetMtlValue(const BackendSemaphore&);
|
||||
} // namespace BackendSemaphores
|
||||
|
||||
} // namespace skgpu::graphite
|
||||
|
||||
#endif // skgpu_graphite_MtlGraphiteTypesUtils_DEFINED
|
||||
@@ -5,23 +5,5 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef skgpu_graphite_MtlGraphiteUtils_DEFINED
|
||||
#define skgpu_graphite_MtlGraphiteUtils_DEFINED
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "include/private/base/SkAPI.h"
|
||||
|
||||
namespace skgpu::graphite {
|
||||
|
||||
class Context;
|
||||
struct ContextOptions;
|
||||
struct MtlBackendContext;
|
||||
|
||||
namespace ContextFactory {
|
||||
SK_API std::unique_ptr<Context> MakeMetal(const MtlBackendContext&, const ContextOptions&);
|
||||
} // namespace ContextFactory
|
||||
|
||||
} // namespace skgpu::graphite
|
||||
|
||||
#endif // skgpu_graphite_MtlGraphiteUtils_DEFINED
|
||||
// DEPRECRATED: MtlGraphiteUtils.h will be removed in the future, please include MtlBackendContext.h
|
||||
#include "include/gpu/graphite/mtl/MtlBackendContext.h"
|
||||
|
||||
@@ -8,13 +8,15 @@
|
||||
#ifndef skgpu_graphite_precompile_Precompile_DEFINED
|
||||
#define skgpu_graphite_precompile_Precompile_DEFINED
|
||||
|
||||
#include "include/core/SkColorSpace.h"
|
||||
#include "include/core/SkColorType.h"
|
||||
#include "include/core/SkSpan.h"
|
||||
#include "include/gpu/graphite/GraphiteTypes.h"
|
||||
|
||||
namespace skgpu::graphite {
|
||||
|
||||
class Context;
|
||||
class PaintOptions;
|
||||
class PrecompileContext;
|
||||
|
||||
/**
|
||||
* Describes the required properties of a RenderPass that will be combined with the
|
||||
@@ -22,8 +24,17 @@ class PaintOptions;
|
||||
* a pipeline.
|
||||
*/
|
||||
struct SK_API RenderPassProperties {
|
||||
bool operator==(const RenderPassProperties& other) const {
|
||||
return fDSFlags == other.fDSFlags &&
|
||||
fDstCT == other.fDstCT &&
|
||||
fRequiresMSAA == other.fRequiresMSAA &&
|
||||
SkColorSpace::Equals(fDstCS.get(), other.fDstCS.get());
|
||||
}
|
||||
bool operator!= (const RenderPassProperties& other) const { return !(*this == other); }
|
||||
|
||||
DepthStencilFlags fDSFlags = DepthStencilFlags::kNone;
|
||||
SkColorType fDstCT = kRGBA_8888_SkColorType;
|
||||
sk_sp<SkColorSpace> fDstCS = nullptr;
|
||||
bool fRequiresMSAA = false;
|
||||
};
|
||||
|
||||
@@ -33,12 +44,12 @@ struct SK_API RenderPassProperties {
|
||||
* drawing. Graphite will always be able to perform an inline compilation if some SkPaint
|
||||
* combination was omitted from precompilation.
|
||||
*
|
||||
* @param context the Context to which the actual draws will be submitted
|
||||
* @param precompileContext thread-safe helper holding required portions of the Context
|
||||
* @param paintOptions captures a set of SkPaints that will be drawn
|
||||
* @param drawTypes communicates which primitives those paints will be drawn with
|
||||
* @param renderPassProperties describes the RenderPasses needed for the desired Pipelines
|
||||
*/
|
||||
void SK_API Precompile(Context* context,
|
||||
void SK_API Precompile(PrecompileContext* precompileContext,
|
||||
const PaintOptions& paintOptions,
|
||||
DrawTypeFlags drawTypes,
|
||||
SkSpan<const RenderPassProperties> renderPassProperties);
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include "include/gpu/graphite/precompile/PrecompileBase.h"
|
||||
|
||||
#include "include/core/SkBlendMode.h"
|
||||
#include "include/core/SkImageInfo.h"
|
||||
#include "include/effects/SkGradientShader.h"
|
||||
|
||||
class SkColorSpace;
|
||||
|
||||
@@ -109,11 +111,13 @@ namespace PrecompileShaders {
|
||||
// time this entry point allows the equivalent precompilation program structure to be created.
|
||||
// Note that this factory is for non-YUV SkImages, the YUVImage factory (below) should be used
|
||||
// to represent the shading and sampling required for YUV images.
|
||||
SK_API sk_sp<PrecompileShader> Image();
|
||||
SK_API sk_sp<PrecompileShader> Image(SkSpan<const SkColorInfo> = {},
|
||||
SkSpan<const SkTileMode> = {});
|
||||
// As with the above Image call, raw ImageShaders are usually created via an
|
||||
// SkImage::makeRawShader call. The RawImage call allows the equivalent precompilation
|
||||
// program structure to be created without needing the SkImage.
|
||||
SK_API sk_sp<PrecompileShader> RawImage();
|
||||
SK_API sk_sp<PrecompileShader> RawImage(SkSpan<const SkColorInfo> = {},
|
||||
SkSpan<const SkTileMode> = {});
|
||||
|
||||
// In the main Skia API, the specifics of the SkImage used for the SkImage::makeShader call
|
||||
// can determine whether normal or YUV sampling is required. This entry point allows clients
|
||||
@@ -126,10 +130,14 @@ namespace PrecompileShaders {
|
||||
SK_API sk_sp<PrecompileShader> MakeTurbulence();
|
||||
|
||||
// --- This block of four matches all the factories in SkGradientShader (SkGradientShader.h)
|
||||
SK_API sk_sp<PrecompileShader> LinearGradient();
|
||||
SK_API sk_sp<PrecompileShader> RadialGradient();
|
||||
SK_API sk_sp<PrecompileShader> TwoPointConicalGradient();
|
||||
SK_API sk_sp<PrecompileShader> SweepGradient();
|
||||
SK_API sk_sp<PrecompileShader> LinearGradient(
|
||||
SkGradientShader::Interpolation = SkGradientShader::Interpolation());
|
||||
SK_API sk_sp<PrecompileShader> RadialGradient(
|
||||
SkGradientShader::Interpolation = SkGradientShader::Interpolation());
|
||||
SK_API sk_sp<PrecompileShader> TwoPointConicalGradient(
|
||||
SkGradientShader::Interpolation = SkGradientShader::Interpolation());
|
||||
SK_API sk_sp<PrecompileShader> SweepGradient(
|
||||
SkGradientShader::Interpolation = SkGradientShader::Interpolation());
|
||||
|
||||
// Normally, SkPicture shaders are only created via SkPicture::makeShader. Since the
|
||||
// SkPicture to be drawn, most likely, won't be available at precompilation time, this
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2022 Google LLC
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef skgpu_graphite_VulkanGraphiteUtils_DEFINED
|
||||
#define skgpu_graphite_VulkanGraphiteUtils_DEFINED
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "include/private/base/SkAPI.h"
|
||||
|
||||
// NOTE: Because there is already a VulkanBackendContext header shared between Ganesh and Graphite,
|
||||
// this is named VulkanGraphiteContext.h to supply just the factory function (differing from the
|
||||
// public headers exposed by other backends).
|
||||
namespace skgpu { struct VulkanBackendContext; }
|
||||
|
||||
namespace skgpu::graphite {
|
||||
|
||||
class Context;
|
||||
struct ContextOptions;
|
||||
|
||||
namespace ContextFactory {
|
||||
SK_API std::unique_ptr<Context> MakeVulkan(const VulkanBackendContext&, const ContextOptions&);
|
||||
} // namespace ContextFactory
|
||||
|
||||
} // namespace skgpu::graphite
|
||||
|
||||
#endif // skgpu_graphite_VulkanGraphiteUtils_DEFINED
|
||||
@@ -13,12 +13,13 @@
|
||||
#include "include/gpu/graphite/TextureInfo.h"
|
||||
#include "include/gpu/vk/VulkanTypes.h"
|
||||
|
||||
class SkStream;
|
||||
class SkWStream;
|
||||
|
||||
namespace skgpu::graphite {
|
||||
|
||||
struct VulkanTextureInfo {
|
||||
uint32_t fSampleCount = 1;
|
||||
Mipmapped fMipmapped = Mipmapped::kNo;
|
||||
|
||||
class SK_API VulkanTextureInfo final : public TextureInfo::Data {
|
||||
public:
|
||||
// VkImageCreateInfo properties
|
||||
// Currently the only supported flag is VK_IMAGE_CREATE_PROTECTED_BIT. Any other flag will not
|
||||
// be accepted
|
||||
@@ -48,8 +49,7 @@ struct VulkanTextureInfo {
|
||||
VkSharingMode sharingMode,
|
||||
VkImageAspectFlags aspectMask,
|
||||
VulkanYcbcrConversionInfo ycbcrConversionInfo)
|
||||
: fSampleCount(sampleCount)
|
||||
, fMipmapped(mipmapped)
|
||||
: Data(sampleCount, mipmapped)
|
||||
, fFlags(flags)
|
||||
, fFormat(format)
|
||||
, fImageTiling(imageTiling)
|
||||
@@ -57,6 +57,29 @@ struct VulkanTextureInfo {
|
||||
, fSharingMode(sharingMode)
|
||||
, fAspectMask(aspectMask)
|
||||
, fYcbcrConversionInfo(ycbcrConversionInfo) {}
|
||||
|
||||
private:
|
||||
friend class TextureInfo;
|
||||
friend class TextureInfoPriv;
|
||||
|
||||
// Non-virtual template API for TextureInfo::Data accessed directly when backend type is known.
|
||||
static constexpr skgpu::BackendApi kBackend = skgpu::BackendApi::kVulkan;
|
||||
|
||||
Protected isProtected() const {
|
||||
return fFlags & VK_IMAGE_CREATE_PROTECTED_BIT ? Protected::kYes : Protected::kNo;
|
||||
}
|
||||
TextureFormat viewFormat() const;
|
||||
|
||||
bool serialize(SkWStream*) const;
|
||||
bool deserialize(SkStream*);
|
||||
|
||||
// Virtual API when the specific backend type is not available.
|
||||
SkString toBackendString() const override;
|
||||
|
||||
void copyTo(TextureInfo::AnyTextureInfoData& dstData) const override {
|
||||
dstData.emplace<VulkanTextureInfo>(*this);
|
||||
}
|
||||
bool isCompatible(const TextureInfo& that, bool requireExact) const override;
|
||||
};
|
||||
|
||||
namespace TextureInfos {
|
||||
@@ -78,7 +101,6 @@ namespace BackendSemaphores {
|
||||
SK_API BackendSemaphore MakeVulkan(VkSemaphore);
|
||||
|
||||
SK_API VkSemaphore GetVkSemaphore(const BackendSemaphore&);
|
||||
|
||||
} // namespace BackendSemaphores
|
||||
|
||||
} // namespace skgpu::graphite
|
||||
|
||||
@@ -5,24 +5,6 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef skgpu_graphite_VulkanGraphiteUtils_DEFINED
|
||||
#define skgpu_graphite_VulkanGraphiteUtils_DEFINED
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "include/private/base/SkAPI.h"
|
||||
|
||||
namespace skgpu { struct VulkanBackendContext; }
|
||||
|
||||
namespace skgpu::graphite {
|
||||
|
||||
class Context;
|
||||
struct ContextOptions;
|
||||
|
||||
namespace ContextFactory {
|
||||
SK_API std::unique_ptr<Context> MakeVulkan(const VulkanBackendContext&, const ContextOptions&);
|
||||
} // namespace ContextFactory
|
||||
|
||||
} // namespace skgpu::graphite
|
||||
|
||||
#endif // skgpu_graphite_VulkanGraphiteUtils_DEFINED
|
||||
// DEPRECRATED: VulkanGraphiteUtils.h will be removed in the future, please include
|
||||
// VulkanGraphiteContext.h
|
||||
#include "include/gpu/graphite/vk/VulkanGraphiteContext.h"
|
||||
|
||||
@@ -65,7 +65,15 @@ bool SK_API Simplify(const SkPath& path, SkPath* result);
|
||||
@param result The tight bounds of the path.
|
||||
@return True if the bounds could be computed.
|
||||
*/
|
||||
bool SK_API TightBounds(const SkPath& path, SkRect* result);
|
||||
[[deprecated]]
|
||||
static inline bool TightBounds(const SkPath& path, SkRect* result) {
|
||||
auto rect = path.computeTightBounds();
|
||||
if (rect.isFinite()) {
|
||||
*result = rect;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Set the result with fill type winding to area equivalent to path.
|
||||
Returns true if successful. Does not detect if path contains contours which
|
||||
|
||||
@@ -13,8 +13,11 @@
|
||||
|
||||
class SkFontMgr;
|
||||
class SkFontConfigInterface;
|
||||
class SkFontScanner;
|
||||
|
||||
/** Creates a SkFontMgr which wraps a SkFontConfigInterface. */
|
||||
SK_API sk_sp<SkFontMgr> SkFontMgr_New_FCI(sk_sp<SkFontConfigInterface> fci);
|
||||
SK_API sk_sp<SkFontMgr> SkFontMgr_New_FCI(sk_sp<SkFontConfigInterface> fci,
|
||||
std::unique_ptr<SkFontScanner> scanner);
|
||||
|
||||
#endif // #ifndef SkFontMgr_FontConfigInterface_DEFINED
|
||||
|
||||
@@ -18,4 +18,7 @@
|
||||
SK_API sk_sp<SkTypeface> SkTypeface_Make_Fontations(std::unique_ptr<SkStreamAsset> fontData,
|
||||
const SkFontArguments& args);
|
||||
|
||||
SK_API sk_sp<SkTypeface> SkTypeface_Make_Fontations(sk_sp<SkData> fontData,
|
||||
const SkFontArguments& args);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -86,6 +86,13 @@ public:
|
||||
virtual bool findGainmapImage(sk_sp<SkData> baseImageData,
|
||||
sk_sp<SkData>& outGainmapImagedata,
|
||||
SkGainmapInfo& outGainmapInfo) = 0;
|
||||
|
||||
/**
|
||||
* Return the first JUMBF superbox, if any, and nullptr otherwise. If |copyData| is false,
|
||||
* then the returned SkData may directly reference the data provided when this object was
|
||||
* created.
|
||||
*/
|
||||
virtual sk_sp<SkData> getJUMBFMetadata(bool copyData) const = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -28,15 +28,6 @@
|
||||
class SkMatrix;
|
||||
class SkRRect;
|
||||
|
||||
// These are computed from a stream of verbs
|
||||
struct SkPathVerbAnalysis {
|
||||
bool valid;
|
||||
int points, weights;
|
||||
unsigned segmentMask;
|
||||
};
|
||||
SkPathVerbAnalysis sk_path_analyze_verbs(const uint8_t verbs[], int count);
|
||||
|
||||
|
||||
/**
|
||||
* Holds the path verbs and points. It is versioned by a generation ID. None of its public methods
|
||||
* modify the contents. To modify or append to the verbs/points wrap the SkPathRef in an
|
||||
@@ -541,7 +532,12 @@ private:
|
||||
|
||||
void callGenIDChangeListeners();
|
||||
|
||||
PointsArray fPoints;
|
||||
VerbsArray fVerbs;
|
||||
ConicWeightsArray fConicWeights;
|
||||
|
||||
mutable SkRect fBounds;
|
||||
SkRect fArcOval;
|
||||
|
||||
enum {
|
||||
kEmptyGenID = 1, // GenID reserved for path ref with zero points and zero verbs.
|
||||
@@ -549,27 +545,25 @@ private:
|
||||
mutable uint32_t fGenerationID;
|
||||
SkIDChangeListener::List fGenIDChangeListeners;
|
||||
|
||||
PointsArray fPoints;
|
||||
VerbsArray fVerbs;
|
||||
ConicWeightsArray fConicWeights;
|
||||
|
||||
SkDEBUGCODE(std::atomic<int> fEditorsAttached;) // assert only one editor in use at any time.
|
||||
|
||||
mutable uint8_t fBoundsIsDirty;
|
||||
mutable bool fIsFinite; // only meaningful if bounds are valid
|
||||
SkScalar fArcStartAngle;
|
||||
SkScalar fArcSweepAngle;
|
||||
|
||||
PathType fType;
|
||||
// Both the circle and rrect special cases have a notion of direction and starting point
|
||||
// The next two variables store that information for either.
|
||||
bool fRRectOrOvalIsCCW;
|
||||
|
||||
mutable uint8_t fBoundsIsDirty;
|
||||
|
||||
uint8_t fRRectOrOvalStartIdx;
|
||||
uint8_t fSegmentMask;
|
||||
// If the path is an arc, these four variables store that information.
|
||||
// We should just store an SkArc, but alignment would cost us 8 more bytes.
|
||||
SkArc::Type fArcType;
|
||||
SkRect fArcOval;
|
||||
SkScalar fArcStartAngle;
|
||||
SkScalar fArcSweepAngle;
|
||||
|
||||
mutable bool fIsFinite; // only meaningful if bounds are valid
|
||||
// Both the circle and rrect special cases have a notion of direction and starting point
|
||||
// The next two variables store that information for either.
|
||||
bool fRRectOrOvalIsCCW;
|
||||
|
||||
friend class PathRefTest_Private;
|
||||
friend class ForceIsRRect_Private; // unit test isRRect
|
||||
|
||||
@@ -52,6 +52,9 @@ public:
|
||||
fValid = false;
|
||||
}
|
||||
|
||||
bool has_value() const { return fValid; }
|
||||
explicit operator bool() const { return this->has_value(); }
|
||||
|
||||
const Base* get() const {
|
||||
SkASSERT(fValid);
|
||||
return std::launder(reinterpret_cast<const Base*>(fData));
|
||||
@@ -65,6 +68,9 @@ public:
|
||||
Base* operator->() { return this->get(); }
|
||||
const Base* operator->() const { return this->get(); }
|
||||
|
||||
Base& operator*() { return *this->get(); }
|
||||
const Base& operator*() const { return *this->get(); }
|
||||
|
||||
private:
|
||||
alignas(8) std::byte fData[Size];
|
||||
bool fValid = false;
|
||||
|
||||
@@ -56,20 +56,26 @@
|
||||
* Used to ignore sanitizer warnings.
|
||||
*/
|
||||
#if !defined(SK_NO_SANITIZE)
|
||||
#if defined(__has_attribute)
|
||||
#if __has_attribute(no_sanitize)
|
||||
// This should be for clang and versions of gcc >= 8.0
|
||||
#define SK_NO_SANITIZE(A) SK_ATTRIBUTE(no_sanitize(A))
|
||||
#else
|
||||
// For compilers that don't support sanitization, just do nothing.
|
||||
#define SK_NO_SANITIZE(A)
|
||||
#endif
|
||||
#else // no __has_attribute, e.g. MSVC
|
||||
#define SK_NO_SANITIZE(A)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Helper macro to define no_sanitize attributes only with clang.
|
||||
* Used to ignore CFI sanitizer warnings, supported only by Clang at the moment.
|
||||
*/
|
||||
#if defined(__clang__) && defined(__has_attribute)
|
||||
#if __has_attribute(no_sanitize)
|
||||
#define SK_CLANG_NO_SANITIZE(A) SK_NO_SANITIZE(A)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(SK_CLANG_NO_SANITIZE)
|
||||
#define SK_CLANG_NO_SANITIZE(A)
|
||||
#if defined(__clang__)
|
||||
#define SK_NO_SANITIZE_CFI SK_NO_SANITIZE("cfi")
|
||||
#else
|
||||
#define SK_NO_SANITIZE_CFI
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
||||
@@ -131,11 +131,7 @@ static constexpr int64_t sk_float_saturate2int64(float x) {
|
||||
// Cast double to float, ignoring any warning about too-large finite values being cast to float.
|
||||
// Clang thinks this is undefined, but it's actually implementation defined to return either
|
||||
// the largest float or infinity (one of the two bracketing representable floats). Good enough!
|
||||
#ifdef __clang__
|
||||
SK_NO_SANITIZE("float-cast-overflow")
|
||||
#elif defined(__GNUC__)
|
||||
SK_ATTRIBUTE(no_sanitize_undefined)
|
||||
#endif
|
||||
static constexpr float sk_double_to_float(double x) {
|
||||
return static_cast<float>(x);
|
||||
}
|
||||
@@ -161,20 +157,12 @@ static inline float sk_float_rsqrt (float x) { return 1.0f / std::sqrt(x
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4723)
|
||||
#endif
|
||||
#ifdef __clang__
|
||||
SK_NO_SANITIZE("float-divide-by-zero")
|
||||
#elif defined(__GNUC__)
|
||||
SK_ATTRIBUTE(no_sanitize_undefined)
|
||||
#endif
|
||||
static constexpr float sk_ieee_float_divide(float numer, float denom) {
|
||||
return numer / denom;
|
||||
}
|
||||
|
||||
#ifdef __clang__
|
||||
SK_NO_SANITIZE("float-divide-by-zero")
|
||||
#elif defined(__GNUC__)
|
||||
SK_ATTRIBUTE(no_sanitize_undefined)
|
||||
#endif
|
||||
static constexpr double sk_ieee_double_divide(double numer, double denom) {
|
||||
return numer / denom;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,14 @@
|
||||
#include "include/private/base/SkThreadAnnotations.h"
|
||||
#include "include/private/base/SkThreadID.h"
|
||||
|
||||
/**
|
||||
* class SkMutex
|
||||
*
|
||||
* This allows us to have a mutex without needing the one in
|
||||
* the C++ std library which does not work with all clients.
|
||||
* go/cstyle#Disallowed_Stdlib
|
||||
*/
|
||||
|
||||
class SK_CAPABILITY("mutex") SkMutex {
|
||||
public:
|
||||
constexpr SkMutex() = default;
|
||||
|
||||
@@ -617,11 +617,7 @@ private:
|
||||
// unpredictable location in memory. Of course, TArray won't actually use fItemArray in this
|
||||
// way, and we don't want to construct a T before the user requests one. There's no real risk
|
||||
// here, so disable CFI when doing these casts.
|
||||
#ifdef __clang__
|
||||
SK_NO_SANITIZE("cfi")
|
||||
#elif defined(__GNUC__)
|
||||
SK_ATTRIBUTE(no_sanitize_undefined)
|
||||
#endif
|
||||
SK_NO_SANITIZE_CFI
|
||||
static T* TCast(void* buffer) {
|
||||
return (T*)buffer;
|
||||
}
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
#include "include/private/base/SkDebug.h"
|
||||
#include "include/private/base/SkTo.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <initializer_list>
|
||||
#include <utility>
|
||||
|
||||
class SK_SPI SkTDStorage {
|
||||
public:
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "include/private/base/SkTLogic.h"
|
||||
#include "include/private/base/SkTo.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
@@ -94,7 +95,8 @@ public:
|
||||
|
||||
|
||||
namespace skia_private {
|
||||
/** Allocate an array of T elements, and free the array in the destructor
|
||||
/** Allocate an array of T elements on the heap. Once this goes out of scope, the
|
||||
* elements will be cleaned up "auto"matically.
|
||||
*/
|
||||
template <typename T> class AutoTArray {
|
||||
public:
|
||||
@@ -164,7 +166,10 @@ private:
|
||||
size_t fSize = 0;
|
||||
};
|
||||
|
||||
/** Wraps AutoTArray, with room for kCountRequested elements preallocated.
|
||||
/** Like AutoTArray with room for kCountRequested elements preallocated on
|
||||
* the Stack. If count exceeds the space of the preallocation, the elements
|
||||
* will live on the heap. Once this goes out of scope, the elements will be
|
||||
* cleaned up "auto"matically.
|
||||
*/
|
||||
template <int kCountRequested, typename T> class AutoSTArray {
|
||||
public:
|
||||
@@ -256,17 +261,25 @@ private:
|
||||
#if defined(SK_BUILD_FOR_GOOGLE3)
|
||||
// Stack frame size is limited for SK_BUILD_FOR_GOOGLE3. 4k is less than the actual max,
|
||||
// but some functions have multiple large stack allocations.
|
||||
static const int kMaxBytes = 4 * 1024;
|
||||
static const int kCount = kCountRequested * sizeof(T) > kMaxBytes
|
||||
static constexpr int kMaxBytes = 4 * 1024;
|
||||
static constexpr int kMinCount = kCountRequested * sizeof(T) > kMaxBytes
|
||||
? kMaxBytes / sizeof(T)
|
||||
: kCountRequested;
|
||||
#else
|
||||
static const int kCount = kCountRequested;
|
||||
static constexpr int kMinCount = kCountRequested;
|
||||
#endif
|
||||
|
||||
int fCount;
|
||||
// Because we are also storing an int, there is a tiny bit of padding that
|
||||
// the C++ compiler adds after fStorage if sizeof(T) <= alignof(T*).
|
||||
// Thus, we can expand how many elements are stored on the stack to make use of this
|
||||
// (e.g. 1 extra element for 4 byte T if kCountRequested was even).
|
||||
static_assert(alignof(int) <= alignof(T*) || alignof(int) <= alignof(T));
|
||||
static constexpr int kCount =
|
||||
SkAlignTo(kMinCount*sizeof(T) + sizeof(int), std::max(alignof(T*), alignof(T))) / sizeof(T);
|
||||
|
||||
T* fArray;
|
||||
alignas(T) char fStorage[kCount * sizeof(T)];
|
||||
alignas(T) std::byte fStorage[kCount * sizeof(T)];
|
||||
int fCount;
|
||||
};
|
||||
|
||||
/** Manages an array of T elements, freeing the array in the destructor.
|
||||
@@ -409,16 +422,16 @@ public:
|
||||
|
||||
private:
|
||||
// Since we use uint32_t storage, we might be able to get more elements for free.
|
||||
static const size_t kCountWithPadding = SkAlign4(kCountRequested*sizeof(T)) / sizeof(T);
|
||||
static constexpr size_t kCountWithPadding = SkAlign4(kCountRequested*sizeof(T)) / sizeof(T);
|
||||
#if defined(SK_BUILD_FOR_GOOGLE3)
|
||||
// Stack frame size is limited for SK_BUILD_FOR_GOOGLE3. 4k is less than the actual max, but some functions
|
||||
// have multiple large stack allocations.
|
||||
static const size_t kMaxBytes = 4 * 1024;
|
||||
static const size_t kCount = kCountRequested * sizeof(T) > kMaxBytes
|
||||
static constexpr size_t kMaxBytes = 4 * 1024;
|
||||
static constexpr size_t kCount = kCountRequested * sizeof(T) > kMaxBytes
|
||||
? kMaxBytes / sizeof(T)
|
||||
: kCountWithPadding;
|
||||
#else
|
||||
static const size_t kCount = kCountWithPadding;
|
||||
static constexpr size_t kCount = kCountWithPadding;
|
||||
#endif
|
||||
|
||||
T* fPtr;
|
||||
|
||||
38
gfx/skia/skia/include/private/chromium/SkPMColor.h
Normal file
38
gfx/skia/skia/include/private/chromium/SkPMColor.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2025 Google LLC
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkPMColor_DEFINED
|
||||
#define SkPMColor_DEFINED
|
||||
|
||||
#include "include/core/SkColor.h"
|
||||
#include "include/private/base/SkAPI.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
/** Returns a SkPMColor value from already premultiplied 8-bit component values.
|
||||
|
||||
@param a amount of alpha, from fully transparent (0) to fully opaque (255)
|
||||
@param r amount of red, from no red (0) to full red (255)
|
||||
@param g amount of green, from no green (0) to full green (255)
|
||||
@param b amount of blue, from no blue (0) to full blue (255)
|
||||
@return premultiplied color
|
||||
*/
|
||||
SK_API SkPMColor SkPMColorSetARGB(SkAlpha a, uint8_t r, uint8_t g, uint8_t b);
|
||||
|
||||
/** Returns alpha component of premultiplied color. */
|
||||
SK_API SkAlpha SkPMColorGetA(SkPMColor);
|
||||
|
||||
/** Returns red component of premultiplied color. */
|
||||
SK_API uint8_t SkPMColorGetR(SkPMColor);
|
||||
|
||||
/** Returns green component of premultiplied color. */
|
||||
SK_API uint8_t SkPMColorGetG(SkPMColor);
|
||||
|
||||
/** Returns blue component of premultiplied color. */
|
||||
SK_API uint8_t SkPMColorGetB(SkPMColor);
|
||||
|
||||
#endif
|
||||
@@ -539,7 +539,7 @@ SK_MAKE_BITFIELD_CLASS_OPS(GpuPathRenderers)
|
||||
enum class GrColorType {
|
||||
kUnknown,
|
||||
kAlpha_8,
|
||||
kBGR_565,
|
||||
kBGR_565, // This corresponds to kRGB_565_SkColorType, which is misnamed.
|
||||
kRGB_565,
|
||||
kABGR_4444, // This name differs from SkColorType. kARGB_4444_SkColorType is misnamed.
|
||||
kRGBA_8888,
|
||||
|
||||
@@ -16,9 +16,6 @@ namespace SkSL {
|
||||
|
||||
class DebugTrace : public SkRefCnt {
|
||||
public:
|
||||
/** Serializes a debug trace to JSON which can be parsed by our debugger. */
|
||||
virtual void writeTrace(SkWStream* w) const = 0;
|
||||
|
||||
/** Generates a human-readable dump of the debug trace. */
|
||||
virtual void dump(SkWStream* o) const = 0;
|
||||
};
|
||||
|
||||
6
gfx/skia/skia/modules/skcms/README.chromium
Normal file
6
gfx/skia/skia/modules/skcms/README.chromium
Normal file
@@ -0,0 +1,6 @@
|
||||
Name: skcms
|
||||
URL: https://skia.org/
|
||||
Version: unknown
|
||||
Security Critical: yes
|
||||
Shipped: yes
|
||||
License: BSD
|
||||
@@ -1464,88 +1464,89 @@ const skcms_ICCProfile* skcms_sRGB_profile() {
|
||||
|
||||
// We choose to represent sRGB with its canonical transfer function,
|
||||
// and with its canonical XYZD50 gamut matrix.
|
||||
true, // has_trc, followed by the 3 trc curves
|
||||
{
|
||||
{ // the 3 trc curves
|
||||
{{0, {2.4f, (float)(1/1.055), (float)(0.055/1.055), (float)(1/12.92), 0.04045f, 0, 0}}},
|
||||
{{0, {2.4f, (float)(1/1.055), (float)(0.055/1.055), (float)(1/12.92), 0.04045f, 0, 0}}},
|
||||
{{0, {2.4f, (float)(1/1.055), (float)(0.055/1.055), (float)(1/12.92), 0.04045f, 0, 0}}},
|
||||
},
|
||||
|
||||
true, // has_toXYZD50, followed by 3x3 toXYZD50 matrix
|
||||
{{
|
||||
{{ // 3x3 toXYZD50 matrix
|
||||
{ 0.436065674f, 0.385147095f, 0.143066406f },
|
||||
{ 0.222488403f, 0.716873169f, 0.060607910f },
|
||||
{ 0.013916016f, 0.097076416f, 0.714096069f },
|
||||
}},
|
||||
|
||||
false, // has_A2B, followed by A2B itself, which we don't care about.
|
||||
{
|
||||
0,
|
||||
{
|
||||
{ // an empty A2B
|
||||
{ // input_curves
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
},
|
||||
{0,0,0,0},
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr, // grid_8
|
||||
nullptr, // grid_16
|
||||
0, // input_channels
|
||||
{0,0,0,0}, // grid_points
|
||||
|
||||
0,
|
||||
{
|
||||
{ // matrix_curves
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
},
|
||||
{{
|
||||
{{ // matrix (3x4)
|
||||
{ 0,0,0,0 },
|
||||
{ 0,0,0,0 },
|
||||
{ 0,0,0,0 },
|
||||
}},
|
||||
0, // matrix_channels
|
||||
|
||||
0, // output_channels
|
||||
{ // output_curves
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
},
|
||||
},
|
||||
|
||||
{ // an empty B2A
|
||||
{ // input_curves
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
},
|
||||
0, // input_channels
|
||||
|
||||
0, // matrix_channels
|
||||
{ // matrix_curves
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
},
|
||||
{{ // matrix (3x4)
|
||||
{ 0,0,0,0 },
|
||||
{ 0,0,0,0 },
|
||||
{ 0,0,0,0 },
|
||||
}},
|
||||
|
||||
0,
|
||||
{
|
||||
{ // output_curves
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
},
|
||||
nullptr, // grid_8
|
||||
nullptr, // grid_16
|
||||
{0,0,0,0}, // grid_points
|
||||
0, // output_channels
|
||||
},
|
||||
|
||||
false, // has_B2A, followed by B2A itself, which we also don't care about.
|
||||
{
|
||||
0,
|
||||
{
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
},
|
||||
{ 0, 0, 0, 0 }, // an empty CICP
|
||||
|
||||
0,
|
||||
{{
|
||||
{ 0,0,0,0 },
|
||||
{ 0,0,0,0 },
|
||||
{ 0,0,0,0 },
|
||||
}},
|
||||
{
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
},
|
||||
|
||||
0,
|
||||
{0,0,0,0},
|
||||
nullptr,
|
||||
nullptr,
|
||||
{
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
},
|
||||
},
|
||||
|
||||
false, // has_CICP, followed by cicp itself which we don't care about.
|
||||
{ 0, 0, 0, 0 },
|
||||
true, // has_trc
|
||||
true, // has_toXYZD50
|
||||
false, // has_A2B
|
||||
false, // has B2A
|
||||
false, // has_CICP
|
||||
};
|
||||
return &sRGB_profile;
|
||||
}
|
||||
@@ -1560,88 +1561,89 @@ const skcms_ICCProfile* skcms_XYZD50_profile() {
|
||||
skcms_Signature_XYZ, // pcs
|
||||
0, // tag count, moot here
|
||||
|
||||
true, // has_trc, followed by the 3 trc curves
|
||||
{
|
||||
{ // the 3 trc curves
|
||||
{{0, {1,1, 0,0,0,0,0}}},
|
||||
{{0, {1,1, 0,0,0,0,0}}},
|
||||
{{0, {1,1, 0,0,0,0,0}}},
|
||||
},
|
||||
|
||||
true, // has_toXYZD50, followed by 3x3 toXYZD50 matrix
|
||||
{{
|
||||
{{ // 3x3 toXYZD50 matrix
|
||||
{ 1,0,0 },
|
||||
{ 0,1,0 },
|
||||
{ 0,0,1 },
|
||||
}},
|
||||
|
||||
false, // has_A2B, followed by A2B itself, which we don't care about.
|
||||
{
|
||||
0,
|
||||
{
|
||||
{ // an empty A2B
|
||||
{ // input_curves
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
},
|
||||
{0,0,0,0},
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr, // grid_8
|
||||
nullptr, // grid_16
|
||||
0, // input_channels
|
||||
{0,0,0,0}, // grid_points
|
||||
|
||||
0,
|
||||
{
|
||||
{ // matrix_curves
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
},
|
||||
{{
|
||||
{{ // matrix (3x4)
|
||||
{ 0,0,0,0 },
|
||||
{ 0,0,0,0 },
|
||||
{ 0,0,0,0 },
|
||||
}},
|
||||
0, // matrix_channels
|
||||
|
||||
0, // output_channels
|
||||
{ // output_curves
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
},
|
||||
},
|
||||
|
||||
{ // an empty B2A
|
||||
{ // input_curves
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
},
|
||||
0, // input_channels
|
||||
|
||||
0, // matrix_channels
|
||||
{ // matrix_curves
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
},
|
||||
{{ // matrix (3x4)
|
||||
{ 0,0,0,0 },
|
||||
{ 0,0,0,0 },
|
||||
{ 0,0,0,0 },
|
||||
}},
|
||||
|
||||
0,
|
||||
{
|
||||
{ // output_curves
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
},
|
||||
nullptr, // grid_8
|
||||
nullptr, // grid_16
|
||||
{0,0,0,0}, // grid_points
|
||||
0, // output_channels
|
||||
},
|
||||
|
||||
false, // has_B2A, followed by B2A itself, which we also don't care about.
|
||||
{
|
||||
0,
|
||||
{
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
},
|
||||
{ 0, 0, 0, 0 }, // an empty CICP
|
||||
|
||||
0,
|
||||
{{
|
||||
{ 0,0,0,0 },
|
||||
{ 0,0,0,0 },
|
||||
{ 0,0,0,0 },
|
||||
}},
|
||||
{
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
},
|
||||
|
||||
0,
|
||||
{0,0,0,0},
|
||||
nullptr,
|
||||
nullptr,
|
||||
{
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
{{0, {0,0, 0,0,0,0,0}}},
|
||||
},
|
||||
},
|
||||
|
||||
false, // has_CICP, followed by cicp itself which we don't care about.
|
||||
{ 0, 0, 0, 0 },
|
||||
true, // has_trc
|
||||
true, // has_toXYZD50
|
||||
false, // has_A2B
|
||||
false, // has B2A
|
||||
false, // has_CICP
|
||||
};
|
||||
|
||||
return &XYZD50_profile;
|
||||
@@ -2843,6 +2845,7 @@ bool skcms_Transform(const void* src,
|
||||
case skcms_PixelFormat_RGB_hhh_Norm >> 1: add_op(Op::store_hhh); break;
|
||||
case skcms_PixelFormat_RGBA_hhhh_Norm >> 1: add_op(Op::store_hhhh); break;
|
||||
case skcms_PixelFormat_RGB_101010x_XR >> 1: add_op(Op::store_101010x_XR); break;
|
||||
case skcms_PixelFormat_RGBA_10101010_XR >> 1: add_op(Op::store_10101010_XR); break;
|
||||
case skcms_PixelFormat_RGB_hhh >> 1: add_op(Op::store_hhh); break;
|
||||
case skcms_PixelFormat_RGBA_hhhh >> 1: add_op(Op::store_hhhh); break;
|
||||
case skcms_PixelFormat_RGB_fff >> 1: add_op(Op::store_fff); break;
|
||||
|
||||
@@ -904,24 +904,19 @@ STAGE(load_1010102, NoCtx) {
|
||||
}
|
||||
|
||||
STAGE(load_101010x_XR, NoCtx) {
|
||||
static constexpr float min = -0.752941f;
|
||||
static constexpr float max = 1.25098f;
|
||||
static constexpr float range = max - min;
|
||||
U32 rgba = load<U32>(src + 4*i);
|
||||
r = cast<F>((rgba >> 0) & 0x3ff) * (1/1023.0f) * range + min;
|
||||
g = cast<F>((rgba >> 10) & 0x3ff) * (1/1023.0f) * range + min;
|
||||
b = cast<F>((rgba >> 20) & 0x3ff) * (1/1023.0f) * range + min;
|
||||
r = cast<F>(((rgba >> 0) & 0x3ff) - 384) / 510.0f;
|
||||
g = cast<F>(((rgba >> 10) & 0x3ff) - 384) / 510.0f;
|
||||
b = cast<F>(((rgba >> 20) & 0x3ff) - 384) / 510.0f;
|
||||
}
|
||||
|
||||
STAGE(load_10101010_XR, NoCtx) {
|
||||
static constexpr float min = -0.752941f;
|
||||
static constexpr float max = 1.25098f;
|
||||
static constexpr float range = max - min;
|
||||
U64 rgba = load<U64>(src + 8 * i);
|
||||
r = cast<F>((rgba >> (0+6)) & 0x3ff) * (1/1023.0f) * range + min;
|
||||
g = cast<F>((rgba >> (16+6)) & 0x3ff) * (1/1023.0f) * range + min;
|
||||
b = cast<F>((rgba >> (32+6)) & 0x3ff) * (1/1023.0f) * range + min;
|
||||
a = cast<F>((rgba >> (48+6)) & 0x3ff) * (1/1023.0f) * range + min;
|
||||
// Each channel is 16 bits, where the 6 low bits are padding.
|
||||
r = cast<F>(((rgba >> ( 0+6)) & 0x3ff) - 384) / 510.0f;
|
||||
g = cast<F>(((rgba >> (16+6)) & 0x3ff) - 384) / 510.0f;
|
||||
b = cast<F>(((rgba >> (32+6)) & 0x3ff) - 384) / 510.0f;
|
||||
a = cast<F>(((rgba >> (48+6)) & 0x3ff) - 384) / 510.0f;
|
||||
}
|
||||
|
||||
STAGE(load_161616LE, NoCtx) {
|
||||
@@ -1310,12 +1305,17 @@ FINAL_STAGE(store_8888, NoCtx) {
|
||||
}
|
||||
|
||||
FINAL_STAGE(store_101010x_XR, NoCtx) {
|
||||
static constexpr float min = -0.752941f;
|
||||
static constexpr float max = 1.25098f;
|
||||
static constexpr float range = max - min;
|
||||
store(dst + 4*i, cast<U32>(to_fixed(((r - min) / range) * 1023)) << 0
|
||||
| cast<U32>(to_fixed(((g - min) / range) * 1023)) << 10
|
||||
| cast<U32>(to_fixed(((b - min) / range) * 1023)) << 20);
|
||||
store(dst + 4*i, cast<U32>(to_fixed((r * 510) + 384)) << 0
|
||||
| cast<U32>(to_fixed((g * 510) + 384)) << 10
|
||||
| cast<U32>(to_fixed((b * 510) + 384)) << 20);
|
||||
}
|
||||
|
||||
FINAL_STAGE(store_10101010_XR, NoCtx) {
|
||||
// Each channel is 16 bits, where the 6 low bits are padding.
|
||||
store(dst + 8*i, cast<U64>(to_fixed((r * 510) + 384)) << ( 0+6)
|
||||
| cast<U64>(to_fixed((g * 510) + 384)) << (16+6)
|
||||
| cast<U64>(to_fixed((b * 510) + 384)) << (32+6)
|
||||
| cast<U64>(to_fixed((a * 510) + 384)) << (48+6));
|
||||
}
|
||||
|
||||
FINAL_STAGE(store_1010102, NoCtx) {
|
||||
@@ -1521,7 +1521,7 @@ FINAL_STAGE(store_ffff, NoCtx) {
|
||||
// NOLINTNEXTLINE(misc-definitions-in-headers)
|
||||
void run_program(const Op* program, const void** contexts, SKCMS_MAYBE_UNUSED ptrdiff_t programSize,
|
||||
const char* src, char* dst, int n,
|
||||
const size_t src_bpp, const size_t dst_bpp) {
|
||||
size_t src_bpp, size_t dst_bpp) {
|
||||
#if SKCMS_HAS_MUSTTAIL
|
||||
// Convert the program into an array of tailcall stages.
|
||||
StageFn stages[32];
|
||||
|
||||
@@ -101,6 +101,7 @@ namespace skcms_private {
|
||||
M(store_161616BE) \
|
||||
M(store_16161616BE) \
|
||||
M(store_101010x_XR) \
|
||||
M(store_10101010_XR) \
|
||||
M(store_hhh) \
|
||||
M(store_hhhh) \
|
||||
M(store_fff) \
|
||||
@@ -144,21 +145,21 @@ namespace baseline {
|
||||
|
||||
void run_program(const Op* program, const void** contexts, ptrdiff_t programSize,
|
||||
const char* src, char* dst, int n,
|
||||
const size_t src_bpp, const size_t dst_bpp);
|
||||
size_t src_bpp, size_t dst_bpp);
|
||||
|
||||
}
|
||||
namespace hsw {
|
||||
|
||||
void run_program(const Op* program, const void** contexts, ptrdiff_t programSize,
|
||||
const char* src, char* dst, int n,
|
||||
const size_t src_bpp, const size_t dst_bpp);
|
||||
size_t src_bpp, size_t dst_bpp);
|
||||
|
||||
}
|
||||
namespace skx {
|
||||
|
||||
void run_program(const Op* program, const void** contexts, ptrdiff_t programSize,
|
||||
const char* src, char* dst, int n,
|
||||
const size_t src_bpp, const size_t dst_bpp);
|
||||
size_t src_bpp, size_t dst_bpp);
|
||||
|
||||
}
|
||||
} // namespace skcms_private
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace hsw {
|
||||
|
||||
void run_program(const Op* program, const void** contexts, ptrdiff_t programSize,
|
||||
const char* src, char* dst, int n,
|
||||
const size_t src_bpp, const size_t dst_bpp) {
|
||||
size_t src_bpp, size_t dst_bpp) {
|
||||
skcms_private::baseline::run_program(program, contexts, programSize,
|
||||
src, dst, n, src_bpp, dst_bpp);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace skx {
|
||||
|
||||
void run_program(const Op* program, const void** contexts, ptrdiff_t programSize,
|
||||
const char* src, char* dst, int n,
|
||||
const size_t src_bpp, const size_t dst_bpp) {
|
||||
size_t src_bpp, size_t dst_bpp) {
|
||||
skcms_private::baseline::run_program(program, contexts, programSize,
|
||||
src, dst, n, src_bpp, dst_bpp);
|
||||
}
|
||||
|
||||
@@ -105,6 +105,9 @@ SKCMS_API bool skcms_TransferFunction_isHLGish (const skcms_TransferFunction*);
|
||||
// Unified representation of 'curv' or 'para' tag data, or a 1D table from 'mft1' or 'mft2'
|
||||
typedef union skcms_Curve {
|
||||
struct {
|
||||
// this needs to line up with alias_of_table_entries so we can tell if there are or
|
||||
// are not table entries. If this is 0, this struct is a parametric function,
|
||||
// otherwise it's a table entry.
|
||||
uint32_t alias_of_table_entries;
|
||||
skcms_TransferFunction parametric;
|
||||
};
|
||||
@@ -123,44 +126,44 @@ typedef struct skcms_A2B {
|
||||
// Optional: N 1D "A" curves, followed by an N-dimensional CLUT.
|
||||
// If input_channels == 0, these curves and CLUT are skipped,
|
||||
// Otherwise, input_channels must be in [1, 4].
|
||||
uint32_t input_channels;
|
||||
skcms_Curve input_curves[4];
|
||||
uint8_t grid_points[4];
|
||||
const uint8_t* grid_8;
|
||||
const uint8_t* grid_16;
|
||||
uint32_t input_channels;
|
||||
uint8_t grid_points[4];
|
||||
|
||||
// Optional: 3 1D "M" curves, followed by a color matrix.
|
||||
// If matrix_channels == 0, these curves and matrix are skipped,
|
||||
// Otherwise, matrix_channels must be 3.
|
||||
uint32_t matrix_channels;
|
||||
skcms_Curve matrix_curves[3];
|
||||
skcms_Matrix3x4 matrix;
|
||||
uint32_t matrix_channels;
|
||||
|
||||
// Required: 3 1D "B" curves. Always present, and output_channels must be 3.
|
||||
uint32_t output_channels;
|
||||
uint32_t output_channels; // list first to pack with matrix_channels
|
||||
skcms_Curve output_curves[3];
|
||||
} skcms_A2B;
|
||||
|
||||
typedef struct skcms_B2A {
|
||||
// Required: 3 1D "B" curves. Always present, and input_channels must be 3.
|
||||
uint32_t input_channels;
|
||||
skcms_Curve input_curves[3];
|
||||
uint32_t input_channels;
|
||||
|
||||
// Optional: a color matrix, followed by 3 1D "M" curves.
|
||||
// If matrix_channels == 0, this matrix and these curves are skipped,
|
||||
// Otherwise, matrix_channels must be 3.
|
||||
uint32_t matrix_channels;
|
||||
skcms_Matrix3x4 matrix;
|
||||
uint32_t matrix_channels; // list first to pack with input_channels
|
||||
skcms_Curve matrix_curves[3];
|
||||
skcms_Matrix3x4 matrix;
|
||||
|
||||
// Optional: an N-dimensional CLUT, followed by N 1D "A" curves.
|
||||
// If output_channels == 0, this CLUT and these curves are skipped,
|
||||
// Otherwise, output_channels must be in [1, 4].
|
||||
uint32_t output_channels;
|
||||
uint8_t grid_points[4];
|
||||
skcms_Curve output_curves[4];
|
||||
const uint8_t* grid_8;
|
||||
const uint8_t* grid_16;
|
||||
skcms_Curve output_curves[4];
|
||||
uint8_t grid_points[4];
|
||||
uint32_t output_channels;
|
||||
} skcms_B2A;
|
||||
|
||||
typedef struct skcms_CICP {
|
||||
@@ -182,30 +185,31 @@ typedef struct skcms_ICCProfile {
|
||||
|
||||
// If we can parse red, green and blue transfer curves from the profile,
|
||||
// trc will be set to those three curves, and has_trc will be true.
|
||||
bool has_trc;
|
||||
skcms_Curve trc[3];
|
||||
|
||||
// If this profile's gamut can be represented by a 3x3 transform to XYZD50,
|
||||
// skcms_Parse() sets toXYZD50 to that transform and has_toXYZD50 to true.
|
||||
bool has_toXYZD50;
|
||||
skcms_Matrix3x3 toXYZD50;
|
||||
|
||||
// If the profile has a valid A2B0 or A2B1 tag, skcms_Parse() sets A2B to
|
||||
// that data, and has_A2B to true. skcms_ParseWithA2BPriority() does the
|
||||
// same following any user-provided prioritization of A2B0, A2B1, or A2B2.
|
||||
bool has_A2B;
|
||||
skcms_A2B A2B;
|
||||
|
||||
// If the profile has a valid B2A0 or B2A1 tag, skcms_Parse() sets B2A to
|
||||
// that data, and has_B2A to true. skcms_ParseWithA2BPriority() does the
|
||||
// same following any user-provided prioritization of B2A0, B2A1, or B2A2.
|
||||
bool has_B2A;
|
||||
skcms_B2A B2A;
|
||||
|
||||
// If the profile has a valid CICP tag, skcms_Parse() sets CICP to that data,
|
||||
// and has_CICP to true.
|
||||
bool has_CICP;
|
||||
skcms_CICP CICP;
|
||||
|
||||
bool has_trc;
|
||||
bool has_toXYZD50;
|
||||
bool has_A2B;
|
||||
bool has_B2A;
|
||||
bool has_CICP;
|
||||
} skcms_ICCProfile;
|
||||
|
||||
// The sRGB color profile is so commonly used that we offer a canonical skcms_ICCProfile for it.
|
||||
|
||||
1
gfx/skia/skia/modules/skcms/version.sha1
Executable file
1
gfx/skia/skia/modules/skcms/version.sha1
Executable file
@@ -0,0 +1 @@
|
||||
1e365691d01ad13edd93056c2731a5c6e0be2a15
|
||||
@@ -15,8 +15,6 @@
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
static constexpr double PI = 3.141592653589793;
|
||||
|
||||
static bool nearly_equal(double x, double y) {
|
||||
if (sk_double_nearly_zero(x)) {
|
||||
return sk_double_nearly_zero(y);
|
||||
@@ -92,11 +90,11 @@ int SkCubics::RootsReal(double A, double B, double C, double D, double solution[
|
||||
r = neg2RootQ * cos(theta / 3) - adiv3;
|
||||
*roots++ = r;
|
||||
|
||||
r = neg2RootQ * cos((theta + 2 * PI) / 3) - adiv3;
|
||||
r = neg2RootQ * cos((theta + 2 * SK_DoublePI) / 3) - adiv3;
|
||||
if (!nearly_equal(solution[0], r)) {
|
||||
*roots++ = r;
|
||||
}
|
||||
r = neg2RootQ * cos((theta - 2 * PI) / 3) - adiv3;
|
||||
r = neg2RootQ * cos((theta - 2 * SK_DoublePI) / 3) - adiv3;
|
||||
if (!nearly_equal(solution[0], r) &&
|
||||
(roots - solution == 1 || !nearly_equal(solution[1], r))) {
|
||||
*roots++ = r;
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
//
|
||||
// This lock does not obey strict queue ordering. It will always alternate between readers and
|
||||
// a single writer.
|
||||
//
|
||||
// This allows us to have a mutex without needing the one in
|
||||
// the C++ std library which does not work with all clients.
|
||||
// go/cstyle#Disallowed_Stdlib
|
||||
class SK_CAPABILITY("mutex") SkSharedMutex {
|
||||
public:
|
||||
SkSharedMutex();
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
#include <utility>
|
||||
|
||||
SkTDStorage::SkTDStorage(int sizeOfT) : fSizeOfT{sizeOfT} {}
|
||||
|
||||
|
||||
@@ -238,7 +238,7 @@ public:
|
||||
/**
|
||||
* C++11 range-for interface.
|
||||
*/
|
||||
bool operator!=(const Iter& that) { return fCurr != that.fCurr; }
|
||||
bool operator!=(const Iter& that) const { return fCurr != that.fCurr; }
|
||||
T* operator*() { return this->get(); }
|
||||
void operator++() { this->next(); }
|
||||
|
||||
|
||||
@@ -684,14 +684,12 @@ template <typename Fn, typename... Args, size_t... I>
|
||||
SI auto map(std::index_sequence<I...>,
|
||||
Fn&& fn, const Args&... args) -> skvx::Vec<sizeof...(I), decltype(fn(args[0]...))> {
|
||||
auto lane = [&](size_t i)
|
||||
#if defined(__clang__)
|
||||
// CFI, specifically -fsanitize=cfi-icall, seems to give a false positive here,
|
||||
// with errors like "control flow integrity check for type 'float (float)
|
||||
// noexcept' failed during indirect function call... note: sqrtf.cfi_jt defined
|
||||
// here". But we can be quite sure fn is the right type: it's all inferred!
|
||||
// So, stifle CFI in this function.
|
||||
__attribute__((no_sanitize("cfi")))
|
||||
#endif
|
||||
SK_NO_SANITIZE_CFI
|
||||
{ return fn(args[static_cast<int>(i)]...); };
|
||||
|
||||
return { lane(I)... };
|
||||
|
||||
@@ -178,7 +178,7 @@ class SkMakeZipDetail {
|
||||
public:
|
||||
template<typename... Ts>
|
||||
static constexpr auto MakeZip(Ts&& ... ts) {
|
||||
|
||||
// NOLINTBEGIN
|
||||
// Pick the first collection that has a size, and use that for the size.
|
||||
size_t size = PickOneSize<DecayPointerT<Ts>...>::Size(std::forward<Ts>(ts)...);
|
||||
|
||||
@@ -197,6 +197,7 @@ public:
|
||||
#endif
|
||||
|
||||
return SkZip<ValueType<Ts>...>(size, Span<Ts>::Data(std::forward<Ts>(ts))...);
|
||||
// NOLINTEND
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,560 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "include/codec/SkAndroidCodec.h"
|
||||
|
||||
#include "include/codec/SkCodec.h"
|
||||
#include "include/codec/SkEncodedImageFormat.h"
|
||||
#include "include/core/SkAlphaType.h"
|
||||
#include "include/core/SkColorType.h"
|
||||
#include "include/core/SkData.h"
|
||||
#include "include/core/SkRect.h"
|
||||
#include "include/core/SkStream.h"
|
||||
#include "modules/skcms/skcms.h"
|
||||
#include "src/codec/SkAndroidCodecAdapter.h"
|
||||
#include "src/codec/SkCodecPriv.h"
|
||||
#include "src/codec/SkSampledCodec.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
class SkPngChunkReader;
|
||||
|
||||
static bool is_valid_sample_size(int sampleSize) {
|
||||
// FIXME: As Leon has mentioned elsewhere, surely there is also a maximum sampleSize?
|
||||
return sampleSize > 0;
|
||||
}
|
||||
|
||||
static bool cicp_get_primaries(uint8_t primaries, skcms_Matrix3x3* sk_primaries) {
|
||||
// Rec. ITU-T H.273, Table 2.
|
||||
switch (primaries) {
|
||||
case 0:
|
||||
// Reserved.
|
||||
break;
|
||||
case 1:
|
||||
*sk_primaries = SkNamedGamut::kSRGB;
|
||||
return true;
|
||||
case 2:
|
||||
// Unspecified.
|
||||
break;
|
||||
case 3:
|
||||
// Reserved.
|
||||
break;
|
||||
case 4:
|
||||
return skcms_PrimariesToXYZD50(
|
||||
0.67f, 0.33f, 0.21f, 0.71f, 0.14f, 0.08f, 0.31f, 0.316f, sk_primaries);
|
||||
case 5:
|
||||
return skcms_PrimariesToXYZD50(
|
||||
0.64f, 0.33f, 0.29f, 0.60f, 0.15f, 0.06f, 0.3127f, 0.3290f, sk_primaries);
|
||||
case 6:
|
||||
return skcms_PrimariesToXYZD50(
|
||||
0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f, 0.3127f, 0.3290f, sk_primaries);
|
||||
case 7:
|
||||
return skcms_PrimariesToXYZD50(
|
||||
0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f, 0.3127f, 0.3290f, sk_primaries);
|
||||
case 8:
|
||||
return skcms_PrimariesToXYZD50(
|
||||
0.681f, 0.319f, 0.243f, 0.692f, 0.145f, 0.049f, 0.310f, 0.316f, sk_primaries);
|
||||
case 9:
|
||||
*sk_primaries = SkNamedGamut::kRec2020;
|
||||
return true;
|
||||
case 10:
|
||||
return skcms_PrimariesToXYZD50(
|
||||
1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f / 3.f, 1.f / 3.f, sk_primaries);
|
||||
case 11:
|
||||
return skcms_PrimariesToXYZD50(
|
||||
0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f, 0.314f, 0.351f, sk_primaries);
|
||||
case 12:
|
||||
return skcms_PrimariesToXYZD50(
|
||||
0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f, 0.3127f, 0.3290f, sk_primaries);
|
||||
case 22:
|
||||
return skcms_PrimariesToXYZD50(
|
||||
0.630f, 0.340f, 0.295f, 0.605f, 0.155f, 0.077f, 0.3127f, 0.3290f, sk_primaries);
|
||||
default:
|
||||
// Reserved.
|
||||
break;
|
||||
}
|
||||
*sk_primaries = SkNamedGamut::kSRGB;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool cicp_get_transfer_fn(uint8_t transfer_characteristics, skcms_TransferFunction* trfn) {
|
||||
// Rec. ITU-T H.273, Table 3.
|
||||
switch (transfer_characteristics) {
|
||||
case 0:
|
||||
// Reserved.
|
||||
break;
|
||||
case 1:
|
||||
*trfn = SkNamedTransferFn::kRec2020;
|
||||
return true;
|
||||
case 2:
|
||||
// Unspecified.
|
||||
break;
|
||||
case 3:
|
||||
// Reserved.
|
||||
break;
|
||||
case 4:
|
||||
*trfn = {2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
|
||||
return true;
|
||||
case 5:
|
||||
*trfn = {2.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
|
||||
return true;
|
||||
case 6:
|
||||
*trfn = SkNamedTransferFn::kRec2020;
|
||||
return true;
|
||||
case 7:
|
||||
*trfn = {2.222222222222f,
|
||||
0.899626676224f,
|
||||
0.100373323776f,
|
||||
0.25f,
|
||||
0.091286342118f,
|
||||
0.f,
|
||||
0.f};
|
||||
return true;
|
||||
case 8:
|
||||
*trfn = SkNamedTransferFn::kLinear;
|
||||
return true;
|
||||
case 9:
|
||||
// Logarithmic transfer characteristic (100:1 range).
|
||||
// Not supported by skcms
|
||||
break;
|
||||
case 10:
|
||||
// Logarithmic transfer characteristic (100 * Sqrt( 10 ) : 1 range).
|
||||
// Not supported by skcms
|
||||
break;
|
||||
case 11:
|
||||
*trfn = SkNamedTransferFn::kSRGB;
|
||||
break;
|
||||
case 12:
|
||||
// Rec. ITU-R BT.1361-0 extended colour gamut system (historical).
|
||||
// Same as kRec709 on positive values, differs on negative values.
|
||||
// Not supported by skcms
|
||||
break;
|
||||
case 13:
|
||||
*trfn = SkNamedTransferFn::kSRGB;
|
||||
return true;
|
||||
case 14:
|
||||
*trfn = SkNamedTransferFn::kRec2020;
|
||||
return true;
|
||||
case 15:
|
||||
*trfn = SkNamedTransferFn::kRec2020;
|
||||
return true;
|
||||
case 16:
|
||||
// Android expects PQ to match 203 nits to SDR white
|
||||
*trfn = {-2.f,
|
||||
-1.55522297832f,
|
||||
1.86045365631f,
|
||||
32 / 2523.0f,
|
||||
2413 / 128.0f,
|
||||
-2392 / 128.0f,
|
||||
8192 / 1305.0f};
|
||||
return true;
|
||||
case 17:
|
||||
*trfn = {2.6f, 1.034080527699f, 0.f, 0.f, 0.f, 0.f, 0.f};
|
||||
return true;
|
||||
case 18:
|
||||
// Android expects HLG to match 203 nits to SDR white
|
||||
if (skcms_TransferFunction_makeScaledHLGish(trfn,
|
||||
0.314509843f,
|
||||
2.f,
|
||||
2.f,
|
||||
1.f / 0.17883277f,
|
||||
0.28466892f,
|
||||
0.55991073f)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// 19-255 Reserved.
|
||||
break;
|
||||
}
|
||||
|
||||
*trfn = SkNamedTransferFn::kSRGB;
|
||||
return false;
|
||||
}
|
||||
|
||||
static sk_sp<SkColorSpace> cicp_get_sk_color_space(uint8_t color_primaries,
|
||||
uint8_t transfer_characteristics,
|
||||
uint8_t matrix_coefficients,
|
||||
uint8_t full_range_flag) {
|
||||
if (matrix_coefficients != 0) return nullptr;
|
||||
|
||||
if (full_range_flag != 1) return nullptr;
|
||||
|
||||
skcms_TransferFunction trfn;
|
||||
if (!cicp_get_transfer_fn(transfer_characteristics, &trfn)) return nullptr;
|
||||
|
||||
skcms_Matrix3x3 primaries;
|
||||
if (!cicp_get_primaries(color_primaries, &primaries)) return nullptr;
|
||||
|
||||
return SkColorSpace::MakeRGB(trfn, primaries);
|
||||
}
|
||||
|
||||
SkAndroidCodec::SkAndroidCodec(SkCodec* codec)
|
||||
: fInfo(codec->getInfo())
|
||||
, fCodec(codec)
|
||||
{}
|
||||
|
||||
SkAndroidCodec::~SkAndroidCodec() {}
|
||||
|
||||
std::unique_ptr<SkAndroidCodec> SkAndroidCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
|
||||
SkPngChunkReader* chunkReader) {
|
||||
auto codec = SkCodec::MakeFromStream(std::move(stream), nullptr, chunkReader);
|
||||
return MakeFromCodec(std::move(codec));
|
||||
}
|
||||
|
||||
std::unique_ptr<SkAndroidCodec> SkAndroidCodec::MakeFromCodec(std::unique_ptr<SkCodec> codec) {
|
||||
if (nullptr == codec) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const SkEncodedImageFormat format = codec->getEncodedFormat();
|
||||
if (format == SkEncodedImageFormat::kAVIF) {
|
||||
if (SkCodecs::HasDecoder("avif")) {
|
||||
// If a dedicated AVIF decoder has been registered, SkAvifCodec can
|
||||
// handle scaling internally.
|
||||
return std::make_unique<SkAndroidCodecAdapter>(codec.release());
|
||||
}
|
||||
// This will fallback to SkHeifCodec, which needs sampling.
|
||||
return std::make_unique<SkSampledCodec>(codec.release());
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case SkEncodedImageFormat::kPNG:
|
||||
case SkEncodedImageFormat::kICO:
|
||||
case SkEncodedImageFormat::kJPEG:
|
||||
case SkEncodedImageFormat::kBMP:
|
||||
case SkEncodedImageFormat::kWBMP:
|
||||
case SkEncodedImageFormat::kHEIF:
|
||||
return std::make_unique<SkSampledCodec>(codec.release());
|
||||
case SkEncodedImageFormat::kGIF:
|
||||
case SkEncodedImageFormat::kWEBP:
|
||||
case SkEncodedImageFormat::kDNG:
|
||||
return std::make_unique<SkAndroidCodecAdapter>(codec.release());
|
||||
case SkEncodedImageFormat::kAVIF: // Handled above
|
||||
case SkEncodedImageFormat::kPKM:
|
||||
case SkEncodedImageFormat::kKTX:
|
||||
case SkEncodedImageFormat::kASTC:
|
||||
case SkEncodedImageFormat::kJPEGXL:
|
||||
return nullptr;
|
||||
}
|
||||
SkUNREACHABLE;
|
||||
}
|
||||
|
||||
std::unique_ptr<SkAndroidCodec> SkAndroidCodec::MakeFromData(sk_sp<SkData> data,
|
||||
SkPngChunkReader* chunkReader) {
|
||||
if (!data) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return MakeFromStream(SkMemoryStream::Make(std::move(data)), chunkReader);
|
||||
}
|
||||
|
||||
SkColorType SkAndroidCodec::computeOutputColorType(SkColorType requestedColorType) {
|
||||
bool highPrecision = fCodec->getEncodedInfo().bitsPerComponent() > 8;
|
||||
uint8_t colorDepth = fCodec->getEncodedInfo().getColorDepth();
|
||||
switch (requestedColorType) {
|
||||
case kARGB_4444_SkColorType:
|
||||
return kN32_SkColorType;
|
||||
case kN32_SkColorType:
|
||||
break;
|
||||
case kAlpha_8_SkColorType:
|
||||
// Fall through to kGray_8. Before kGray_8_SkColorType existed,
|
||||
// we allowed clients to request kAlpha_8 when they wanted a
|
||||
// grayscale decode.
|
||||
case kGray_8_SkColorType:
|
||||
if (kGray_8_SkColorType == this->getInfo().colorType()) {
|
||||
return kGray_8_SkColorType;
|
||||
}
|
||||
break;
|
||||
case kRGB_565_SkColorType:
|
||||
if (kOpaque_SkAlphaType == this->getInfo().alphaType()) {
|
||||
return kRGB_565_SkColorType;
|
||||
}
|
||||
break;
|
||||
case kRGBA_1010102_SkColorType:
|
||||
if (colorDepth == 10) {
|
||||
return kRGBA_1010102_SkColorType;
|
||||
}
|
||||
break;
|
||||
case kRGBA_F16_SkColorType:
|
||||
return kRGBA_F16_SkColorType;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// F16 is the Android default for high precision images.
|
||||
return highPrecision ? kRGBA_F16_SkColorType :
|
||||
(colorDepth == 10 ? kRGBA_1010102_SkColorType : kN32_SkColorType);
|
||||
}
|
||||
|
||||
SkAlphaType SkAndroidCodec::computeOutputAlphaType(bool requestedUnpremul) {
|
||||
if (kOpaque_SkAlphaType == this->getInfo().alphaType()) {
|
||||
return kOpaque_SkAlphaType;
|
||||
}
|
||||
return requestedUnpremul ? kUnpremul_SkAlphaType : kPremul_SkAlphaType;
|
||||
}
|
||||
|
||||
sk_sp<SkColorSpace> SkAndroidCodec::computeOutputColorSpace(SkColorType outputColorType,
|
||||
sk_sp<SkColorSpace> prefColorSpace) {
|
||||
switch (outputColorType) {
|
||||
case kRGBA_F16_SkColorType:
|
||||
case kRGB_565_SkColorType:
|
||||
case kRGBA_8888_SkColorType:
|
||||
case kBGRA_8888_SkColorType:
|
||||
case kRGBA_1010102_SkColorType: {
|
||||
// If |prefColorSpace| is supplied, choose it.
|
||||
if (prefColorSpace) {
|
||||
return prefColorSpace;
|
||||
}
|
||||
|
||||
const skcms_ICCProfile* encodedProfile = fCodec->getEncodedInfo().profile();
|
||||
if (encodedProfile) {
|
||||
// Prefer CICP information if it exists.
|
||||
if (encodedProfile->has_CICP) {
|
||||
const auto cicpColorSpace =
|
||||
cicp_get_sk_color_space(encodedProfile->CICP.color_primaries,
|
||||
encodedProfile->CICP.transfer_characteristics,
|
||||
encodedProfile->CICP.matrix_coefficients,
|
||||
encodedProfile->CICP.video_full_range_flag);
|
||||
if (cicpColorSpace) {
|
||||
return cicpColorSpace;
|
||||
}
|
||||
}
|
||||
if (auto encodedSpace = SkColorSpace::Make(*encodedProfile)) {
|
||||
// Leave the pixels in the encoded color space. Color space conversion
|
||||
// will be handled after decode time.
|
||||
return encodedSpace;
|
||||
}
|
||||
|
||||
if (encodedProfile->has_toXYZD50) {
|
||||
return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB,
|
||||
encodedProfile->toXYZD50);
|
||||
}
|
||||
}
|
||||
|
||||
return SkColorSpace::MakeSRGB();
|
||||
}
|
||||
default:
|
||||
// Color correction not supported for kGray.
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static bool supports_any_down_scale(const SkCodec* codec) {
|
||||
return codec->getEncodedFormat() == SkEncodedImageFormat::kWEBP;
|
||||
}
|
||||
|
||||
// There are a variety of ways two SkISizes could be compared. This method
|
||||
// returns true if either dimensions of a is < that of b.
|
||||
// computeSampleSize also uses the opposite, which means that both
|
||||
// dimensions of a >= b.
|
||||
static inline bool smaller_than(const SkISize& a, const SkISize& b) {
|
||||
return a.width() < b.width() || a.height() < b.height();
|
||||
}
|
||||
|
||||
// Both dimensions of a > that of b.
|
||||
static inline bool strictly_bigger_than(const SkISize& a, const SkISize& b) {
|
||||
return a.width() > b.width() && a.height() > b.height();
|
||||
}
|
||||
|
||||
int SkAndroidCodec::computeSampleSize(SkISize* desiredSize) const {
|
||||
SkASSERT(desiredSize);
|
||||
|
||||
const auto origDims = fCodec->dimensions();
|
||||
if (!desiredSize || *desiredSize == origDims) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (smaller_than(origDims, *desiredSize)) {
|
||||
*desiredSize = origDims;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Handle bad input:
|
||||
if (desiredSize->width() < 1 || desiredSize->height() < 1) {
|
||||
*desiredSize = SkISize::Make(std::max(1, desiredSize->width()),
|
||||
std::max(1, desiredSize->height()));
|
||||
}
|
||||
|
||||
if (supports_any_down_scale(fCodec.get())) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sampleX = origDims.width() / desiredSize->width();
|
||||
int sampleY = origDims.height() / desiredSize->height();
|
||||
int sampleSize = std::min(sampleX, sampleY);
|
||||
auto computedSize = this->getSampledDimensions(sampleSize);
|
||||
if (computedSize == *desiredSize) {
|
||||
return sampleSize;
|
||||
}
|
||||
|
||||
if (computedSize == origDims || sampleSize == 1) {
|
||||
// Cannot downscale
|
||||
*desiredSize = computedSize;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (strictly_bigger_than(computedSize, *desiredSize)) {
|
||||
// See if there is a tighter fit.
|
||||
while (true) {
|
||||
auto smaller = this->getSampledDimensions(sampleSize + 1);
|
||||
if (smaller == *desiredSize) {
|
||||
return sampleSize + 1;
|
||||
}
|
||||
if (smaller == computedSize || smaller_than(smaller, *desiredSize)) {
|
||||
// Cannot get any smaller without being smaller than desired.
|
||||
*desiredSize = computedSize;
|
||||
return sampleSize;
|
||||
}
|
||||
|
||||
sampleSize++;
|
||||
computedSize = smaller;
|
||||
}
|
||||
|
||||
SkASSERT(false);
|
||||
}
|
||||
|
||||
if (!smaller_than(computedSize, *desiredSize)) {
|
||||
// This means one of the computed dimensions is equal to desired, and
|
||||
// the other is bigger. This is as close as we can get.
|
||||
*desiredSize = computedSize;
|
||||
return sampleSize;
|
||||
}
|
||||
|
||||
// computedSize is too small. Make it larger.
|
||||
while (sampleSize > 2) {
|
||||
auto bigger = this->getSampledDimensions(sampleSize - 1);
|
||||
if (bigger == *desiredSize || !smaller_than(bigger, *desiredSize)) {
|
||||
*desiredSize = bigger;
|
||||
return sampleSize - 1;
|
||||
}
|
||||
sampleSize--;
|
||||
}
|
||||
|
||||
*desiredSize = origDims;
|
||||
return 1;
|
||||
}
|
||||
|
||||
SkISize SkAndroidCodec::getSampledDimensions(int sampleSize) const {
|
||||
if (!is_valid_sample_size(sampleSize)) {
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
// Fast path for when we are not scaling.
|
||||
if (1 == sampleSize) {
|
||||
return fCodec->dimensions();
|
||||
}
|
||||
|
||||
return this->onGetSampledDimensions(sampleSize);
|
||||
}
|
||||
|
||||
bool SkAndroidCodec::getSupportedSubset(SkIRect* desiredSubset) const {
|
||||
if (!desiredSubset || !is_valid_subset(*desiredSubset, fCodec->dimensions())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this->onGetSupportedSubset(desiredSubset);
|
||||
}
|
||||
|
||||
SkISize SkAndroidCodec::getSampledSubsetDimensions(int sampleSize, const SkIRect& subset) const {
|
||||
if (!is_valid_sample_size(sampleSize)) {
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
// We require that the input subset is a subset that is supported by SkAndroidCodec.
|
||||
// We test this by calling getSupportedSubset() and verifying that no modifications
|
||||
// are made to the subset.
|
||||
SkIRect copySubset = subset;
|
||||
if (!this->getSupportedSubset(©Subset) || copySubset != subset) {
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
// If the subset is the entire image, for consistency, use getSampledDimensions().
|
||||
if (fCodec->dimensions() == subset.size()) {
|
||||
return this->getSampledDimensions(sampleSize);
|
||||
}
|
||||
|
||||
// This should perhaps call a virtual function, but currently both of our subclasses
|
||||
// want the same implementation.
|
||||
return {get_scaled_dimension(subset.width(), sampleSize),
|
||||
get_scaled_dimension(subset.height(), sampleSize)};
|
||||
}
|
||||
|
||||
SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo& requestInfo,
|
||||
void* requestPixels, size_t requestRowBytes, const AndroidOptions* options) {
|
||||
if (!requestPixels) {
|
||||
return SkCodec::kInvalidParameters;
|
||||
}
|
||||
if (requestRowBytes < requestInfo.minRowBytes()) {
|
||||
return SkCodec::kInvalidParameters;
|
||||
}
|
||||
|
||||
AndroidOptions defaultOptions;
|
||||
if (!options) {
|
||||
options = &defaultOptions;
|
||||
} else {
|
||||
if (options->fSubset) {
|
||||
if (!is_valid_subset(*options->fSubset, fCodec->dimensions())) {
|
||||
return SkCodec::kInvalidParameters;
|
||||
}
|
||||
|
||||
if (SkIRect::MakeSize(fCodec->dimensions()) == *options->fSubset) {
|
||||
// The caller wants the whole thing, rather than a subset. Modify
|
||||
// the AndroidOptions passed to onGetAndroidPixels to not specify
|
||||
// a subset.
|
||||
defaultOptions = *options;
|
||||
defaultOptions.fSubset = nullptr;
|
||||
options = &defaultOptions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We may need to have handleFrameIndex recursively call this method
|
||||
// to resolve one frame depending on another. The recursion stops
|
||||
// when we find a frame which does not require an earlier frame
|
||||
// e.g. frame->getRequiredFrame() returns kNoFrame
|
||||
auto getPixelsFn = [&](const SkImageInfo& info, void* pixels, size_t rowBytes,
|
||||
const SkCodec::Options& opts, int requiredFrame
|
||||
) -> SkCodec::Result {
|
||||
SkAndroidCodec::AndroidOptions prevFrameOptions(
|
||||
reinterpret_cast<const SkAndroidCodec::AndroidOptions&>(opts));
|
||||
prevFrameOptions.fFrameIndex = requiredFrame;
|
||||
return this->getAndroidPixels(info, pixels, rowBytes, &prevFrameOptions);
|
||||
};
|
||||
if (auto result = fCodec->handleFrameIndex(requestInfo, requestPixels, requestRowBytes,
|
||||
*options, getPixelsFn); result != SkCodec::kSuccess) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return this->onGetAndroidPixels(requestInfo, requestPixels, requestRowBytes, *options);
|
||||
}
|
||||
|
||||
SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo& info, void* pixels,
|
||||
size_t rowBytes) {
|
||||
return this->getAndroidPixels(info, pixels, rowBytes, nullptr);
|
||||
}
|
||||
|
||||
bool SkAndroidCodec::getGainmapAndroidCodec(SkGainmapInfo* info,
|
||||
std::unique_ptr<SkAndroidCodec>* outCodec) {
|
||||
if (outCodec) {
|
||||
std::unique_ptr<SkCodec> gainmapCodec;
|
||||
if (!fCodec->onGetGainmapCodec(info, &gainmapCodec)) {
|
||||
return false;
|
||||
}
|
||||
*outCodec = MakeFromCodec(std::move(gainmapCodec));
|
||||
return true;
|
||||
}
|
||||
return fCodec->onGetGainmapCodec(info, nullptr);
|
||||
}
|
||||
|
||||
bool SkAndroidCodec::getAndroidGainmap(SkGainmapInfo* info,
|
||||
std::unique_ptr<SkStream>* outGainmapImageStream) {
|
||||
return fCodec->onGetGainmapInfo(info, outGainmapImageStream);
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "src/codec/SkAndroidCodecAdapter.h"
|
||||
#include "src/codec/SkCodecPriv.h"
|
||||
|
||||
struct SkIRect;
|
||||
struct SkImageInfo;
|
||||
|
||||
SkAndroidCodecAdapter::SkAndroidCodecAdapter(SkCodec* codec)
|
||||
: INHERITED(codec)
|
||||
{}
|
||||
|
||||
SkISize SkAndroidCodecAdapter::onGetSampledDimensions(int sampleSize) const {
|
||||
float scale = get_scale_from_sample_size(sampleSize);
|
||||
return this->codec()->getScaledDimensions(scale);
|
||||
}
|
||||
|
||||
bool SkAndroidCodecAdapter::onGetSupportedSubset(SkIRect* desiredSubset) const {
|
||||
return this->codec()->getValidSubset(desiredSubset);
|
||||
}
|
||||
|
||||
SkCodec::Result SkAndroidCodecAdapter::onGetAndroidPixels(const SkImageInfo& info, void* pixels,
|
||||
size_t rowBytes, const AndroidOptions& options) {
|
||||
return this->codec()->getPixels(info, pixels, rowBytes, &options);
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#ifndef SkAndroidCodecAdapter_DEFINED
|
||||
#define SkAndroidCodecAdapter_DEFINED
|
||||
|
||||
#include "include/codec/SkAndroidCodec.h"
|
||||
#include "include/codec/SkCodec.h"
|
||||
#include "include/core/SkSize.h"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
struct SkIRect;
|
||||
struct SkImageInfo;
|
||||
|
||||
/**
|
||||
* This class wraps SkCodec to implement the functionality of SkAndroidCodec.
|
||||
* The underlying SkCodec implements sampled decodes. SkCodec's that do not
|
||||
* implement that are wrapped with SkSampledCodec instead.
|
||||
*/
|
||||
class SkAndroidCodecAdapter : public SkAndroidCodec {
|
||||
public:
|
||||
|
||||
explicit SkAndroidCodecAdapter(SkCodec*);
|
||||
|
||||
~SkAndroidCodecAdapter() override {}
|
||||
|
||||
protected:
|
||||
|
||||
SkISize onGetSampledDimensions(int sampleSize) const override;
|
||||
|
||||
bool onGetSupportedSubset(SkIRect* desiredSubset) const override;
|
||||
|
||||
SkCodec::Result onGetAndroidPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
|
||||
const AndroidOptions& options) override;
|
||||
|
||||
private:
|
||||
|
||||
using INHERITED = SkAndroidCodec;
|
||||
};
|
||||
#endif // SkAndroidCodecAdapter_DEFINED
|
||||
@@ -1,274 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "src/codec/SkAvifCodec.h"
|
||||
|
||||
#include "include/codec/SkAvifDecoder.h"
|
||||
#include "include/codec/SkCodec.h"
|
||||
#include "include/codec/SkCodecAnimation.h"
|
||||
#include "include/core/SkColorType.h"
|
||||
#include "include/core/SkImageInfo.h"
|
||||
#include "include/core/SkSize.h"
|
||||
#include "include/core/SkStream.h"
|
||||
#include "include/core/SkTypes.h"
|
||||
#include "modules/skcms/skcms.h"
|
||||
#include "src/core/SkStreamPriv.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
|
||||
#include "avif/avif.h"
|
||||
|
||||
void AvifDecoderDeleter::operator()(avifDecoder* decoder) const {
|
||||
if (decoder != nullptr) {
|
||||
avifDecoderDestroy(decoder);
|
||||
}
|
||||
}
|
||||
|
||||
bool SkAvifCodec::IsAvif(const void* buffer, size_t bytesRead) {
|
||||
avifROData avifData = {static_cast<const uint8_t*>(buffer), bytesRead};
|
||||
bool isAvif = avifPeekCompatibleFileType(&avifData) == AVIF_TRUE;
|
||||
if (isAvif) return true;
|
||||
// Peeking sometimes fails if the ftyp box is too large. Check the signature
|
||||
// just to be sure.
|
||||
const char* bytes = static_cast<const char*>(buffer);
|
||||
isAvif = bytesRead >= 12 && !memcmp(&bytes[4], "ftyp", 4) &&
|
||||
(!memcmp(&bytes[8], "avif", 4) || !memcmp(&bytes[8], "avis", 4));
|
||||
return isAvif;
|
||||
}
|
||||
|
||||
std::unique_ptr<SkCodec> SkAvifCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
|
||||
Result* result) {
|
||||
SkASSERT(result);
|
||||
if (!stream) {
|
||||
*result = SkCodec::kInvalidInput;
|
||||
return nullptr;
|
||||
}
|
||||
AvifDecoder avifDecoder(avifDecoderCreate());
|
||||
if (avifDecoder == nullptr) {
|
||||
*result = SkCodec::kInternalError;
|
||||
return nullptr;
|
||||
}
|
||||
avifDecoder->ignoreXMP = AVIF_TRUE;
|
||||
avifDecoder->ignoreExif = AVIF_TRUE;
|
||||
avifDecoder->allowProgressive = AVIF_FALSE;
|
||||
avifDecoder->allowIncremental = AVIF_FALSE;
|
||||
avifDecoder->strictFlags = AVIF_STRICT_DISABLED;
|
||||
// TODO(vigneshv): Enable threading based on number of CPU cores available.
|
||||
avifDecoder->maxThreads = 1;
|
||||
|
||||
// libavif needs a contiguous data buffer.
|
||||
sk_sp<SkData> data = nullptr;
|
||||
if (stream->getMemoryBase()) {
|
||||
// It is safe to make without copy because we'll hold onto the stream.
|
||||
data = SkData::MakeWithoutCopy(stream->getMemoryBase(), stream->getLength());
|
||||
} else {
|
||||
data = SkCopyStreamToData(stream.get());
|
||||
// If we are forced to copy the stream to a data, we can go ahead and
|
||||
// delete the stream.
|
||||
stream.reset(nullptr);
|
||||
}
|
||||
|
||||
avifResult res = avifDecoderSetIOMemory(avifDecoder.get(), data->bytes(), data->size());
|
||||
if (res != AVIF_RESULT_OK) {
|
||||
*result = SkCodec::kInternalError;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
res = avifDecoderParse(avifDecoder.get());
|
||||
if (res != AVIF_RESULT_OK) {
|
||||
*result = SkCodec::kInvalidInput;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<SkEncodedInfo::ICCProfile> profile = nullptr;
|
||||
// TODO(vigneshv): Get ICC Profile from the avif decoder.
|
||||
|
||||
const int bitsPerComponent = avifDecoder->image->depth > 8 ? 16 : 8;
|
||||
SkEncodedInfo::Color color;
|
||||
SkEncodedInfo::Alpha alpha;
|
||||
if (avifDecoder->alphaPresent) {
|
||||
color = SkEncodedInfo::kRGBA_Color;
|
||||
alpha = SkEncodedInfo::kUnpremul_Alpha;
|
||||
} else {
|
||||
color = SkEncodedInfo::kRGB_Color;
|
||||
alpha = SkEncodedInfo::kOpaque_Alpha;
|
||||
}
|
||||
SkEncodedInfo info = SkEncodedInfo::Make(avifDecoder->image->width,
|
||||
avifDecoder->image->height,
|
||||
color,
|
||||
alpha,
|
||||
bitsPerComponent,
|
||||
std::move(profile),
|
||||
avifDecoder->image->depth);
|
||||
bool animation = avifDecoder->imageCount > 1;
|
||||
*result = kSuccess;
|
||||
return std::unique_ptr<SkCodec>(new SkAvifCodec(std::move(info),
|
||||
std::move(stream),
|
||||
std::move(data),
|
||||
std::move(avifDecoder),
|
||||
kDefault_SkEncodedOrigin,
|
||||
animation));
|
||||
}
|
||||
|
||||
SkAvifCodec::SkAvifCodec(SkEncodedInfo&& info,
|
||||
std::unique_ptr<SkStream> stream,
|
||||
sk_sp<SkData> data,
|
||||
AvifDecoder avifDecoder,
|
||||
SkEncodedOrigin origin,
|
||||
bool useAnimation)
|
||||
: INHERITED(std::move(info), skcms_PixelFormat_RGBA_8888, std::move(stream), origin)
|
||||
, fData(std::move(data))
|
||||
, fAvifDecoder(std::move(avifDecoder))
|
||||
, fUseAnimation(useAnimation) {}
|
||||
|
||||
int SkAvifCodec::onGetFrameCount() {
|
||||
if (!fUseAnimation) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (fFrameHolder.size() == 0) {
|
||||
if (fAvifDecoder->imageCount <= 1) {
|
||||
fUseAnimation = false;
|
||||
return 1;
|
||||
}
|
||||
fFrameHolder.reserve(fAvifDecoder->imageCount);
|
||||
for (int i = 0; i < fAvifDecoder->imageCount; i++) {
|
||||
Frame* frame = fFrameHolder.appendNewFrame(fAvifDecoder->alphaPresent == AVIF_TRUE);
|
||||
frame->setXYWH(0, 0, fAvifDecoder->image->width, fAvifDecoder->image->height);
|
||||
frame->setDisposalMethod(SkCodecAnimation::DisposalMethod::kKeep);
|
||||
avifImageTiming timing;
|
||||
avifDecoderNthImageTiming(fAvifDecoder.get(), i, &timing);
|
||||
frame->setDuration(timing.duration * 1000);
|
||||
frame->setRequiredFrame(SkCodec::kNoFrame);
|
||||
frame->setHasAlpha(fAvifDecoder->alphaPresent == AVIF_TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
return fFrameHolder.size();
|
||||
}
|
||||
|
||||
const SkFrame* SkAvifCodec::FrameHolder::onGetFrame(int i) const {
|
||||
return static_cast<const SkFrame*>(this->frame(i));
|
||||
}
|
||||
|
||||
SkAvifCodec::Frame* SkAvifCodec::FrameHolder::appendNewFrame(bool hasAlpha) {
|
||||
const int i = this->size();
|
||||
fFrames.emplace_back(i,
|
||||
hasAlpha ? SkEncodedInfo::kUnpremul_Alpha : SkEncodedInfo::kOpaque_Alpha);
|
||||
return &fFrames[i];
|
||||
}
|
||||
|
||||
const SkAvifCodec::Frame* SkAvifCodec::FrameHolder::frame(int i) const {
|
||||
SkASSERT(i >= 0 && i < this->size());
|
||||
return &fFrames[i];
|
||||
}
|
||||
|
||||
bool SkAvifCodec::onGetFrameInfo(int i, FrameInfo* frameInfo) const {
|
||||
if (i >= fFrameHolder.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Frame* frame = fFrameHolder.frame(i);
|
||||
if (!frame) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (frameInfo) {
|
||||
frame->fillIn(frameInfo, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int SkAvifCodec::onGetRepetitionCount() { return kRepetitionCountInfinite; }
|
||||
|
||||
SkCodec::Result SkAvifCodec::onGetPixels(const SkImageInfo& dstInfo,
|
||||
void* dst,
|
||||
size_t dstRowBytes,
|
||||
const Options& options,
|
||||
int* rowsDecoded) {
|
||||
if (options.fSubset) {
|
||||
return kUnimplemented;
|
||||
}
|
||||
|
||||
const SkColorType dstColorType = dstInfo.colorType();
|
||||
if (dstColorType != kRGBA_8888_SkColorType && dstColorType != kRGBA_F16_SkColorType) {
|
||||
// TODO(vigneshv): Check if more color types need to be supported.
|
||||
// Currently android supports at least RGB565 and BGRA8888 which is not
|
||||
// supported here.
|
||||
return kUnimplemented;
|
||||
}
|
||||
|
||||
avifResult result = avifDecoderNthImage(fAvifDecoder.get(), options.fFrameIndex);
|
||||
if (result != AVIF_RESULT_OK) {
|
||||
return kInvalidInput;
|
||||
}
|
||||
|
||||
if (this->dimensions() != dstInfo.dimensions()) {
|
||||
result = avifImageScale(
|
||||
fAvifDecoder->image, dstInfo.width(), dstInfo.height(), &fAvifDecoder->diag);
|
||||
if (result != AVIF_RESULT_OK) {
|
||||
return kInvalidInput;
|
||||
}
|
||||
}
|
||||
|
||||
avifRGBImage rgbImage;
|
||||
avifRGBImageSetDefaults(&rgbImage, fAvifDecoder->image);
|
||||
|
||||
if (dstColorType == kRGBA_8888_SkColorType) {
|
||||
rgbImage.depth = 8;
|
||||
} else if (dstColorType == kRGBA_F16_SkColorType) {
|
||||
rgbImage.depth = 16;
|
||||
rgbImage.isFloat = AVIF_TRUE;
|
||||
}
|
||||
|
||||
rgbImage.pixels = static_cast<uint8_t*>(dst);
|
||||
rgbImage.rowBytes = dstRowBytes;
|
||||
rgbImage.chromaUpsampling = AVIF_CHROMA_UPSAMPLING_FASTEST;
|
||||
|
||||
result = avifImageYUVToRGB(fAvifDecoder->image, &rgbImage);
|
||||
if (result != AVIF_RESULT_OK) {
|
||||
return kInvalidInput;
|
||||
}
|
||||
|
||||
*rowsDecoded = fAvifDecoder->image->height;
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
namespace SkAvifDecoder {
|
||||
namespace LibAvif {
|
||||
|
||||
bool IsAvif(const void* data, size_t len) {
|
||||
return SkAvifCodec::IsAvif(data, len);
|
||||
}
|
||||
|
||||
std::unique_ptr<SkCodec> Decode(std::unique_ptr<SkStream> stream,
|
||||
SkCodec::Result* outResult,
|
||||
SkCodecs::DecodeContext) {
|
||||
SkCodec::Result resultStorage;
|
||||
if (!outResult) {
|
||||
outResult = &resultStorage;
|
||||
}
|
||||
return SkAvifCodec::MakeFromStream(std::move(stream), outResult);
|
||||
}
|
||||
|
||||
std::unique_ptr<SkCodec> Decode(sk_sp<SkData> data,
|
||||
SkCodec::Result* outResult,
|
||||
SkCodecs::DecodeContext) {
|
||||
if (!data) {
|
||||
if (outResult) {
|
||||
*outResult = SkCodec::kInvalidInput;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
return Decode(SkMemoryStream::Make(std::move(data)), outResult, nullptr);
|
||||
}
|
||||
|
||||
} // namespace LibAvif
|
||||
} // namespace SkAvifDecoder
|
||||
@@ -1,110 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkAvifCodec_DEFINED
|
||||
#define SkAvifCodec_DEFINED
|
||||
|
||||
#include "include/codec/SkEncodedImageFormat.h"
|
||||
#include "include/codec/SkEncodedOrigin.h"
|
||||
#include "include/core/SkData.h"
|
||||
#include "include/core/SkRefCnt.h"
|
||||
#include "include/private/SkEncodedInfo.h"
|
||||
#include "src/codec/SkFrameHolder.h"
|
||||
#include "src/codec/SkScalingCodec.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
class SkCodec;
|
||||
class SkStream;
|
||||
struct SkImageInfo;
|
||||
struct avifDecoder;
|
||||
struct AvifDecoderDeleter {
|
||||
void operator()(avifDecoder* decoder) const;
|
||||
};
|
||||
using AvifDecoder = std::unique_ptr<avifDecoder, AvifDecoderDeleter>;
|
||||
|
||||
class SkAvifCodec : public SkScalingCodec {
|
||||
public:
|
||||
/*
|
||||
* Returns true if an AVIF image is detected. Returns false otherwise.
|
||||
*/
|
||||
static bool IsAvif(const void*, size_t);
|
||||
|
||||
/*
|
||||
* Assumes IsAvif() was called and it returned true.
|
||||
*/
|
||||
static std::unique_ptr<SkCodec> MakeFromStream(std::unique_ptr<SkStream>, Result*);
|
||||
|
||||
protected:
|
||||
Result onGetPixels(const SkImageInfo& dstInfo,
|
||||
void* dst,
|
||||
size_t dstRowBytes,
|
||||
const Options& options,
|
||||
int* rowsDecoded) override;
|
||||
|
||||
SkEncodedImageFormat onGetEncodedFormat() const override { return SkEncodedImageFormat::kAVIF; }
|
||||
|
||||
int onGetFrameCount() override;
|
||||
bool onGetFrameInfo(int, FrameInfo*) const override;
|
||||
int onGetRepetitionCount() override;
|
||||
const SkFrameHolder* getFrameHolder() const override { return &fFrameHolder; }
|
||||
|
||||
private:
|
||||
SkAvifCodec(SkEncodedInfo&&,
|
||||
std::unique_ptr<SkStream>,
|
||||
sk_sp<SkData>,
|
||||
AvifDecoder,
|
||||
SkEncodedOrigin,
|
||||
bool);
|
||||
|
||||
// fAvifDecoder has a pointer to this data. This should not be freed until
|
||||
// the decode is completed. To ensure that, we declare this before
|
||||
// fAvifDecoder.
|
||||
sk_sp<SkData> fData;
|
||||
|
||||
AvifDecoder fAvifDecoder;
|
||||
bool fUseAnimation;
|
||||
|
||||
class Frame : public SkFrame {
|
||||
public:
|
||||
Frame(int i, SkEncodedInfo::Alpha alpha) : INHERITED(i), fReportedAlpha(alpha) {}
|
||||
|
||||
protected:
|
||||
SkEncodedInfo::Alpha onReportedAlpha() const override { return fReportedAlpha; }
|
||||
|
||||
private:
|
||||
const SkEncodedInfo::Alpha fReportedAlpha;
|
||||
|
||||
using INHERITED = SkFrame;
|
||||
};
|
||||
|
||||
class FrameHolder : public SkFrameHolder {
|
||||
public:
|
||||
~FrameHolder() override {}
|
||||
void setScreenSize(int w, int h) {
|
||||
fScreenWidth = w;
|
||||
fScreenHeight = h;
|
||||
}
|
||||
Frame* appendNewFrame(bool hasAlpha);
|
||||
const Frame* frame(int i) const;
|
||||
int size() const { return static_cast<int>(fFrames.size()); }
|
||||
void reserve(int size) { fFrames.reserve(size); }
|
||||
|
||||
protected:
|
||||
const SkFrame* onGetFrame(int i) const override;
|
||||
|
||||
private:
|
||||
std::vector<Frame> fFrames;
|
||||
};
|
||||
|
||||
FrameHolder fFrameHolder;
|
||||
using INHERITED = SkScalingCodec;
|
||||
};
|
||||
|
||||
#endif // SkAvifCodec_DEFINED
|
||||
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#include "src/codec/SkBmpBaseCodec.h"
|
||||
|
||||
#include "include/core/SkStream.h"
|
||||
#include "include/private/SkEncodedInfo.h"
|
||||
#include "include/private/base/SkMalloc.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
SkBmpBaseCodec::~SkBmpBaseCodec() {}
|
||||
|
||||
SkBmpBaseCodec::SkBmpBaseCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
|
||||
uint16_t bitsPerPixel, SkCodec::SkScanlineOrder rowOrder)
|
||||
: INHERITED(std::move(info), std::move(stream), bitsPerPixel, rowOrder)
|
||||
, fSrcBuffer(sk_malloc_canfail(this->srcRowBytes()))
|
||||
{}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#ifndef SkBmpBaseCodec_DEFINED
|
||||
#define SkBmpBaseCodec_DEFINED
|
||||
|
||||
#include "include/codec/SkCodec.h"
|
||||
#include "include/private/base/SkTemplates.h"
|
||||
#include "src/codec/SkBmpCodec.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
class SkStream;
|
||||
struct SkEncodedInfo;
|
||||
|
||||
/*
|
||||
* Common base class for SkBmpStandardCodec and SkBmpMaskCodec.
|
||||
*/
|
||||
class SkBmpBaseCodec : public SkBmpCodec {
|
||||
public:
|
||||
~SkBmpBaseCodec() override;
|
||||
|
||||
/*
|
||||
* Whether fSrcBuffer was successfully created.
|
||||
*
|
||||
* If false, this Codec must not be used.
|
||||
*/
|
||||
bool didCreateSrcBuffer() const { return fSrcBuffer != nullptr; }
|
||||
|
||||
protected:
|
||||
SkBmpBaseCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream>,
|
||||
uint16_t bitsPerPixel, SkCodec::SkScanlineOrder rowOrder);
|
||||
|
||||
uint8_t* srcBuffer() { return reinterpret_cast<uint8_t*>(fSrcBuffer.get()); }
|
||||
|
||||
private:
|
||||
skia_private::UniqueVoidPtr fSrcBuffer;
|
||||
|
||||
using INHERITED = SkBmpCodec;
|
||||
};
|
||||
#endif // SkBmpBaseCodec_DEFINED
|
||||
@@ -1,695 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "src/codec/SkBmpCodec.h"
|
||||
|
||||
#include "include/codec/SkBmpDecoder.h"
|
||||
#include "include/core/SkData.h"
|
||||
#include "include/core/SkImageInfo.h"
|
||||
#include "include/core/SkRefCnt.h"
|
||||
#include "include/core/SkSize.h"
|
||||
#include "include/core/SkStream.h"
|
||||
#include "include/private/SkEncodedInfo.h"
|
||||
#include "include/private/base/SkAlign.h"
|
||||
#include "src/codec/SkBmpMaskCodec.h"
|
||||
#include "src/codec/SkBmpRLECodec.h"
|
||||
#include "src/codec/SkBmpStandardCodec.h"
|
||||
#include "src/codec/SkCodecPriv.h"
|
||||
#include "src/core/SkMasks.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
/*
|
||||
* Defines the version and type of the second bitmap header
|
||||
*/
|
||||
enum BmpHeaderType {
|
||||
kInfoV1_BmpHeaderType,
|
||||
kInfoV2_BmpHeaderType,
|
||||
kInfoV3_BmpHeaderType,
|
||||
kInfoV4_BmpHeaderType,
|
||||
kInfoV5_BmpHeaderType,
|
||||
kOS2V1_BmpHeaderType,
|
||||
kOS2VX_BmpHeaderType,
|
||||
kUnknown_BmpHeaderType
|
||||
};
|
||||
|
||||
/*
|
||||
* Possible bitmap compression types
|
||||
*/
|
||||
enum BmpCompressionMethod {
|
||||
kNone_BmpCompressionMethod = 0,
|
||||
k8BitRLE_BmpCompressionMethod = 1,
|
||||
k4BitRLE_BmpCompressionMethod = 2,
|
||||
kBitMasks_BmpCompressionMethod = 3,
|
||||
kJpeg_BmpCompressionMethod = 4,
|
||||
kPng_BmpCompressionMethod = 5,
|
||||
kAlphaBitMasks_BmpCompressionMethod = 6,
|
||||
kCMYK_BmpCompressionMethod = 11,
|
||||
kCMYK8BitRLE_BmpCompressionMethod = 12,
|
||||
kCMYK4BitRLE_BmpCompressionMethod = 13
|
||||
};
|
||||
|
||||
/*
|
||||
* Used to define the input format of the bmp
|
||||
*/
|
||||
enum BmpInputFormat {
|
||||
kStandard_BmpInputFormat,
|
||||
kRLE_BmpInputFormat,
|
||||
kBitMask_BmpInputFormat,
|
||||
kUnknown_BmpInputFormat
|
||||
};
|
||||
|
||||
/*
|
||||
* Checks the start of the stream to see if the image is a bitmap
|
||||
*/
|
||||
bool SkBmpCodec::IsBmp(const void* buffer, size_t bytesRead) {
|
||||
// TODO: Support "IC", "PT", "CI", "CP", "BA"
|
||||
const char bmpSig[] = { 'B', 'M' };
|
||||
return bytesRead >= sizeof(bmpSig) && !memcmp(buffer, bmpSig, sizeof(bmpSig));
|
||||
}
|
||||
|
||||
/*
|
||||
* Assumes IsBmp was called and returned true
|
||||
* Creates a bmp decoder
|
||||
* Reads enough of the stream to determine the image format
|
||||
*/
|
||||
std::unique_ptr<SkCodec> SkBmpCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
|
||||
Result* result) {
|
||||
return SkBmpCodec::MakeFromStream(std::move(stream), result, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a bmp decoder for a bmp embedded in ico
|
||||
* Reads enough of the stream to determine the image format
|
||||
*/
|
||||
std::unique_ptr<SkCodec> SkBmpCodec::MakeFromIco(std::unique_ptr<SkStream> stream, Result* result) {
|
||||
return SkBmpCodec::MakeFromStream(std::move(stream), result, true);
|
||||
}
|
||||
|
||||
// Header size constants
|
||||
static constexpr uint32_t kBmpHeaderBytes = 14;
|
||||
static constexpr uint32_t kBmpHeaderBytesPlusFour = kBmpHeaderBytes + 4;
|
||||
static constexpr uint32_t kBmpOS2V1Bytes = 12;
|
||||
static constexpr uint32_t kBmpOS2V2Bytes = 64;
|
||||
static constexpr uint32_t kBmpInfoBaseBytes = 16;
|
||||
static constexpr uint32_t kBmpInfoV1Bytes = 40;
|
||||
static constexpr uint32_t kBmpInfoV2Bytes = 52;
|
||||
static constexpr uint32_t kBmpInfoV3Bytes = 56;
|
||||
static constexpr uint32_t kBmpInfoV4Bytes = 108;
|
||||
static constexpr uint32_t kBmpInfoV5Bytes = 124;
|
||||
static constexpr uint32_t kBmpMaskBytes = 12;
|
||||
|
||||
static BmpHeaderType get_header_type(size_t infoBytes) {
|
||||
if (infoBytes >= kBmpInfoBaseBytes) {
|
||||
// Check the version of the header
|
||||
switch (infoBytes) {
|
||||
case kBmpInfoV1Bytes:
|
||||
return kInfoV1_BmpHeaderType;
|
||||
case kBmpInfoV2Bytes:
|
||||
return kInfoV2_BmpHeaderType;
|
||||
case kBmpInfoV3Bytes:
|
||||
return kInfoV3_BmpHeaderType;
|
||||
case kBmpInfoV4Bytes:
|
||||
return kInfoV4_BmpHeaderType;
|
||||
case kBmpInfoV5Bytes:
|
||||
return kInfoV5_BmpHeaderType;
|
||||
case 16:
|
||||
case 20:
|
||||
case 24:
|
||||
case 28:
|
||||
case 32:
|
||||
case 36:
|
||||
case 42:
|
||||
case 46:
|
||||
case 48:
|
||||
case 60:
|
||||
case kBmpOS2V2Bytes:
|
||||
return kOS2VX_BmpHeaderType;
|
||||
default:
|
||||
SkCodecPrintf("Error: unknown bmp header format.\n");
|
||||
return kUnknown_BmpHeaderType;
|
||||
}
|
||||
} if (infoBytes >= kBmpOS2V1Bytes) {
|
||||
// The OS2V1 is treated separately because it has a unique format
|
||||
return kOS2V1_BmpHeaderType;
|
||||
} else {
|
||||
// There are no valid bmp headers
|
||||
SkCodecPrintf("Error: second bitmap header size is invalid.\n");
|
||||
return kUnknown_BmpHeaderType;
|
||||
}
|
||||
}
|
||||
|
||||
SkCodec::Result SkBmpCodec::ReadHeader(SkStream* stream, bool inIco,
|
||||
std::unique_ptr<SkCodec>* codecOut) {
|
||||
// The total bytes in the bmp file
|
||||
// We only need to use this value for RLE decoding, so we will only
|
||||
// check that it is valid in the RLE case.
|
||||
uint32_t totalBytes;
|
||||
// The offset from the start of the file where the pixel data begins
|
||||
uint32_t offset;
|
||||
// The size of the second (info) header in bytes
|
||||
uint32_t infoBytes;
|
||||
|
||||
// Bmps embedded in Icos skip the first Bmp header
|
||||
if (!inIco) {
|
||||
// Read the first header and the size of the second header
|
||||
uint8_t hBuffer[kBmpHeaderBytesPlusFour];
|
||||
if (stream->read(hBuffer, kBmpHeaderBytesPlusFour) !=
|
||||
kBmpHeaderBytesPlusFour) {
|
||||
SkCodecPrintf("Error: unable to read first bitmap header.\n");
|
||||
return kIncompleteInput;
|
||||
}
|
||||
|
||||
totalBytes = get_int(hBuffer, 2);
|
||||
offset = get_int(hBuffer, 10);
|
||||
if (offset < kBmpHeaderBytes + kBmpOS2V1Bytes) {
|
||||
SkCodecPrintf("Error: invalid starting location for pixel data\n");
|
||||
return kInvalidInput;
|
||||
}
|
||||
|
||||
// The size of the second (info) header in bytes
|
||||
// The size is the first field of the second header, so we have already
|
||||
// read the first four infoBytes.
|
||||
infoBytes = get_int(hBuffer, 14);
|
||||
if (infoBytes < kBmpOS2V1Bytes) {
|
||||
SkCodecPrintf("Error: invalid second header size.\n");
|
||||
return kInvalidInput;
|
||||
}
|
||||
} else {
|
||||
// This value is only used by RLE compression. Bmp in Ico files do not
|
||||
// use RLE. If the compression field is incorrectly signaled as RLE,
|
||||
// we will catch this and signal an error below.
|
||||
totalBytes = 0;
|
||||
|
||||
// Bmps in Ico cannot specify an offset. We will always assume that
|
||||
// pixel data begins immediately after the color table. This value
|
||||
// will be corrected below.
|
||||
offset = 0;
|
||||
|
||||
// Read the size of the second header
|
||||
uint8_t hBuffer[4];
|
||||
if (stream->read(hBuffer, 4) != 4) {
|
||||
SkCodecPrintf("Error: unable to read size of second bitmap header.\n");
|
||||
return kIncompleteInput;
|
||||
}
|
||||
infoBytes = get_int(hBuffer, 0);
|
||||
if (infoBytes < kBmpOS2V1Bytes) {
|
||||
SkCodecPrintf("Error: invalid second header size.\n");
|
||||
return kInvalidInput;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine image information depending on second header format
|
||||
const BmpHeaderType headerType = get_header_type(infoBytes);
|
||||
if (kUnknown_BmpHeaderType == headerType) {
|
||||
return kInvalidInput;
|
||||
}
|
||||
|
||||
// We already read the first four bytes of the info header to get the size
|
||||
const uint32_t infoBytesRemaining = infoBytes - 4;
|
||||
|
||||
// Read the second header
|
||||
std::unique_ptr<uint8_t[]> iBuffer(new uint8_t[infoBytesRemaining]);
|
||||
if (stream->read(iBuffer.get(), infoBytesRemaining) != infoBytesRemaining) {
|
||||
SkCodecPrintf("Error: unable to read second bitmap header.\n");
|
||||
return kIncompleteInput;
|
||||
}
|
||||
|
||||
// The number of bits used per pixel in the pixel data
|
||||
uint16_t bitsPerPixel;
|
||||
|
||||
// The compression method for the pixel data
|
||||
uint32_t compression = kNone_BmpCompressionMethod;
|
||||
|
||||
// Number of colors in the color table, defaults to 0 or max (see below)
|
||||
uint32_t numColors = 0;
|
||||
|
||||
// Bytes per color in the color table, early versions use 3, most use 4
|
||||
uint32_t bytesPerColor;
|
||||
|
||||
// The image width and height
|
||||
int width, height;
|
||||
|
||||
switch (headerType) {
|
||||
case kInfoV1_BmpHeaderType:
|
||||
case kInfoV2_BmpHeaderType:
|
||||
case kInfoV3_BmpHeaderType:
|
||||
case kInfoV4_BmpHeaderType:
|
||||
case kInfoV5_BmpHeaderType:
|
||||
case kOS2VX_BmpHeaderType:
|
||||
// We check the size of the header before entering the if statement.
|
||||
// We should not reach this point unless the size is large enough for
|
||||
// these required fields.
|
||||
SkASSERT(infoBytesRemaining >= 12);
|
||||
width = get_int(iBuffer.get(), 0);
|
||||
height = get_int(iBuffer.get(), 4);
|
||||
bitsPerPixel = get_short(iBuffer.get(), 10);
|
||||
|
||||
// Some versions do not have these fields, so we check before
|
||||
// overwriting the default value.
|
||||
if (infoBytesRemaining >= 16) {
|
||||
compression = get_int(iBuffer.get(), 12);
|
||||
if (infoBytesRemaining >= 32) {
|
||||
numColors = get_int(iBuffer.get(), 28);
|
||||
}
|
||||
}
|
||||
|
||||
// All of the headers that reach this point, store color table entries
|
||||
// using 4 bytes per pixel.
|
||||
bytesPerColor = 4;
|
||||
break;
|
||||
case kOS2V1_BmpHeaderType:
|
||||
// The OS2V1 is treated separately because it has a unique format
|
||||
width = (int) get_short(iBuffer.get(), 0);
|
||||
height = (int) get_short(iBuffer.get(), 2);
|
||||
bitsPerPixel = get_short(iBuffer.get(), 6);
|
||||
bytesPerColor = 3;
|
||||
break;
|
||||
case kUnknown_BmpHeaderType:
|
||||
// We'll exit above in this case.
|
||||
SkASSERT(false);
|
||||
return kInvalidInput;
|
||||
}
|
||||
|
||||
// Check for valid dimensions from header
|
||||
SkCodec::SkScanlineOrder rowOrder = SkCodec::kBottomUp_SkScanlineOrder;
|
||||
if (height < 0) {
|
||||
// We can't negate INT32_MIN.
|
||||
if (height == INT32_MIN) {
|
||||
return kInvalidInput;
|
||||
}
|
||||
|
||||
height = -height;
|
||||
rowOrder = SkCodec::kTopDown_SkScanlineOrder;
|
||||
}
|
||||
// The height field for bmp in ico is double the actual height because they
|
||||
// contain an XOR mask followed by an AND mask
|
||||
if (inIco) {
|
||||
height /= 2;
|
||||
}
|
||||
|
||||
// Arbitrary maximum. Matches Chromium.
|
||||
constexpr int kMaxDim = 1 << 16;
|
||||
if (width <= 0 || height <= 0 || width >= kMaxDim || height >= kMaxDim) {
|
||||
SkCodecPrintf("Error: invalid bitmap dimensions.\n");
|
||||
return kInvalidInput;
|
||||
}
|
||||
|
||||
// Create mask struct
|
||||
SkMasks::InputMasks inputMasks;
|
||||
memset(&inputMasks, 0, sizeof(SkMasks::InputMasks));
|
||||
|
||||
// Determine the input compression format and set bit masks if necessary
|
||||
uint32_t maskBytes = 0;
|
||||
BmpInputFormat inputFormat = kUnknown_BmpInputFormat;
|
||||
switch (compression) {
|
||||
case kNone_BmpCompressionMethod:
|
||||
inputFormat = kStandard_BmpInputFormat;
|
||||
|
||||
// In addition to more standard pixel compression formats, bmp supports
|
||||
// the use of bit masks to determine pixel components. The standard
|
||||
// format for representing 16-bit colors is 555 (XRRRRRGGGGGBBBBB),
|
||||
// which does not map well to any Skia color formats. For this reason,
|
||||
// we will always enable mask mode with 16 bits per pixel.
|
||||
if (16 == bitsPerPixel) {
|
||||
inputMasks.red = 0x7C00;
|
||||
inputMasks.green = 0x03E0;
|
||||
inputMasks.blue = 0x001F;
|
||||
inputFormat = kBitMask_BmpInputFormat;
|
||||
}
|
||||
break;
|
||||
case k8BitRLE_BmpCompressionMethod:
|
||||
if (bitsPerPixel != 8) {
|
||||
SkCodecPrintf("Warning: correcting invalid bitmap format.\n");
|
||||
bitsPerPixel = 8;
|
||||
}
|
||||
inputFormat = kRLE_BmpInputFormat;
|
||||
break;
|
||||
case k4BitRLE_BmpCompressionMethod:
|
||||
if (bitsPerPixel != 4) {
|
||||
SkCodecPrintf("Warning: correcting invalid bitmap format.\n");
|
||||
bitsPerPixel = 4;
|
||||
}
|
||||
inputFormat = kRLE_BmpInputFormat;
|
||||
break;
|
||||
case kAlphaBitMasks_BmpCompressionMethod:
|
||||
case kBitMasks_BmpCompressionMethod:
|
||||
// Load the masks
|
||||
inputFormat = kBitMask_BmpInputFormat;
|
||||
switch (headerType) {
|
||||
case kInfoV1_BmpHeaderType: {
|
||||
// The V1 header stores the bit masks after the header
|
||||
uint8_t buffer[kBmpMaskBytes];
|
||||
if (stream->read(buffer, kBmpMaskBytes) != kBmpMaskBytes) {
|
||||
SkCodecPrintf("Error: unable to read bit inputMasks.\n");
|
||||
return kIncompleteInput;
|
||||
}
|
||||
maskBytes = kBmpMaskBytes;
|
||||
inputMasks.red = get_int(buffer, 0);
|
||||
inputMasks.green = get_int(buffer, 4);
|
||||
inputMasks.blue = get_int(buffer, 8);
|
||||
break;
|
||||
}
|
||||
case kInfoV2_BmpHeaderType:
|
||||
case kInfoV3_BmpHeaderType:
|
||||
case kInfoV4_BmpHeaderType:
|
||||
case kInfoV5_BmpHeaderType:
|
||||
// Header types are matched based on size. If the header
|
||||
// is V2+, we are guaranteed to be able to read at least
|
||||
// this size.
|
||||
SkASSERT(infoBytesRemaining >= 48);
|
||||
inputMasks.red = get_int(iBuffer.get(), 36);
|
||||
inputMasks.green = get_int(iBuffer.get(), 40);
|
||||
inputMasks.blue = get_int(iBuffer.get(), 44);
|
||||
|
||||
if (kInfoV2_BmpHeaderType == headerType ||
|
||||
(kInfoV3_BmpHeaderType == headerType && !inIco)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// V3+ bmp files introduce an alpha mask and allow the creator of the image
|
||||
// to use the alpha channels. However, many of these images leave the
|
||||
// alpha channel blank and expect to be rendered as opaque. This is the
|
||||
// case for almost all V3 images, so we ignore the alpha mask. For V4+
|
||||
// images in kMask mode, we will use the alpha mask. Additionally, V3
|
||||
// bmp-in-ico expect us to use the alpha mask.
|
||||
//
|
||||
// skbug.com/4116: We should perhaps also apply the alpha mask in kStandard
|
||||
// mode. We just haven't seen any images that expect this
|
||||
// behavior.
|
||||
//
|
||||
// Header types are matched based on size. If the header is
|
||||
// V3+, we are guaranteed to be able to read at least this size.
|
||||
SkASSERT(infoBytesRemaining >= 52);
|
||||
inputMasks.alpha = get_int(iBuffer.get(), 48);
|
||||
break;
|
||||
case kOS2VX_BmpHeaderType:
|
||||
// TODO: Decide if we intend to support this.
|
||||
// It is unsupported in the previous version and
|
||||
// in chromium. I have not come across a test case
|
||||
// that uses this format.
|
||||
SkCodecPrintf("Error: huffman format unsupported.\n");
|
||||
return kUnimplemented;
|
||||
default:
|
||||
SkCodecPrintf("Error: invalid bmp bit masks header.\n");
|
||||
return kInvalidInput;
|
||||
}
|
||||
break;
|
||||
case kJpeg_BmpCompressionMethod:
|
||||
if (24 == bitsPerPixel) {
|
||||
inputFormat = kRLE_BmpInputFormat;
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
case kPng_BmpCompressionMethod:
|
||||
// TODO: Decide if we intend to support this.
|
||||
// It is unsupported in the previous version and
|
||||
// in chromium. I think it is used mostly for printers.
|
||||
SkCodecPrintf("Error: compression format not supported.\n");
|
||||
return kUnimplemented;
|
||||
case kCMYK_BmpCompressionMethod:
|
||||
case kCMYK8BitRLE_BmpCompressionMethod:
|
||||
case kCMYK4BitRLE_BmpCompressionMethod:
|
||||
// TODO: Same as above.
|
||||
SkCodecPrintf("Error: CMYK not supported for bitmap decoding.\n");
|
||||
return kUnimplemented;
|
||||
default:
|
||||
SkCodecPrintf("Error: invalid format for bitmap decoding.\n");
|
||||
return kInvalidInput;
|
||||
}
|
||||
iBuffer.reset();
|
||||
|
||||
// Calculate the number of bytes read so far
|
||||
const uint32_t bytesRead = kBmpHeaderBytes + infoBytes + maskBytes;
|
||||
if (!inIco && offset < bytesRead) {
|
||||
// TODO (msarett): Do we really want to fail if the offset in the header is invalid?
|
||||
// Seems like we can just assume that the offset is zero and try to decode?
|
||||
// Maybe we don't want to try to decode corrupt images?
|
||||
SkCodecPrintf("Error: pixel data offset less than header size.\n");
|
||||
return kInvalidInput;
|
||||
}
|
||||
|
||||
|
||||
|
||||
switch (inputFormat) {
|
||||
case kStandard_BmpInputFormat: {
|
||||
// BMPs are generally opaque, however BMPs-in-ICOs may contain
|
||||
// a transparency mask after the image. Therefore, we mark the
|
||||
// alpha as kBinary if the BMP is contained in an ICO.
|
||||
// We use |isOpaque| to indicate if the BMP itself is opaque.
|
||||
SkEncodedInfo::Alpha alpha = inIco ? SkEncodedInfo::kBinary_Alpha :
|
||||
SkEncodedInfo::kOpaque_Alpha;
|
||||
bool isOpaque = true;
|
||||
|
||||
SkEncodedInfo::Color color;
|
||||
uint8_t bitsPerComponent;
|
||||
switch (bitsPerPixel) {
|
||||
// Palette formats
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
case 8:
|
||||
// In the case of ICO, kBGRA is actually the closest match,
|
||||
// since we will need to apply a transparency mask.
|
||||
if (inIco) {
|
||||
color = SkEncodedInfo::kBGRA_Color;
|
||||
bitsPerComponent = 8;
|
||||
} else {
|
||||
color = SkEncodedInfo::kPalette_Color;
|
||||
bitsPerComponent = (uint8_t) bitsPerPixel;
|
||||
}
|
||||
break;
|
||||
case 24:
|
||||
// In the case of ICO, kBGRA is actually the closest match,
|
||||
// since we will need to apply a transparency mask.
|
||||
color = inIco ? SkEncodedInfo::kBGRA_Color : SkEncodedInfo::kBGR_Color;
|
||||
bitsPerComponent = 8;
|
||||
break;
|
||||
case 32:
|
||||
// 32-bit BMP-in-ICOs actually use the alpha channel in place of a
|
||||
// transparency mask.
|
||||
if (inIco) {
|
||||
isOpaque = false;
|
||||
alpha = SkEncodedInfo::kUnpremul_Alpha;
|
||||
color = SkEncodedInfo::kBGRA_Color;
|
||||
} else {
|
||||
color = SkEncodedInfo::kBGRX_Color;
|
||||
}
|
||||
bitsPerComponent = 8;
|
||||
break;
|
||||
default:
|
||||
SkCodecPrintf("Error: invalid input value for bits per pixel.\n");
|
||||
return kInvalidInput;
|
||||
}
|
||||
|
||||
if (codecOut) {
|
||||
// We require streams to have a memory base for Bmp-in-Ico decodes.
|
||||
SkASSERT(!inIco || nullptr != stream->getMemoryBase());
|
||||
|
||||
// Set the image info and create a codec.
|
||||
auto info = SkEncodedInfo::Make(width, height, color, alpha, bitsPerComponent);
|
||||
*codecOut = std::make_unique<SkBmpStandardCodec>(std::move(info),
|
||||
std::unique_ptr<SkStream>(stream),
|
||||
bitsPerPixel, numColors, bytesPerColor,
|
||||
offset - bytesRead, rowOrder, isOpaque,
|
||||
inIco);
|
||||
return static_cast<SkBmpStandardCodec*>(codecOut->get())->didCreateSrcBuffer()
|
||||
? kSuccess : kInvalidInput;
|
||||
}
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
case kBitMask_BmpInputFormat: {
|
||||
// Bmp-in-Ico must be standard mode
|
||||
if (inIco) {
|
||||
SkCodecPrintf("Error: Icos may not use bit mask format.\n");
|
||||
return kInvalidInput;
|
||||
}
|
||||
|
||||
switch (bitsPerPixel) {
|
||||
case 16:
|
||||
case 24:
|
||||
case 32:
|
||||
break;
|
||||
default:
|
||||
SkCodecPrintf("Error: invalid input value for bits per pixel.\n");
|
||||
return kInvalidInput;
|
||||
}
|
||||
|
||||
// Skip to the start of the pixel array.
|
||||
// We can do this here because there is no color table to read
|
||||
// in bit mask mode.
|
||||
if (stream->skip(offset - bytesRead) != offset - bytesRead) {
|
||||
SkCodecPrintf("Error: unable to skip to image data.\n");
|
||||
return kIncompleteInput;
|
||||
}
|
||||
|
||||
if (codecOut) {
|
||||
// Check that input bit masks are valid and create the masks object
|
||||
SkASSERT(bitsPerPixel % 8 == 0);
|
||||
std::unique_ptr<SkMasks> masks(SkMasks::CreateMasks(inputMasks, bitsPerPixel/8));
|
||||
if (nullptr == masks) {
|
||||
SkCodecPrintf("Error: invalid input masks.\n");
|
||||
return kInvalidInput;
|
||||
}
|
||||
|
||||
// Masked bmps are not a great fit for SkEncodedInfo, since they have
|
||||
// arbitrary component orderings and bits per component. Here we choose
|
||||
// somewhat reasonable values - it's ok that we don't match exactly
|
||||
// because SkBmpMaskCodec has its own mask swizzler anyway.
|
||||
SkEncodedInfo::Color color;
|
||||
SkEncodedInfo::Alpha alpha;
|
||||
if (masks->getAlphaMask()) {
|
||||
color = SkEncodedInfo::kBGRA_Color;
|
||||
alpha = SkEncodedInfo::kUnpremul_Alpha;
|
||||
} else {
|
||||
color = SkEncodedInfo::kBGR_Color;
|
||||
alpha = SkEncodedInfo::kOpaque_Alpha;
|
||||
}
|
||||
auto info = SkEncodedInfo::Make(width, height, color, alpha, 8);
|
||||
*codecOut = std::make_unique<SkBmpMaskCodec>(std::move(info),
|
||||
std::unique_ptr<SkStream>(stream), bitsPerPixel,
|
||||
masks.release(), rowOrder);
|
||||
return static_cast<SkBmpMaskCodec*>(codecOut->get())->didCreateSrcBuffer()
|
||||
? kSuccess : kInvalidInput;
|
||||
}
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
case kRLE_BmpInputFormat: {
|
||||
// We should not reach this point without a valid value of bitsPerPixel.
|
||||
SkASSERT(4 == bitsPerPixel || 8 == bitsPerPixel || 24 == bitsPerPixel);
|
||||
|
||||
// Check for a valid number of total bytes when in RLE mode
|
||||
if (totalBytes <= offset) {
|
||||
SkCodecPrintf("Error: RLE requires valid input size.\n");
|
||||
return kInvalidInput;
|
||||
}
|
||||
|
||||
// Bmp-in-Ico must be standard mode
|
||||
// When inIco is true, this line cannot be reached, since we
|
||||
// require that RLE Bmps have a valid number of totalBytes, and
|
||||
// Icos skip the header that contains totalBytes.
|
||||
SkASSERT(!inIco);
|
||||
|
||||
if (codecOut) {
|
||||
// RLE inputs may skip pixels, leaving them as transparent. This
|
||||
// is uncommon, but we cannot be certain that an RLE bmp will be
|
||||
// opaque or that we will be able to represent it with a palette.
|
||||
// For that reason, we always indicate that we are kBGRA.
|
||||
auto info = SkEncodedInfo::Make(width, height, SkEncodedInfo::kBGRA_Color,
|
||||
SkEncodedInfo::kBinary_Alpha, 8);
|
||||
*codecOut = std::make_unique<SkBmpRLECodec>(std::move(info),
|
||||
std::unique_ptr<SkStream>(stream), bitsPerPixel,
|
||||
numColors, bytesPerColor, offset - bytesRead,
|
||||
rowOrder);
|
||||
}
|
||||
return kSuccess;
|
||||
}
|
||||
default:
|
||||
SkASSERT(false);
|
||||
return kInvalidInput;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a bmp decoder
|
||||
* Reads enough of the stream to determine the image format
|
||||
*/
|
||||
std::unique_ptr<SkCodec> SkBmpCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
|
||||
Result* result, bool inIco) {
|
||||
SkASSERT(result);
|
||||
if (!stream) {
|
||||
*result = SkCodec::kInvalidInput;
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<SkCodec> codec;
|
||||
*result = ReadHeader(stream.get(), inIco, &codec);
|
||||
if (codec) {
|
||||
// codec has taken ownership of stream, so we do not need to delete it.
|
||||
stream.release();
|
||||
}
|
||||
return kSuccess == *result ? std::move(codec) : nullptr;
|
||||
}
|
||||
|
||||
SkBmpCodec::SkBmpCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
|
||||
uint16_t bitsPerPixel, SkCodec::SkScanlineOrder rowOrder)
|
||||
: INHERITED(std::move(info), kXformSrcColorFormat, std::move(stream))
|
||||
, fBitsPerPixel(bitsPerPixel)
|
||||
, fRowOrder(rowOrder)
|
||||
, fSrcRowBytes(SkAlign4(compute_row_bytes(this->dimensions().width(), fBitsPerPixel)))
|
||||
, fXformBuffer(nullptr)
|
||||
{}
|
||||
|
||||
bool SkBmpCodec::onRewind() {
|
||||
return SkBmpCodec::ReadHeader(this->stream(), this->inIco(), nullptr) == kSuccess;
|
||||
}
|
||||
|
||||
int32_t SkBmpCodec::getDstRow(int32_t y, int32_t height) const {
|
||||
if (SkCodec::kTopDown_SkScanlineOrder == fRowOrder) {
|
||||
return y;
|
||||
}
|
||||
SkASSERT(SkCodec::kBottomUp_SkScanlineOrder == fRowOrder);
|
||||
return height - y - 1;
|
||||
}
|
||||
|
||||
SkCodec::Result SkBmpCodec::prepareToDecode(const SkImageInfo& dstInfo,
|
||||
const SkCodec::Options& options) {
|
||||
return this->onPrepareToDecode(dstInfo, options);
|
||||
}
|
||||
|
||||
SkCodec::Result SkBmpCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
|
||||
const SkCodec::Options& options) {
|
||||
return prepareToDecode(dstInfo, options);
|
||||
}
|
||||
|
||||
int SkBmpCodec::onGetScanlines(void* dst, int count, size_t rowBytes) {
|
||||
// Create a new image info representing the portion of the image to decode
|
||||
SkImageInfo rowInfo = this->dstInfo().makeWH(this->dstInfo().width(), count);
|
||||
|
||||
// Decode the requested rows
|
||||
return this->decodeRows(rowInfo, dst, rowBytes, this->options());
|
||||
}
|
||||
|
||||
bool SkBmpCodec::skipRows(int count) {
|
||||
const size_t bytesToSkip = count * fSrcRowBytes;
|
||||
return this->stream()->skip(bytesToSkip) == bytesToSkip;
|
||||
}
|
||||
|
||||
bool SkBmpCodec::onSkipScanlines(int count) {
|
||||
return this->skipRows(count);
|
||||
}
|
||||
|
||||
namespace SkBmpDecoder {
|
||||
bool IsBmp(const void* data, size_t len) {
|
||||
return SkBmpCodec::IsBmp(data, len);
|
||||
}
|
||||
|
||||
std::unique_ptr<SkCodec> Decode(std::unique_ptr<SkStream> stream,
|
||||
SkCodec::Result* outResult,
|
||||
SkCodecs::DecodeContext) {
|
||||
SkCodec::Result resultStorage;
|
||||
if (!outResult) {
|
||||
outResult = &resultStorage;
|
||||
}
|
||||
return SkBmpCodec::MakeFromStream(std::move(stream), outResult);
|
||||
}
|
||||
|
||||
std::unique_ptr<SkCodec> Decode(sk_sp<SkData> data,
|
||||
SkCodec::Result* outResult,
|
||||
SkCodecs::DecodeContext) {
|
||||
if (!data) {
|
||||
if (outResult) {
|
||||
*outResult = SkCodec::kInvalidInput;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
return Decode(SkMemoryStream::Make(std::move(data)), outResult, nullptr);
|
||||
}
|
||||
} // namespace SkBmpDecoder
|
||||
@@ -1,157 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#ifndef SkBmpCodec_DEFINED
|
||||
#define SkBmpCodec_DEFINED
|
||||
|
||||
#include "include/codec/SkCodec.h"
|
||||
#include "include/codec/SkEncodedImageFormat.h"
|
||||
#include "include/core/SkColorType.h"
|
||||
#include "include/core/SkTypes.h"
|
||||
#include "modules/skcms/skcms.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
class SkStream;
|
||||
struct SkEncodedInfo;
|
||||
struct SkImageInfo;
|
||||
|
||||
/*
|
||||
* This class enables code sharing between its bmp codec subclasses. The
|
||||
* subclasses actually do the work.
|
||||
*/
|
||||
class SkBmpCodec : public SkCodec {
|
||||
public:
|
||||
static bool IsBmp(const void*, size_t);
|
||||
|
||||
/*
|
||||
* Assumes IsBmp was called and returned true
|
||||
* Creates a bmp decoder
|
||||
* Reads enough of the stream to determine the image format
|
||||
*/
|
||||
static std::unique_ptr<SkCodec> MakeFromStream(std::unique_ptr<SkStream>, Result*);
|
||||
|
||||
/*
|
||||
* Creates a bmp decoder for a bmp embedded in ico
|
||||
* Reads enough of the stream to determine the image format
|
||||
*/
|
||||
static std::unique_ptr<SkCodec> MakeFromIco(std::unique_ptr<SkStream>, Result*);
|
||||
|
||||
protected:
|
||||
|
||||
SkBmpCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream>,
|
||||
uint16_t bitsPerPixel, SkCodec::SkScanlineOrder rowOrder);
|
||||
|
||||
SkEncodedImageFormat onGetEncodedFormat() const override { return SkEncodedImageFormat::kBMP; }
|
||||
|
||||
/*
|
||||
* Read enough of the stream to initialize the SkBmpCodec.
|
||||
* On kSuccess, if codecOut is not nullptr, it will be set to a new SkBmpCodec.
|
||||
*/
|
||||
static Result ReadHeader(SkStream*, bool inIco, std::unique_ptr<SkCodec>* codecOut);
|
||||
|
||||
bool onRewind() override;
|
||||
|
||||
/*
|
||||
* Returns whether this BMP is part of an ICO image.
|
||||
*/
|
||||
bool inIco() const {
|
||||
return this->onInIco();
|
||||
}
|
||||
|
||||
virtual bool onInIco() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the destination row number corresponding to the encoded row number.
|
||||
* For kTopDown, we simply return y, but for kBottomUp, the rows will be
|
||||
* decoded in reverse order.
|
||||
*
|
||||
* @param y Iterates from 0 to height, indicating the current row.
|
||||
* @param height The height of the current subset of the image that we are
|
||||
* decoding. This is generally equal to the full height
|
||||
* when we want to decode the full or one when we are
|
||||
* sampling.
|
||||
*/
|
||||
int32_t getDstRow(int32_t y, int32_t height) const;
|
||||
|
||||
/*
|
||||
* Accessors used by subclasses
|
||||
*/
|
||||
uint16_t bitsPerPixel() const { return fBitsPerPixel; }
|
||||
SkScanlineOrder onGetScanlineOrder() const override { return fRowOrder; }
|
||||
size_t srcRowBytes() const { return fSrcRowBytes; }
|
||||
|
||||
/*
|
||||
* To be overriden by bmp subclasses, which provide unique implementations.
|
||||
* Performs subclass specific setup.
|
||||
*
|
||||
* @param dstInfo Contains output information. Height specifies
|
||||
* the total number of rows that will be decoded.
|
||||
* @param options Additonal options to pass to the decoder.
|
||||
*/
|
||||
virtual SkCodec::Result onPrepareToDecode(const SkImageInfo& dstInfo,
|
||||
const SkCodec::Options& options) = 0;
|
||||
SkCodec::Result prepareToDecode(const SkImageInfo& dstInfo,
|
||||
const SkCodec::Options& options);
|
||||
|
||||
uint32_t* xformBuffer() const { return fXformBuffer.get(); }
|
||||
void resetXformBuffer(int count) { fXformBuffer.reset(new uint32_t[count]); }
|
||||
|
||||
/*
|
||||
* BMPs are typically encoded as BGRA/BGR so this is a more efficient choice
|
||||
* than RGBA.
|
||||
*/
|
||||
inline static constexpr SkColorType kXformSrcColorType = kBGRA_8888_SkColorType;
|
||||
inline static constexpr auto kXformSrcColorFormat = skcms_PixelFormat_BGRA_8888;
|
||||
|
||||
private:
|
||||
|
||||
/*
|
||||
* Creates a bmp decoder
|
||||
* Reads enough of the stream to determine the image format
|
||||
*/
|
||||
static std::unique_ptr<SkCodec> MakeFromStream(std::unique_ptr<SkStream>, Result*, bool inIco);
|
||||
|
||||
/*
|
||||
* Decodes the next dstInfo.height() lines.
|
||||
*
|
||||
* onGetPixels() uses this for full image decodes.
|
||||
* SkScaledCodec::onGetPixels() uses the scanline decoder to call this with
|
||||
* dstInfo.height() = 1, in order to implement sampling.
|
||||
* A potential future use is to allow the caller to decode a subset of the
|
||||
* lines in the image.
|
||||
*
|
||||
* @param dstInfo Contains output information. Height specifies the
|
||||
* number of rows to decode at this time.
|
||||
* @param dst Memory location to store output pixels
|
||||
* @param dstRowBytes Bytes in a row of the destination
|
||||
* @return Number of rows successfully decoded
|
||||
*/
|
||||
virtual int decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes,
|
||||
const Options& opts) = 0;
|
||||
|
||||
virtual bool skipRows(int count);
|
||||
|
||||
Result onStartScanlineDecode(const SkImageInfo& dstInfo,
|
||||
const SkCodec::Options&) override;
|
||||
|
||||
int onGetScanlines(void* dst, int count, size_t rowBytes) override;
|
||||
|
||||
bool onSkipScanlines(int count) override;
|
||||
|
||||
const uint16_t fBitsPerPixel;
|
||||
const SkScanlineOrder fRowOrder;
|
||||
const size_t fSrcRowBytes;
|
||||
std::unique_ptr<uint32_t[]> fXformBuffer;
|
||||
|
||||
using INHERITED = SkCodec;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "src/codec/SkBmpMaskCodec.h"
|
||||
|
||||
#include "include/core/SkAlphaType.h"
|
||||
#include "include/core/SkImageInfo.h"
|
||||
#include "include/core/SkSize.h"
|
||||
#include "include/core/SkStream.h"
|
||||
#include "include/private/SkEncodedInfo.h"
|
||||
#include "include/private/base/SkTemplates.h"
|
||||
#include "src/codec/SkCodecPriv.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
/*
|
||||
* Creates an instance of the decoder
|
||||
*/
|
||||
SkBmpMaskCodec::SkBmpMaskCodec(SkEncodedInfo&& info,
|
||||
std::unique_ptr<SkStream> stream,
|
||||
uint16_t bitsPerPixel, SkMasks* masks,
|
||||
SkCodec::SkScanlineOrder rowOrder)
|
||||
: INHERITED(std::move(info), std::move(stream), bitsPerPixel, rowOrder)
|
||||
, fMasks(masks)
|
||||
, fMaskSwizzler(nullptr)
|
||||
{}
|
||||
|
||||
/*
|
||||
* Initiates the bitmap decode
|
||||
*/
|
||||
SkCodec::Result SkBmpMaskCodec::onGetPixels(const SkImageInfo& dstInfo,
|
||||
void* dst, size_t dstRowBytes,
|
||||
const Options& opts,
|
||||
int* rowsDecoded) {
|
||||
if (opts.fSubset) {
|
||||
// Subsets are not supported.
|
||||
return kUnimplemented;
|
||||
}
|
||||
if (dstInfo.dimensions() != this->dimensions()) {
|
||||
SkCodecPrintf("Error: scaling not supported.\n");
|
||||
return kInvalidScale;
|
||||
}
|
||||
|
||||
Result result = this->prepareToDecode(dstInfo, opts);
|
||||
if (kSuccess != result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
int rows = this->decodeRows(dstInfo, dst, dstRowBytes, opts);
|
||||
if (rows != dstInfo.height()) {
|
||||
*rowsDecoded = rows;
|
||||
return kIncompleteInput;
|
||||
}
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
SkCodec::Result SkBmpMaskCodec::onPrepareToDecode(const SkImageInfo& dstInfo,
|
||||
const SkCodec::Options& options) {
|
||||
if (this->colorXform()) {
|
||||
this->resetXformBuffer(dstInfo.width());
|
||||
}
|
||||
|
||||
SkImageInfo swizzlerInfo = dstInfo;
|
||||
if (this->colorXform()) {
|
||||
swizzlerInfo = swizzlerInfo.makeColorType(kXformSrcColorType);
|
||||
if (kPremul_SkAlphaType == dstInfo.alphaType()) {
|
||||
swizzlerInfo = swizzlerInfo.makeAlphaType(kUnpremul_SkAlphaType);
|
||||
}
|
||||
}
|
||||
|
||||
bool srcIsOpaque = this->getEncodedInfo().opaque();
|
||||
fMaskSwizzler.reset(SkMaskSwizzler::CreateMaskSwizzler(swizzlerInfo, srcIsOpaque,
|
||||
fMasks.get(), this->bitsPerPixel(), options));
|
||||
SkASSERT(fMaskSwizzler);
|
||||
|
||||
return SkCodec::kSuccess;
|
||||
}
|
||||
|
||||
/*
|
||||
* Performs the decoding
|
||||
*/
|
||||
int SkBmpMaskCodec::decodeRows(const SkImageInfo& dstInfo,
|
||||
void* dst, size_t dstRowBytes,
|
||||
const Options& opts) {
|
||||
// Iterate over rows of the image
|
||||
uint8_t* srcRow = this->srcBuffer();
|
||||
const int height = dstInfo.height();
|
||||
for (int y = 0; y < height; y++) {
|
||||
// Read a row of the input
|
||||
if (this->stream()->read(srcRow, this->srcRowBytes()) != this->srcRowBytes()) {
|
||||
SkCodecPrintf("Warning: incomplete input stream.\n");
|
||||
return y;
|
||||
}
|
||||
|
||||
// Decode the row in destination format
|
||||
uint32_t row = this->getDstRow(y, height);
|
||||
void* dstRow = SkTAddOffset<void>(dst, row * dstRowBytes);
|
||||
|
||||
if (this->colorXform()) {
|
||||
fMaskSwizzler->swizzle(this->xformBuffer(), srcRow);
|
||||
this->applyColorXform(dstRow, this->xformBuffer(), fMaskSwizzler->swizzleWidth());
|
||||
} else {
|
||||
fMaskSwizzler->swizzle(dstRow, srcRow);
|
||||
}
|
||||
}
|
||||
|
||||
// Finished decoding the entire image
|
||||
return height;
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkBmpMaskCodec_DEFINED
|
||||
#define SkBmpMaskCodec_DEFINED
|
||||
|
||||
#include "include/codec/SkCodec.h"
|
||||
#include "include/core/SkTypes.h"
|
||||
#include "src/codec/SkBmpBaseCodec.h"
|
||||
#include "src/codec/SkMaskSwizzler.h"
|
||||
#include "src/core/SkMasks.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
class SkSampler;
|
||||
class SkStream;
|
||||
struct SkEncodedInfo;
|
||||
struct SkImageInfo;
|
||||
|
||||
/*
|
||||
* This class implements the decoding for bmp images using bit masks
|
||||
*/
|
||||
class SkBmpMaskCodec : public SkBmpBaseCodec {
|
||||
public:
|
||||
|
||||
/*
|
||||
* Creates an instance of the decoder
|
||||
*
|
||||
* Called only by SkBmpCodec::MakeFromStream
|
||||
* There should be no other callers despite this being public
|
||||
*
|
||||
* @param info contains properties of the encoded data
|
||||
* @param stream the stream of encoded image data
|
||||
* @param bitsPerPixel the number of bits used to store each pixel
|
||||
* @param masks color masks for certain bmp formats
|
||||
* @param rowOrder indicates whether rows are ordered top-down or bottom-up
|
||||
*/
|
||||
SkBmpMaskCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream>,
|
||||
uint16_t bitsPerPixel, SkMasks* masks,
|
||||
SkCodec::SkScanlineOrder rowOrder);
|
||||
|
||||
protected:
|
||||
|
||||
Result onGetPixels(const SkImageInfo& dstInfo, void* dst,
|
||||
size_t dstRowBytes, const Options&,
|
||||
int*) override;
|
||||
|
||||
SkCodec::Result onPrepareToDecode(const SkImageInfo& dstInfo,
|
||||
const SkCodec::Options& options) override;
|
||||
|
||||
private:
|
||||
|
||||
SkSampler* getSampler(bool createIfNecessary) override {
|
||||
SkASSERT(fMaskSwizzler);
|
||||
return fMaskSwizzler.get();
|
||||
}
|
||||
|
||||
int decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes,
|
||||
const Options& opts) override;
|
||||
|
||||
std::unique_ptr<SkMasks> fMasks;
|
||||
std::unique_ptr<SkMaskSwizzler> fMaskSwizzler;
|
||||
|
||||
using INHERITED = SkBmpBaseCodec;
|
||||
};
|
||||
#endif // SkBmpMaskCodec_DEFINED
|
||||
@@ -1,581 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "src/codec/SkBmpRLECodec.h"
|
||||
|
||||
#include "include/core/SkAlphaType.h"
|
||||
#include "include/core/SkColor.h"
|
||||
#include "include/core/SkColorPriv.h"
|
||||
#include "include/core/SkColorType.h"
|
||||
#include "include/core/SkImageInfo.h"
|
||||
#include "include/core/SkSize.h"
|
||||
#include "include/core/SkStream.h"
|
||||
#include "include/private/SkColorData.h"
|
||||
#include "include/private/SkEncodedInfo.h"
|
||||
#include "include/private/base/SkAlign.h"
|
||||
#include "include/private/base/SkMalloc.h"
|
||||
#include "include/private/base/SkTemplates.h"
|
||||
#include "src/codec/SkCodecPriv.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
/*
|
||||
* Creates an instance of the decoder
|
||||
* Called only by NewFromStream
|
||||
*/
|
||||
SkBmpRLECodec::SkBmpRLECodec(SkEncodedInfo&& info,
|
||||
std::unique_ptr<SkStream> stream,
|
||||
uint16_t bitsPerPixel, uint32_t numColors,
|
||||
uint32_t bytesPerColor, uint32_t offset,
|
||||
SkCodec::SkScanlineOrder rowOrder)
|
||||
: INHERITED(std::move(info), std::move(stream), bitsPerPixel, rowOrder)
|
||||
, fColorTable(nullptr)
|
||||
, fNumColors(numColors)
|
||||
, fBytesPerColor(bytesPerColor)
|
||||
, fOffset(offset)
|
||||
, fBytesBuffered(0)
|
||||
, fCurrRLEByte(0)
|
||||
, fSampleX(1)
|
||||
{}
|
||||
|
||||
/*
|
||||
* Initiates the bitmap decode
|
||||
*/
|
||||
SkCodec::Result SkBmpRLECodec::onGetPixels(const SkImageInfo& dstInfo,
|
||||
void* dst, size_t dstRowBytes,
|
||||
const Options& opts,
|
||||
int* rowsDecoded) {
|
||||
if (opts.fSubset) {
|
||||
// Subsets are not supported.
|
||||
return kUnimplemented;
|
||||
}
|
||||
|
||||
Result result = this->prepareToDecode(dstInfo, opts);
|
||||
if (kSuccess != result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Perform the decode
|
||||
int rows = this->decodeRows(dstInfo, dst, dstRowBytes, opts);
|
||||
if (rows != dstInfo.height()) {
|
||||
// We set rowsDecoded equal to the height because the background has already
|
||||
// been filled. RLE encodings sometimes skip pixels, so we always start by
|
||||
// filling the background.
|
||||
*rowsDecoded = dstInfo.height();
|
||||
return kIncompleteInput;
|
||||
}
|
||||
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process the color table for the bmp input
|
||||
*/
|
||||
bool SkBmpRLECodec::createColorTable(SkColorType dstColorType) {
|
||||
// Allocate memory for color table
|
||||
uint32_t colorBytes = 0;
|
||||
SkPMColor colorTable[256];
|
||||
if (this->bitsPerPixel() <= 8) {
|
||||
// Inform the caller of the number of colors
|
||||
uint32_t maxColors = 1 << this->bitsPerPixel();
|
||||
// Don't bother reading more than maxColors.
|
||||
const uint32_t numColorsToRead =
|
||||
fNumColors == 0 ? maxColors : std::min(fNumColors, maxColors);
|
||||
|
||||
// Read the color table from the stream
|
||||
colorBytes = numColorsToRead * fBytesPerColor;
|
||||
std::unique_ptr<uint8_t[]> cBuffer(new uint8_t[colorBytes]);
|
||||
if (stream()->read(cBuffer.get(), colorBytes) != colorBytes) {
|
||||
SkCodecPrintf("Error: unable to read color table.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fill in the color table
|
||||
PackColorProc packARGB = choose_pack_color_proc(false, dstColorType);
|
||||
uint32_t i = 0;
|
||||
for (; i < numColorsToRead; i++) {
|
||||
uint8_t blue = get_byte(cBuffer.get(), i*fBytesPerColor);
|
||||
uint8_t green = get_byte(cBuffer.get(), i*fBytesPerColor + 1);
|
||||
uint8_t red = get_byte(cBuffer.get(), i*fBytesPerColor + 2);
|
||||
colorTable[i] = packARGB(0xFF, red, green, blue);
|
||||
}
|
||||
|
||||
// To avoid segmentation faults on bad pixel data, fill the end of the
|
||||
// color table with black. This is the same the behavior as the
|
||||
// chromium decoder.
|
||||
for (; i < maxColors; i++) {
|
||||
colorTable[i] = SkPackARGB32(0xFF, 0, 0, 0);
|
||||
}
|
||||
|
||||
// Set the color table
|
||||
fColorTable.reset(new SkColorPalette(colorTable, maxColors));
|
||||
}
|
||||
|
||||
// Check that we have not read past the pixel array offset
|
||||
if(fOffset < colorBytes) {
|
||||
// This may occur on OS 2.1 and other old versions where the color
|
||||
// table defaults to max size, and the bmp tries to use a smaller
|
||||
// color table. This is invalid, and our decision is to indicate
|
||||
// an error, rather than try to guess the intended size of the
|
||||
// color table.
|
||||
SkCodecPrintf("Error: pixel data offset less than color table size.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// After reading the color table, skip to the start of the pixel array
|
||||
if (stream()->skip(fOffset - colorBytes) != fOffset - colorBytes) {
|
||||
SkCodecPrintf("Error: unable to skip to image data.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return true on success
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkBmpRLECodec::initializeStreamBuffer() {
|
||||
fBytesBuffered = this->stream()->read(fStreamBuffer, kBufferSize);
|
||||
if (fBytesBuffered == 0) {
|
||||
SkCodecPrintf("Error: could not read RLE image data.\n");
|
||||
return false;
|
||||
}
|
||||
fCurrRLEByte = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* @return the number of bytes remaining in the stream buffer after
|
||||
* attempting to read more bytes from the stream
|
||||
*/
|
||||
size_t SkBmpRLECodec::checkForMoreData() {
|
||||
const size_t remainingBytes = fBytesBuffered - fCurrRLEByte;
|
||||
uint8_t* buffer = fStreamBuffer;
|
||||
|
||||
// We will be reusing the same buffer, starting over from the beginning.
|
||||
// Move any remaining bytes to the start of the buffer.
|
||||
// We use memmove() instead of memcpy() because there is risk that the dst
|
||||
// and src memory will overlap in corrupt images.
|
||||
memmove(buffer, SkTAddOffset<uint8_t>(buffer, fCurrRLEByte), remainingBytes);
|
||||
|
||||
// Adjust the buffer ptr to the start of the unfilled data.
|
||||
buffer += remainingBytes;
|
||||
|
||||
// Try to read additional bytes from the stream. There are fCurrRLEByte
|
||||
// bytes of additional space remaining in the buffer, assuming that we
|
||||
// have already copied remainingBytes to the start of the buffer.
|
||||
size_t additionalBytes = this->stream()->read(buffer, fCurrRLEByte);
|
||||
|
||||
// Update counters and return the number of bytes we currently have
|
||||
// available. We are at the start of the buffer again.
|
||||
fCurrRLEByte = 0;
|
||||
fBytesBuffered = remainingBytes + additionalBytes;
|
||||
return fBytesBuffered;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set an RLE pixel using the color table
|
||||
*/
|
||||
void SkBmpRLECodec::setPixel(void* dst, size_t dstRowBytes,
|
||||
const SkImageInfo& dstInfo, uint32_t x, uint32_t y,
|
||||
uint8_t index) {
|
||||
if (dst && is_coord_necessary(x, fSampleX, dstInfo.width())) {
|
||||
// Set the row
|
||||
uint32_t row = this->getDstRow(y, dstInfo.height());
|
||||
|
||||
// Set the pixel based on destination color type
|
||||
const int dstX = get_dst_coord(x, fSampleX);
|
||||
switch (dstInfo.colorType()) {
|
||||
case kRGBA_8888_SkColorType:
|
||||
case kBGRA_8888_SkColorType: {
|
||||
SkPMColor* dstRow = SkTAddOffset<SkPMColor>(dst, row * (int) dstRowBytes);
|
||||
dstRow[dstX] = fColorTable->operator[](index);
|
||||
break;
|
||||
}
|
||||
case kRGB_565_SkColorType: {
|
||||
uint16_t* dstRow = SkTAddOffset<uint16_t>(dst, row * (int) dstRowBytes);
|
||||
dstRow[dstX] = SkPixel32ToPixel16(fColorTable->operator[](index));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// This case should not be reached. We should catch an invalid
|
||||
// color type when we check that the conversion is possible.
|
||||
SkASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set an RLE pixel from R, G, B values
|
||||
*/
|
||||
void SkBmpRLECodec::setRGBPixel(void* dst, size_t dstRowBytes,
|
||||
const SkImageInfo& dstInfo, uint32_t x,
|
||||
uint32_t y, uint8_t red, uint8_t green,
|
||||
uint8_t blue) {
|
||||
if (dst && is_coord_necessary(x, fSampleX, dstInfo.width())) {
|
||||
// Set the row
|
||||
uint32_t row = this->getDstRow(y, dstInfo.height());
|
||||
|
||||
// Set the pixel based on destination color type
|
||||
const int dstX = get_dst_coord(x, fSampleX);
|
||||
switch (dstInfo.colorType()) {
|
||||
case kRGBA_8888_SkColorType: {
|
||||
SkPMColor* dstRow = SkTAddOffset<SkPMColor>(dst, row * (int) dstRowBytes);
|
||||
dstRow[dstX] = SkPackARGB_as_RGBA(0xFF, red, green, blue);
|
||||
break;
|
||||
}
|
||||
case kBGRA_8888_SkColorType: {
|
||||
SkPMColor* dstRow = SkTAddOffset<SkPMColor>(dst, row * (int) dstRowBytes);
|
||||
dstRow[dstX] = SkPackARGB_as_BGRA(0xFF, red, green, blue);
|
||||
break;
|
||||
}
|
||||
case kRGB_565_SkColorType: {
|
||||
uint16_t* dstRow = SkTAddOffset<uint16_t>(dst, row * (int) dstRowBytes);
|
||||
dstRow[dstX] = SkPack888ToRGB16(red, green, blue);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// This case should not be reached. We should catch an invalid
|
||||
// color type when we check that the conversion is possible.
|
||||
SkASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SkCodec::Result SkBmpRLECodec::onPrepareToDecode(const SkImageInfo& dstInfo,
|
||||
const SkCodec::Options& options) {
|
||||
// FIXME: Support subsets for scanline decodes.
|
||||
if (options.fSubset) {
|
||||
// Subsets are not supported.
|
||||
return kUnimplemented;
|
||||
}
|
||||
|
||||
// Reset fSampleX. If it needs to be a value other than 1, it will get modified by
|
||||
// the sampler.
|
||||
fSampleX = 1;
|
||||
fLinesToSkip = 0;
|
||||
|
||||
SkColorType colorTableColorType = dstInfo.colorType();
|
||||
if (this->colorXform()) {
|
||||
// Just set a known colorType for the colorTable. No need to actually transform
|
||||
// the colors in the colorTable.
|
||||
colorTableColorType = kBGRA_8888_SkColorType;
|
||||
}
|
||||
|
||||
// Create the color table if necessary and prepare the stream for decode
|
||||
// Note that if it is non-NULL, inputColorCount will be modified
|
||||
if (!this->createColorTable(colorTableColorType)) {
|
||||
SkCodecPrintf("Error: could not create color table.\n");
|
||||
return SkCodec::kInvalidInput;
|
||||
}
|
||||
|
||||
// Initialize a buffer for encoded RLE data
|
||||
if (!this->initializeStreamBuffer()) {
|
||||
SkCodecPrintf("Error: cannot initialize stream buffer.\n");
|
||||
return SkCodec::kInvalidInput;
|
||||
}
|
||||
|
||||
return SkCodec::kSuccess;
|
||||
}
|
||||
|
||||
/*
|
||||
* Performs the bitmap decoding for RLE input format
|
||||
* RLE decoding is performed all at once, rather than a one row at a time
|
||||
*/
|
||||
int SkBmpRLECodec::decodeRows(const SkImageInfo& info, void* dst, size_t dstRowBytes,
|
||||
const Options& opts) {
|
||||
int height = info.height();
|
||||
|
||||
// Account for sampling.
|
||||
SkImageInfo dstInfo = info.makeWH(this->fillWidth(), height);
|
||||
|
||||
// Set the background as transparent. Then, if the RLE code skips pixels,
|
||||
// the skipped pixels will be transparent.
|
||||
if (dst) {
|
||||
SkSampler::Fill(dstInfo, dst, dstRowBytes, opts.fZeroInitialized);
|
||||
}
|
||||
|
||||
// Adjust the height and the dst if the previous call to decodeRows() left us
|
||||
// with lines that need to be skipped.
|
||||
if (height > fLinesToSkip) {
|
||||
height -= fLinesToSkip;
|
||||
if (dst) {
|
||||
dst = SkTAddOffset<void>(dst, fLinesToSkip * dstRowBytes);
|
||||
}
|
||||
fLinesToSkip = 0;
|
||||
|
||||
dstInfo = dstInfo.makeWH(dstInfo.width(), height);
|
||||
} else {
|
||||
fLinesToSkip -= height;
|
||||
return height;
|
||||
}
|
||||
|
||||
void* decodeDst = dst;
|
||||
size_t decodeRowBytes = dstRowBytes;
|
||||
SkImageInfo decodeInfo = dstInfo;
|
||||
if (decodeDst) {
|
||||
if (this->colorXform()) {
|
||||
decodeInfo = decodeInfo.makeColorType(kXformSrcColorType);
|
||||
if (kRGBA_F16_SkColorType == dstInfo.colorType()) {
|
||||
int count = height * dstInfo.width();
|
||||
this->resetXformBuffer(count);
|
||||
sk_bzero(this->xformBuffer(), count * sizeof(uint32_t));
|
||||
decodeDst = this->xformBuffer();
|
||||
decodeRowBytes = dstInfo.width() * sizeof(uint32_t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int decodedHeight = this->decodeRLE(decodeInfo, decodeDst, decodeRowBytes);
|
||||
if (this->colorXform() && decodeDst) {
|
||||
for (int y = 0; y < decodedHeight; y++) {
|
||||
this->applyColorXform(dst, decodeDst, dstInfo.width());
|
||||
decodeDst = SkTAddOffset<void>(decodeDst, decodeRowBytes);
|
||||
dst = SkTAddOffset<void>(dst, dstRowBytes);
|
||||
}
|
||||
}
|
||||
|
||||
return decodedHeight;
|
||||
}
|
||||
|
||||
int SkBmpRLECodec::decodeRLE(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes) {
|
||||
// Use the original width to count the number of pixels in each row.
|
||||
const int width = this->dimensions().width();
|
||||
|
||||
// This tells us the number of rows that we are meant to decode.
|
||||
const int height = dstInfo.height();
|
||||
|
||||
// Set RLE flags
|
||||
constexpr uint8_t RLE_ESCAPE = 0;
|
||||
constexpr uint8_t RLE_EOL = 0;
|
||||
constexpr uint8_t RLE_EOF = 1;
|
||||
constexpr uint8_t RLE_DELTA = 2;
|
||||
|
||||
// Destination parameters
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
|
||||
while (true) {
|
||||
// If we have reached a row that is beyond the requested height, we have
|
||||
// succeeded.
|
||||
if (y >= height) {
|
||||
// It would be better to check for the EOF marker before indicating
|
||||
// success, but we may be performing a scanline decode, which
|
||||
// would require us to stop before decoding the full height.
|
||||
return height;
|
||||
}
|
||||
|
||||
// Every entry takes at least two bytes
|
||||
if ((int) fBytesBuffered - fCurrRLEByte < 2) {
|
||||
if (this->checkForMoreData() < 2) {
|
||||
return y;
|
||||
}
|
||||
}
|
||||
|
||||
// Read the next two bytes. These bytes have different meanings
|
||||
// depending on their values. In the first interpretation, the first
|
||||
// byte is an escape flag and the second byte indicates what special
|
||||
// task to perform.
|
||||
const uint8_t flag = fStreamBuffer[fCurrRLEByte++];
|
||||
const uint8_t task = fStreamBuffer[fCurrRLEByte++];
|
||||
|
||||
// Perform decoding
|
||||
if (RLE_ESCAPE == flag) {
|
||||
switch (task) {
|
||||
case RLE_EOL:
|
||||
x = 0;
|
||||
y++;
|
||||
break;
|
||||
case RLE_EOF:
|
||||
return height;
|
||||
case RLE_DELTA: {
|
||||
// Two bytes are needed to specify delta
|
||||
if ((int) fBytesBuffered - fCurrRLEByte < 2) {
|
||||
if (this->checkForMoreData() < 2) {
|
||||
return y;
|
||||
}
|
||||
}
|
||||
// Modify x and y
|
||||
const uint8_t dx = fStreamBuffer[fCurrRLEByte++];
|
||||
const uint8_t dy = fStreamBuffer[fCurrRLEByte++];
|
||||
x += dx;
|
||||
y += dy;
|
||||
if (x > width) {
|
||||
SkCodecPrintf("Warning: invalid RLE input.\n");
|
||||
return y - dy;
|
||||
} else if (y > height) {
|
||||
fLinesToSkip = y - height;
|
||||
return height;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// If task does not match any of the above signals, it
|
||||
// indicates that we have a sequence of non-RLE pixels.
|
||||
// Furthermore, the value of task is equal to the number
|
||||
// of pixels to interpret.
|
||||
uint8_t numPixels = task;
|
||||
const size_t rowBytes = compute_row_bytes(numPixels,
|
||||
this->bitsPerPixel());
|
||||
if (x + numPixels > width) {
|
||||
SkCodecPrintf("Warning: invalid RLE input.\n");
|
||||
}
|
||||
|
||||
// Abort if there are not enough bytes
|
||||
// remaining in the stream to set numPixels.
|
||||
|
||||
// At most, alignedRowBytes can be 255 (max uint8_t) *
|
||||
// 3 (max bytes per pixel) + 1 (aligned) = 766. If
|
||||
// fStreamBuffer was smaller than this,
|
||||
// checkForMoreData would never succeed for some bmps.
|
||||
static_assert(255 * 3 + 1 < kBufferSize,
|
||||
"kBufferSize needs to be larger!");
|
||||
const size_t alignedRowBytes = SkAlign2(rowBytes);
|
||||
if ((int) fBytesBuffered - fCurrRLEByte < alignedRowBytes) {
|
||||
SkASSERT(alignedRowBytes < kBufferSize);
|
||||
if (this->checkForMoreData() < alignedRowBytes) {
|
||||
return y;
|
||||
}
|
||||
}
|
||||
// Set numPixels number of pixels
|
||||
while ((numPixels > 0) && (x < width)) {
|
||||
switch(this->bitsPerPixel()) {
|
||||
case 4: {
|
||||
SkASSERT(fCurrRLEByte < fBytesBuffered);
|
||||
uint8_t val = fStreamBuffer[fCurrRLEByte++];
|
||||
setPixel(dst, dstRowBytes, dstInfo, x++,
|
||||
y, val >> 4);
|
||||
numPixels--;
|
||||
if (numPixels != 0) {
|
||||
setPixel(dst, dstRowBytes, dstInfo,
|
||||
x++, y, val & 0xF);
|
||||
numPixels--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 8:
|
||||
SkASSERT(fCurrRLEByte < fBytesBuffered);
|
||||
setPixel(dst, dstRowBytes, dstInfo, x++,
|
||||
y, fStreamBuffer[fCurrRLEByte++]);
|
||||
numPixels--;
|
||||
break;
|
||||
case 24: {
|
||||
SkASSERT(fCurrRLEByte + 2 < fBytesBuffered);
|
||||
uint8_t blue = fStreamBuffer[fCurrRLEByte++];
|
||||
uint8_t green = fStreamBuffer[fCurrRLEByte++];
|
||||
uint8_t red = fStreamBuffer[fCurrRLEByte++];
|
||||
setRGBPixel(dst, dstRowBytes, dstInfo,
|
||||
x++, y, red, green, blue);
|
||||
numPixels--;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
SkASSERT(false);
|
||||
return y;
|
||||
}
|
||||
}
|
||||
// Skip a byte if necessary to maintain alignment
|
||||
if (!SkIsAlign2(rowBytes)) {
|
||||
fCurrRLEByte++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If the first byte read is not a flag, it indicates the number of
|
||||
// pixels to set in RLE mode.
|
||||
const uint8_t numPixels = flag;
|
||||
const int endX = std::min<int>(x + numPixels, width);
|
||||
|
||||
if (24 == this->bitsPerPixel()) {
|
||||
// In RLE24, the second byte read is part of the pixel color.
|
||||
// There are two more required bytes to finish encoding the
|
||||
// color.
|
||||
if ((int) fBytesBuffered - fCurrRLEByte < 2) {
|
||||
if (this->checkForMoreData() < 2) {
|
||||
return y;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill the pixels up to endX with the specified color
|
||||
uint8_t blue = task;
|
||||
uint8_t green = fStreamBuffer[fCurrRLEByte++];
|
||||
uint8_t red = fStreamBuffer[fCurrRLEByte++];
|
||||
while (x < endX) {
|
||||
setRGBPixel(dst, dstRowBytes, dstInfo, x++, y, red, green, blue);
|
||||
}
|
||||
} else {
|
||||
// In RLE8 or RLE4, the second byte read gives the index in the
|
||||
// color table to look up the pixel color.
|
||||
// RLE8 has one color index that gets repeated
|
||||
// RLE4 has two color indexes in the upper and lower 4 bits of
|
||||
// the bytes, which are alternated
|
||||
uint8_t indices[2] = { task, task };
|
||||
if (4 == this->bitsPerPixel()) {
|
||||
indices[0] >>= 4;
|
||||
indices[1] &= 0xf;
|
||||
}
|
||||
|
||||
// Set the indicated number of pixels
|
||||
for (int which = 0; x < endX; x++) {
|
||||
setPixel(dst, dstRowBytes, dstInfo, x, y, indices[which]);
|
||||
which = !which;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SkBmpRLECodec::skipRows(int count) {
|
||||
const SkImageInfo rowInfo = SkImageInfo::Make(this->dimensions().width(), count,
|
||||
kN32_SkColorType, kUnpremul_SkAlphaType);
|
||||
return count == this->decodeRows(rowInfo, nullptr, 0, this->options());
|
||||
}
|
||||
|
||||
// FIXME: Make SkBmpRLECodec have no knowledge of sampling.
|
||||
// Or it should do all sampling natively.
|
||||
// It currently is a hybrid that needs to know what SkScaledCodec is doing.
|
||||
class SkBmpRLESampler : public SkSampler {
|
||||
public:
|
||||
SkBmpRLESampler(SkBmpRLECodec* codec)
|
||||
: fCodec(codec)
|
||||
{
|
||||
SkASSERT(fCodec);
|
||||
}
|
||||
|
||||
int fillWidth() const override {
|
||||
return fCodec->fillWidth();
|
||||
}
|
||||
|
||||
private:
|
||||
int onSetSampleX(int sampleX) override {
|
||||
return fCodec->setSampleX(sampleX);
|
||||
}
|
||||
|
||||
// Unowned pointer. fCodec will delete this class in its destructor.
|
||||
SkBmpRLECodec* fCodec;
|
||||
};
|
||||
|
||||
SkSampler* SkBmpRLECodec::getSampler(bool createIfNecessary) {
|
||||
if (!fSampler && createIfNecessary) {
|
||||
fSampler = std::make_unique<SkBmpRLESampler>(this);
|
||||
}
|
||||
|
||||
return fSampler.get();
|
||||
}
|
||||
|
||||
int SkBmpRLECodec::setSampleX(int sampleX) {
|
||||
fSampleX = sampleX;
|
||||
return this->fillWidth();
|
||||
}
|
||||
|
||||
int SkBmpRLECodec::fillWidth() const {
|
||||
return get_scaled_dimension(this->dimensions().width(), fSampleX);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user