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:
Lee Salzman
2025-04-04 03:46:00 +00:00
parent a1d186a7d0
commit e1fbc38338
395 changed files with 9423 additions and 24891 deletions

View File

@@ -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',

View File

@@ -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',

View File

@@ -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 {

View File

@@ -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

View File

@@ -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>);

View File

@@ -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

View File

@@ -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.
*/

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -41,7 +41,7 @@ public:
kUltraExpanded_Width = 9,
};
enum Slant {
enum Slant : uint8_t {
kUpright_Slant,
kItalic_Slant,
kOblique_Slant,

View File

@@ -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);

View File

@@ -5,5 +5,5 @@
* found in the LICENSE file.
*/
#ifndef SK_MILESTONE
#define SK_MILESTONE 132
#define SK_MILESTONE 136
#endif

View File

@@ -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.

View File

@@ -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[],

View File

@@ -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

View File

@@ -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

View File

@@ -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[]) {

View File

@@ -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);
}

View File

@@ -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.

View File

@@ -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*);

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View 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

View File

@@ -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;

View File

@@ -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;

View File

@@ -38,6 +38,10 @@ public:
static SkMaskFilter* CreateClip(uint8_t min, uint8_t max);
SkTableMaskFilter() = delete;
private:
static void RegisterFlattenables();
friend class SkFlattenable;
};
#endif

View File

@@ -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;
};
/**

View File

@@ -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

View File

@@ -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

View File

@@ -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.
*/

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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.
*/

View File

@@ -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

View File

@@ -15,6 +15,7 @@
#include "include/gpu/GpuTypes.h"
#include <string_view>
#include <tuple>
class SkYUVAInfo;
class SkYUVAPixmaps;

View 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

View 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

View File

@@ -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.

View File

@@ -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();

View File

@@ -18,70 +18,115 @@ 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:
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();
~TextureInfo();
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
#endif //skgpu_graphite_TextureInfo_DEFINED
#endif // skgpu_graphite_TextureInfo_DEFINED

View File

@@ -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);

View File

@@ -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

View 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

View File

@@ -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"

View File

@@ -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"

View File

@@ -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

View File

@@ -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__

View File

@@ -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"

View File

@@ -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

View File

@@ -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"

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -56,20 +56,26 @@
* Used to ignore sanitizer warnings.
*/
#if !defined(SK_NO_SANITIZE)
# define SK_NO_SANITIZE(A) SK_ATTRIBUTE(no_sanitize(A))
#endif
/**
* Helper macro to define no_sanitize attributes only with clang.
*/
#if defined(__clang__) && defined(__has_attribute)
#if defined(__has_attribute)
#if __has_attribute(no_sanitize)
#define SK_CLANG_NO_SANITIZE(A) SK_NO_SANITIZE(A)
// 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
#if !defined(SK_CLANG_NO_SANITIZE)
#define SK_CLANG_NO_SANITIZE(A)
/**
* Used to ignore CFI sanitizer warnings, supported only by Clang at the moment.
*/
#if defined(__clang__)
#define SK_NO_SANITIZE_CFI SK_NO_SANITIZE("cfi")
#else
#define SK_NO_SANITIZE_CFI
#endif
/**

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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:

View File

@@ -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;

View 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

View File

@@ -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,

View File

@@ -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;
};

View File

@@ -0,0 +1,6 @@
Name: skcms
URL: https://skia.org/
Version: unknown
Security Critical: yes
Shipped: yes
License: BSD

View File

@@ -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;

View File

@@ -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];

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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.

View File

@@ -0,0 +1 @@
1e365691d01ad13edd93056c2731a5c6e0be2a15

View File

@@ -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;

View File

@@ -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();

View File

@@ -16,7 +16,6 @@
#include <cstdint>
#include <cstring>
#include <new>
#include <utility>
SkTDStorage::SkTDStorage(int sizeOfT) : fSizeOfT{sizeOfT} {}

View File

@@ -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(); }

View File

@@ -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)... };

View File

@@ -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
}
};

View File

@@ -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(&copySubset) || 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);
}

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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()))
{}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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