From 992bfbde1e4448df483eb297bfb500fb439c63ef Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Tue, 11 Jun 2024 12:29:24 +0400 Subject: [PATCH] Lottie experiments --- Tests/LottieMetalTest/BUILD | 1 + .../SoftwareLottieRenderer/BUILD | 2 + .../Sources/SkiaCanvasImpl.cpp | 316 ++ .../Sources/SkiaCanvasImpl.h | 57 + .../Sources/SoftwareLottieRenderer.mm | 82 +- .../Sources/ViewController.swift | 8 +- Tests/LottieMetalTest/skia/BUILD | 43 + .../skia/PublicHeaders/skia/include/OWNERS | 15 + .../skia/include/codec/SkAndroidCodec.h | 297 ++ .../skia/include/codec/SkAvifDecoder.h | 44 + .../skia/include/codec/SkBmpDecoder.h | 44 + .../skia/include/codec/SkCodec.h | 1085 +++++++ .../skia/include/codec/SkCodecAnimation.h | 61 + .../skia/include/codec/SkEncodedImageFormat.h | 33 + .../skia/include/codec/SkEncodedOrigin.h | 54 + .../skia/include/codec/SkGifDecoder.h | 44 + .../skia/include/codec/SkIcoDecoder.h | 44 + .../skia/include/codec/SkJpegDecoder.h | 44 + .../skia/include/codec/SkJpegxlDecoder.h | 44 + .../skia/include/codec/SkPixmapUtils.h | 31 + .../skia/include/codec/SkPngChunkReader.h | 45 + .../skia/include/codec/SkPngDecoder.h | 44 + .../skia/include/codec/SkRawDecoder.h | 50 + .../skia/include/codec/SkWbmpDecoder.h | 44 + .../skia/include/codec/SkWebpDecoder.h | 44 + .../PublicHeaders/skia/include/config/OWNERS | 2 + .../skia/include/config/SkUserConfig.h | 121 + .../skia/include/core/SkAlphaType.h | 45 + .../skia/include/core/SkAnnotation.h | 52 + .../PublicHeaders/skia/include/core/SkArc.h | 69 + .../skia/include/core/SkBBHFactory.h | 67 + .../skia/include/core/SkBitmap.h | 1275 ++++++++ .../skia/include/core/SkBlendMode.h | 112 + .../skia/include/core/SkBlender.h | 31 + .../skia/include/core/SkBlurTypes.h | 20 + .../skia/include/core/SkCanvas.h | 2686 +++++++++++++++ .../include/core/SkCanvasVirtualEnforcer.h | 61 + .../skia/include/core/SkCapabilities.h | 39 + .../skia/include/core/SkClipOp.h | 19 + .../PublicHeaders/skia/include/core/SkColor.h | 447 +++ .../skia/include/core/SkColorFilter.h | 156 + .../skia/include/core/SkColorPriv.h | 167 + .../skia/include/core/SkColorSpace.h | 242 ++ .../skia/include/core/SkColorTable.h | 62 + .../skia/include/core/SkColorType.h | 70 + .../skia/include/core/SkContourMeasure.h | 139 + .../skia/include/core/SkCoverageMode.h | 28 + .../skia/include/core/SkCubicMap.h | 47 + .../PublicHeaders/skia/include/core/SkData.h | 191 ++ .../skia/include/core/SkDataTable.h | 122 + .../skia/include/core/SkDocument.h | 93 + .../skia/include/core/SkDrawable.h | 178 + .../skia/include/core/SkExecutor.h | 41 + .../skia/include/core/SkFlattenable.h | 115 + .../PublicHeaders/skia/include/core/SkFont.h | 539 +++ .../skia/include/core/SkFontArguments.h | 94 + .../skia/include/core/SkFontMetrics.h | 139 + .../skia/include/core/SkFontMgr.h | 143 + .../skia/include/core/SkFontParameters.h | 42 + .../skia/include/core/SkFontStyle.h | 84 + .../skia/include/core/SkFontTypes.h | 25 + .../skia/include/core/SkGraphics.h | 169 + .../PublicHeaders/skia/include/core/SkImage.h | 948 ++++++ .../skia/include/core/SkImageFilter.h | 119 + .../skia/include/core/SkImageGenerator.h | 148 + .../skia/include/core/SkImageInfo.h | 628 ++++ .../PublicHeaders/skia/include/core/SkM44.h | 442 +++ .../skia/include/core/SkMallocPixelRef.h | 45 + .../skia/include/core/SkMaskFilter.h | 53 + .../skia/include/core/SkMatrix.h | 1997 ++++++++++++ .../PublicHeaders/skia/include/core/SkMesh.h | 429 +++ .../skia/include/core/SkMilestone.h | 9 + .../skia/include/core/SkOpenTypeSVGDecoder.h | 30 + .../skia/include/core/SkOverdrawCanvas.h | 94 + .../PublicHeaders/skia/include/core/SkPaint.h | 695 ++++ .../PublicHeaders/skia/include/core/SkPath.h | 1943 +++++++++++ .../skia/include/core/SkPathBuilder.h | 271 ++ .../skia/include/core/SkPathEffect.h | 113 + .../skia/include/core/SkPathMeasure.h | 95 + .../skia/include/core/SkPathTypes.h | 57 + .../skia/include/core/SkPathUtils.h | 42 + .../skia/include/core/SkPicture.h | 291 ++ .../skia/include/core/SkPictureRecorder.h | 115 + .../skia/include/core/SkPixelRef.h | 119 + .../skia/include/core/SkPixmap.h | 731 +++++ .../PublicHeaders/skia/include/core/SkPoint.h | 10 + .../skia/include/core/SkPoint3.h | 149 + .../PublicHeaders/skia/include/core/SkRRect.h | 516 +++ .../skia/include/core/SkRSXform.h | 71 + .../include/core/SkRasterHandleAllocator.h | 94 + .../PublicHeaders/skia/include/core/SkRect.h | 1374 ++++++++ .../skia/include/core/SkRefCnt.h | 389 +++ .../skia/include/core/SkRegion.h | 684 ++++ .../skia/include/core/SkSamplingOptions.h | 107 + .../skia/include/core/SkScalar.h | 161 + .../skia/include/core/SkSerialProcs.h | 117 + .../skia/include/core/SkShader.h | 112 + .../PublicHeaders/skia/include/core/SkSize.h | 93 + .../PublicHeaders/skia/include/core/SkSpan.h | 13 + .../skia/include/core/SkStream.h | 512 +++ .../skia/include/core/SkString.h | 293 ++ .../skia/include/core/SkStrokeRec.h | 159 + .../skia/include/core/SkSurface.h | 659 ++++ .../skia/include/core/SkSurfaceProps.h | 116 + .../skia/include/core/SkSwizzle.h | 21 + .../skia/include/core/SkTextBlob.h | 519 +++ .../include/core/SkTextureCompressionType.h | 30 + .../skia/include/core/SkTileMode.h | 41 + .../skia/include/core/SkTiledImageUtils.h | 125 + .../skia/include/core/SkTraceMemoryDump.h | 114 + .../skia/include/core/SkTypeface.h | 434 +++ .../PublicHeaders/skia/include/core/SkTypes.h | 199 ++ .../skia/include/core/SkUnPreMultiply.h | 55 + .../skia/include/core/SkVertices.h | 136 + .../skia/include/core/SkYUVAInfo.h | 308 ++ .../skia/include/core/SkYUVAPixmaps.h | 337 ++ .../include/docs/SkMultiPictureDocument.h | 53 + .../skia/include/docs/SkPDFDocument.h | 224 ++ .../skia/include/docs/SkXPSDocument.h | 27 + .../skia/include/effects/Sk1DPathEffect.h | 40 + .../skia/include/effects/Sk2DPathEffect.h | 33 + .../skia/include/effects/SkBlenders.h | 27 + .../skia/include/effects/SkBlurMaskFilter.h | 35 + .../skia/include/effects/SkColorMatrix.h | 57 + .../include/effects/SkColorMatrixFilter.h | 22 + .../skia/include/effects/SkCornerPathEffect.h | 32 + .../skia/include/effects/SkDashPathEffect.h | 43 + .../include/effects/SkDiscretePathEffect.h | 37 + .../skia/include/effects/SkGradientShader.h | 354 ++ .../include/effects/SkHighContrastFilter.h | 84 + .../skia/include/effects/SkImageFilters.h | 615 ++++ .../skia/include/effects/SkLumaColorFilter.h | 37 + .../include/effects/SkOverdrawColorFilter.h | 32 + .../include/effects/SkPerlinNoiseShader.h | 53 + .../skia/include/effects/SkRuntimeEffect.h | 517 +++ .../skia/include/effects/SkShaderMaskFilter.h | 28 + .../skia/include/effects/SkTableMaskFilter.h | 43 + .../skia/include/effects/SkTrimPathEffect.h | 45 + .../skia/include/encode/SkEncoder.h | 63 + .../PublicHeaders/skia/include/encode/SkICC.h | 36 + .../skia/include/encode/SkJpegEncoder.h | 128 + .../skia/include/encode/SkPngEncoder.h | 117 + .../skia/include/encode/SkWebpEncoder.h | 92 + .../skia/include/pathops/SkPathOps.h | 113 + .../skia/include/ports/SkCFObject.h | 180 + .../include/ports/SkFontConfigInterface.h | 112 + .../ports/SkFontMgr_FontConfigInterface.h | 20 + .../skia/include/ports/SkFontMgr_Fontations.h | 20 + .../skia/include/ports/SkFontMgr_android.h | 50 + .../skia/include/ports/SkFontMgr_data.h | 22 + .../skia/include/ports/SkFontMgr_directory.h | 21 + .../skia/include/ports/SkFontMgr_empty.h | 21 + .../skia/include/ports/SkFontMgr_fontconfig.h | 22 + .../skia/include/ports/SkFontMgr_fuchsia.h | 19 + .../skia/include/ports/SkFontMgr_mac_ct.h | 27 + .../skia/include/ports/SkImageGeneratorCG.h | 28 + .../skia/include/ports/SkImageGeneratorNDK.h | 40 + .../skia/include/ports/SkImageGeneratorWIC.h | 41 + .../include/ports/SkTypeface_fontations.h | 21 + .../skia/include/ports/SkTypeface_mac.h | 44 + .../skia/include/ports/SkTypeface_win.h | 63 + .../PublicHeaders/skia/include/private/OWNERS | 4 + .../skia/include/private/SkColorData.h | 385 +++ .../skia/include/private/SkEncodedInfo.h | 283 ++ .../skia/include/private/SkExif.h | 55 + .../skia/include/private/SkGainmapInfo.h | 140 + .../skia/include/private/SkGainmapShader.h | 54 + .../skia/include/private/SkIDChangeListener.h | 76 + .../include/private/SkJpegGainmapEncoder.h | 48 + .../include/private/SkJpegMetadataDecoder.h | 86 + .../skia/include/private/SkPathRef.h | 582 ++++ .../skia/include/private/SkSLSampleUsage.h | 85 + .../skia/include/private/SkWeakRefCnt.h | 173 + .../skia/include/private/SkXmp.h | 62 + .../skia/include/private/base/README.md | 4 + .../skia/include/private/base/SingleOwner.h | 75 + .../skia/include/private/base/SkAPI.h | 52 + .../skia/include/private/base/SkASAN.h | 56 + .../skia/include/private/base/SkAlign.h | 39 + .../include/private/base/SkAlignedStorage.h | 32 + .../skia/include/private/base/SkAnySubclass.h | 73 + .../skia/include/private/base/SkAssert.h | 202 ++ .../skia/include/private/base/SkAttributes.h | 90 + .../skia/include/private/base/SkCPUTypes.h | 25 + .../skia/include/private/base/SkContainers.h | 54 + .../skia/include/private/base/SkDebug.h | 27 + .../skia/include/private/base/SkDeque.h | 138 + .../skia/include/private/base/SkFeatures.h | 165 + .../skia/include/private/base/SkFixed.h | 143 + .../include/private/base/SkFloatingPoint.h | 184 ++ .../include/private/base/SkLoadUserConfig.h | 63 + .../skia/include/private/base/SkMacros.h | 94 + .../skia/include/private/base/SkMalloc.h | 152 + .../skia/include/private/base/SkMath.h | 77 + .../skia/include/private/base/SkMutex.h | 64 + .../skia/include/private/base/SkNoncopyable.h | 30 + .../skia/include/private/base/SkOnce.h | 55 + .../skia/include/private/base/SkPoint_impl.h | 560 ++++ .../skia/include/private/base/SkSafe32.h | 49 + .../skia/include/private/base/SkSemaphore.h | 84 + .../skia/include/private/base/SkSpan_impl.h | 131 + .../skia/include/private/base/SkTArray.h | 806 +++++ .../skia/include/private/base/SkTDArray.h | 235 ++ .../skia/include/private/base/SkTFitsIn.h | 105 + .../skia/include/private/base/SkTLogic.h | 56 + .../skia/include/private/base/SkTPin.h | 23 + .../skia/include/private/base/SkTemplates.h | 446 +++ .../private/base/SkThreadAnnotations.h | 104 + .../skia/include/private/base/SkThreadID.h | 23 + .../skia/include/private/base/SkTo.h | 39 + .../skia/include/private/base/SkTypeTraits.h | 33 + .../private/chromium/GrDeferredDisplayList.h | 120 + .../chromium/GrDeferredDisplayListRecorder.h | 62 + .../private/chromium/GrPromiseImageTexture.h | 43 + .../chromium/GrSurfaceCharacterization.h | 211 ++ .../chromium/GrVkSecondaryCBDrawContext.h | 131 + .../chromium/SkChromeRemoteGlyphCache.h | 150 + .../private/chromium/SkDiscardableMemory.h | 70 + .../private/chromium/SkImageChromium.h | 117 + .../skia/include/private/chromium/Slug.h | 72 + .../private/gpu/ganesh/GrContext_Base.h | 104 + .../private/gpu/ganesh/GrD3DTypesMinimal.h | 74 + .../private/gpu/ganesh/GrImageContext.h | 56 + .../private/gpu/ganesh/GrTextureGenerator.h | 66 + .../include/private/gpu/ganesh/GrTypesPriv.h | 1007 ++++++ .../private/gpu/graphite/ContextOptionsPriv.h | 70 + .../private/gpu/graphite/DawnTypesPriv.h | 61 + .../gpu/graphite/MtlGraphiteTypesPriv.h | 95 + .../gpu/graphite/VulkanGraphiteTypesPriv.h | 83 + .../skia/include/private/gpu/vk/SkiaVulkan.h | 36 + .../PublicHeaders/skia/include/sksl/OWNERS | 3 + .../skia/include/sksl/SkSLDebugTrace.h | 28 + .../skia/include/sksl/SkSLVersion.h | 27 + .../skia/include/svg/SkSVGCanvas.h | 42 + .../skia/include/utils/SkCamera.h | 109 + .../skia/include/utils/SkCanvasStateUtils.h | 81 + .../skia/include/utils/SkCustomTypeface.h | 69 + .../skia/include/utils/SkEventTracer.h | 90 + .../skia/include/utils/SkNWayCanvas.h | 123 + .../skia/include/utils/SkNoDrawCanvas.h | 78 + .../skia/include/utils/SkNullCanvas.h | 22 + .../skia/include/utils/SkOrderedFontMgr.h | 66 + .../skia/include/utils/SkPaintFilterCanvas.h | 140 + .../skia/include/utils/SkParse.h | 37 + .../skia/include/utils/SkParsePath.h | 25 + .../skia/include/utils/SkShadowUtils.h | 102 + .../skia/include/utils/SkTextUtils.h | 43 + .../skia/include/utils/SkTraceEventPhase.h | 19 + .../skia/include/utils/mac/SkCGUtils.h | 90 + .../PublicHeaders/skia/modules/skcms/BUILD.gn | 100 + .../PublicHeaders/skia/modules/skcms/OWNERS | 2 + .../skia/modules/skcms/README.chromium | 6 + .../PublicHeaders/skia/modules/skcms/skcms.cc | 2889 +++++++++++++++++ .../skia/modules/skcms/skcms.gni | 57 + .../PublicHeaders/skia/modules/skcms/skcms.h | 10 + .../skia/modules/skcms/src/Transform_inl.h | 1541 +++++++++ .../skia/modules/skcms/src/skcms_Transform.h | 162 + .../skcms/src/skcms_TransformBaseline.cc | 48 + .../modules/skcms/src/skcms_TransformHsw.cc | 61 + .../modules/skcms/src/skcms_TransformSkx.cc | 58 + .../skia/modules/skcms/src/skcms_internals.h | 138 + .../skia/modules/skcms/src/skcms_public.h | 406 +++ .../skia/modules/skcms/version.sha1 | 1 + .../skia/libskia.framework/Info.plist | Bin 0 -> 756 bytes .../skia/libskia.framework/libskia | Bin 0 -> 2961400 bytes submodules/LottieCpp/lottiecpp | 2 +- 266 files changed, 47526 insertions(+), 6 deletions(-) create mode 100644 Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SkiaCanvasImpl.cpp create mode 100644 Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SkiaCanvasImpl.h create mode 100644 Tests/LottieMetalTest/skia/BUILD create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/OWNERS create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkAndroidCodec.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkAvifDecoder.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkBmpDecoder.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkCodec.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkCodecAnimation.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkEncodedImageFormat.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkEncodedOrigin.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkGifDecoder.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkIcoDecoder.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkJpegDecoder.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkJpegxlDecoder.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkPixmapUtils.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkPngChunkReader.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkPngDecoder.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkRawDecoder.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkWbmpDecoder.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkWebpDecoder.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/config/OWNERS create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/config/SkUserConfig.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkAlphaType.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkAnnotation.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkArc.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkBBHFactory.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkBitmap.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkBlendMode.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkBlender.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkBlurTypes.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkCanvas.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkCanvasVirtualEnforcer.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkCapabilities.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkClipOp.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkColor.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkColorFilter.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkColorPriv.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkColorSpace.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkColorTable.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkColorType.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkContourMeasure.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkCoverageMode.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkCubicMap.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkData.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkDataTable.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkDocument.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkDrawable.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkExecutor.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFlattenable.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFont.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFontArguments.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFontMetrics.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFontMgr.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFontParameters.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFontStyle.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFontTypes.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkGraphics.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkImage.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkImageFilter.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkImageGenerator.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkImageInfo.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkM44.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkMallocPixelRef.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkMaskFilter.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkMatrix.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkMesh.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkMilestone.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkOpenTypeSVGDecoder.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkOverdrawCanvas.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPaint.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPath.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPathBuilder.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPathEffect.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPathMeasure.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPathTypes.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPathUtils.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPicture.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPictureRecorder.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPixelRef.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPixmap.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPoint.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPoint3.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkRRect.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkRSXform.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkRasterHandleAllocator.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkRect.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkRefCnt.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkRegion.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkSamplingOptions.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkScalar.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkSerialProcs.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkShader.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkSize.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkSpan.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkStream.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkString.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkStrokeRec.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkSurface.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkSurfaceProps.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkSwizzle.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkTextBlob.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkTextureCompressionType.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkTileMode.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkTiledImageUtils.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkTraceMemoryDump.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkTypeface.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkTypes.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkUnPreMultiply.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkVertices.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkYUVAInfo.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkYUVAPixmaps.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/docs/SkMultiPictureDocument.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/docs/SkPDFDocument.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/docs/SkXPSDocument.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/Sk1DPathEffect.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/Sk2DPathEffect.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkBlenders.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkBlurMaskFilter.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkColorMatrix.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkColorMatrixFilter.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkCornerPathEffect.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkDashPathEffect.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkDiscretePathEffect.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkGradientShader.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkHighContrastFilter.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkImageFilters.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkLumaColorFilter.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkOverdrawColorFilter.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkPerlinNoiseShader.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkRuntimeEffect.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkShaderMaskFilter.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkTableMaskFilter.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkTrimPathEffect.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/encode/SkEncoder.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/encode/SkICC.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/encode/SkJpegEncoder.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/encode/SkPngEncoder.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/encode/SkWebpEncoder.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/pathops/SkPathOps.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkCFObject.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontConfigInterface.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_FontConfigInterface.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_Fontations.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_android.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_data.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_directory.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_empty.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_fontconfig.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_fuchsia.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_mac_ct.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkImageGeneratorCG.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkImageGeneratorNDK.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkImageGeneratorWIC.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkTypeface_fontations.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkTypeface_mac.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkTypeface_win.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/OWNERS create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkColorData.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkEncodedInfo.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkExif.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkGainmapInfo.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkGainmapShader.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkIDChangeListener.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkJpegGainmapEncoder.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkJpegMetadataDecoder.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkPathRef.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkSLSampleUsage.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkWeakRefCnt.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkXmp.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/README.md create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SingleOwner.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkAPI.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkASAN.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkAlign.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkAlignedStorage.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkAnySubclass.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkAssert.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkAttributes.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkCPUTypes.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkContainers.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkDebug.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkDeque.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkFeatures.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkFixed.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkFloatingPoint.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkLoadUserConfig.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkMacros.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkMalloc.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkMath.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkMutex.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkNoncopyable.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkOnce.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkPoint_impl.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkSafe32.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkSemaphore.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkSpan_impl.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTArray.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTDArray.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTFitsIn.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTLogic.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTPin.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTemplates.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkThreadAnnotations.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkThreadID.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTo.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTypeTraits.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/GrDeferredDisplayList.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/GrDeferredDisplayListRecorder.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/GrPromiseImageTexture.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/GrSurfaceCharacterization.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/GrVkSecondaryCBDrawContext.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/SkChromeRemoteGlyphCache.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/SkDiscardableMemory.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/SkImageChromium.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/Slug.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/ganesh/GrContext_Base.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/ganesh/GrD3DTypesMinimal.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/ganesh/GrImageContext.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/ganesh/GrTextureGenerator.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/ganesh/GrTypesPriv.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/graphite/ContextOptionsPriv.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/graphite/DawnTypesPriv.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/graphite/MtlGraphiteTypesPriv.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/graphite/VulkanGraphiteTypesPriv.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/vk/SkiaVulkan.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/sksl/OWNERS create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/sksl/SkSLDebugTrace.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/sksl/SkSLVersion.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/svg/SkSVGCanvas.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkCamera.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkCanvasStateUtils.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkCustomTypeface.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkEventTracer.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkNWayCanvas.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkNoDrawCanvas.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkNullCanvas.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkOrderedFontMgr.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkPaintFilterCanvas.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkParse.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkParsePath.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkShadowUtils.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkTextUtils.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkTraceEventPhase.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/mac/SkCGUtils.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/BUILD.gn create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/OWNERS create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/README.chromium create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/skcms.cc create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/skcms.gni create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/skcms.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/src/Transform_inl.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/src/skcms_Transform.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/src/skcms_TransformBaseline.cc create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/src/skcms_TransformHsw.cc create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/src/skcms_TransformSkx.cc create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/src/skcms_internals.h create mode 100644 Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/src/skcms_public.h create mode 100755 Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/version.sha1 create mode 100644 Tests/LottieMetalTest/skia/libskia.framework/Info.plist create mode 100755 Tests/LottieMetalTest/skia/libskia.framework/libskia diff --git a/Tests/LottieMetalTest/BUILD b/Tests/LottieMetalTest/BUILD index bc5d916de6..4226bfd67d 100644 --- a/Tests/LottieMetalTest/BUILD +++ b/Tests/LottieMetalTest/BUILD @@ -201,6 +201,7 @@ ios_application( resources = [ "//Tests/Common:LaunchScreen", ":TestDataBundle", + "//Tests/LottieMetalTest/skia", ], frameworks = [ ], diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/BUILD b/Tests/LottieMetalTest/SoftwareLottieRenderer/BUILD index df62415519..041c8eda84 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/BUILD +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/BUILD @@ -25,6 +25,8 @@ objc_library( deps = [ "//submodules/LottieCpp", "//Tests/LottieMetalTest/thorvg", + "//Tests/LottieMetalTest/skia", + "//Tests/LottieMetalTest/skia:libskia" ], sdk_frameworks = [ "Foundation", diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SkiaCanvasImpl.cpp b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SkiaCanvasImpl.cpp new file mode 100644 index 0000000000..cb7bc72879 --- /dev/null +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SkiaCanvasImpl.cpp @@ -0,0 +1,316 @@ +#include "SkiaCanvasImpl.h" + +#include "include/core/SkCanvas.h" +#include "include/core/SkColor.h" +#include "include/core/SkFont.h" +#include "include/core/SkFontTypes.h" +#include "include/core/SkGraphics.h" +#include "include/core/SkPaint.h" +#include "include/core/SkPoint.h" +#include "include/core/SkRect.h" +#include "include/core/SkShader.h" +#include "include/core/SkString.h" +#include "include/core/SkSurface.h" +#include "include/core/SkTileMode.h" +#include "include/core/SkPath.h" +#include "include/core/SkPathEffect.h" +#include "include/effects/SkDashPathEffect.h" +#include "include/effects/SkGradientShader.h" + +namespace lottie { + +namespace { + +SkColor skColor(Color const &color) { + return SkColorSetARGB((uint8_t)(color.a * 255.0), (uint8_t)(color.r * 255.0), (uint8_t)(color.g * 255.0), (uint8_t)(color.b * 255.0)); +} + +void skPath(CanvasPathEnumerator const &enumeratePath, SkPath &nativePath) { + enumeratePath([&](PathCommand const &command) { + switch (command.type) { + case PathCommandType::MoveTo: { + nativePath.moveTo(command.points[0].x, command.points[0].y); + break; + } + case PathCommandType::LineTo: { + nativePath.lineTo(command.points[0].x, command.points[0].y); + break; + } + case PathCommandType::CurveTo: { + nativePath.cubicTo(command.points[0].x, command.points[0].y, command.points[1].x, command.points[1].y, command.points[2].x, command.points[2].y); + break; + } + case PathCommandType::Close: { + nativePath.close(); + break; + } + } + }); +} + +} + +SkiaCanvasImpl::SkiaCanvasImpl(int width, int height) : +_width(width), _height(height) { + int bytesPerRow = width * 4; + _pixelData = malloc(bytesPerRow * height); + _ownsPixelData = true; + + _surface = SkSurfaces::WrapPixels( + SkImageInfo::MakeN32Premul(width, height), + _pixelData, + bytesPerRow, + nullptr + ); + + _canvas = _surface->getCanvas(); + _canvas->resetMatrix(); + _canvas->clear(SkColorSetARGB(0, 0, 0, 0)); +} + +SkiaCanvasImpl::SkiaCanvasImpl(int width, int height, int bytesPerRow, void *pixelData) { + _pixelData = pixelData; + _ownsPixelData = false; + + _surface = SkSurfaces::WrapPixels( + SkImageInfo::MakeN32Premul(width, height), + _pixelData, + bytesPerRow, + nullptr + ); + + _canvas = _surface->getCanvas(); + _canvas->resetMatrix(); + _canvas->clear(SkColorSetARGB(0, 0, 0, 0)); +} + +SkiaCanvasImpl::~SkiaCanvasImpl() { + if (_ownsPixelData) { + free(_pixelData); + } +} + +int SkiaCanvasImpl::width() const { + return _width; +} + +int SkiaCanvasImpl::height() const { + return _height; +} + +std::shared_ptr SkiaCanvasImpl::makeLayer(int width, int height) { + return std::make_shared(width, height); +} + +void SkiaCanvasImpl::saveState() { + _canvas->save(); +} + +void SkiaCanvasImpl::restoreState() { + _canvas->restore(); +} + +void SkiaCanvasImpl::fillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, lottie::Color const &color) { + SkPaint paint; + paint.setColor(skColor(color)); + paint.setAlphaf(_alpha); + paint.setAntiAlias(true); + paint.setBlendMode(_blendMode); + + SkPath nativePath; + skPath(enumeratePath, nativePath); + nativePath.setFillType(fillRule == FillRule::EvenOdd ? SkPathFillType::kEvenOdd : SkPathFillType::kWinding); + + _canvas->drawPath(nativePath, paint); +} + +void SkiaCanvasImpl::linearGradientFillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, lottie::Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) { + SkPaint paint; + paint.setAntiAlias(true); + paint.setBlendMode(_blendMode); + paint.setDither(false); + paint.setStyle(SkPaint::Style::kFill_Style); + + SkPoint linearPoints[2] = { + SkPoint::Make(start.x, start.y), + SkPoint::Make(end.x, end.y) + }; + + std::vector colors; + for (const auto &color : gradient.colors()) { + colors.push_back(skColor(Color(color.r, color.g, color.b, color.a * _alpha))); + } + + std::vector locations; + for (auto location : gradient.locations()) { + locations.push_back(location); + } + + paint.setShader(SkGradientShader::MakeLinear(linearPoints, colors.data(), locations.data(), (int)colors.size(), SkTileMode::kMirror)); + + SkPath nativePath; + skPath(enumeratePath, nativePath); + nativePath.setFillType(fillRule == FillRule::EvenOdd ? SkPathFillType::kEvenOdd : SkPathFillType::kWinding); + + _canvas->drawPath(nativePath, paint); +} + +void SkiaCanvasImpl::radialGradientFillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, lottie::Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) { + SkPaint paint; + paint.setAntiAlias(true); + paint.setBlendMode(_blendMode); + paint.setDither(false); + paint.setStyle(SkPaint::Style::kFill_Style); + + std::vector colors; + for (const auto &color : gradient.colors()) { + colors.push_back(skColor(Color(color.r, color.g, color.b, color.a * _alpha))); + } + + std::vector locations; + for (auto location : gradient.locations()) { + locations.push_back(location); + } + + paint.setShader(SkGradientShader::MakeRadial(SkPoint::Make(startCenter.x, startCenter.y), endRadius, colors.data(), locations.data(), (int)colors.size(), SkTileMode::kMirror)); + + SkPath nativePath; + skPath(enumeratePath, nativePath); + nativePath.setFillType(fillRule == FillRule::EvenOdd ? SkPathFillType::kEvenOdd : SkPathFillType::kWinding); + + _canvas->drawPath(nativePath, paint); +} + +void SkiaCanvasImpl::strokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, lottie::Color const &color) { + SkPaint paint; + paint.setAntiAlias(true); + paint.setBlendMode(_blendMode); + paint.setColor(skColor(color)); + paint.setAlphaf(_alpha); + paint.setStyle(SkPaint::Style::kStroke_Style); + + paint.setStrokeWidth(lineWidth); + switch (lineJoin) { + case LineJoin::Miter: { + paint.setStrokeJoin(SkPaint::Join::kMiter_Join); + break; + } + case LineJoin::Round: { + paint.setStrokeJoin(SkPaint::Join::kRound_Join); + break; + } + case LineJoin::Bevel: { + paint.setStrokeJoin(SkPaint::Join::kBevel_Join); + break; + } + default: { + paint.setStrokeJoin(SkPaint::Join::kBevel_Join); + break; + } + } + + switch (lineCap) { + case LineCap::Butt: { + paint.setStrokeCap(SkPaint::Cap::kButt_Cap); + break; + } + case LineCap::Round: { + paint.setStrokeCap(SkPaint::Cap::kRound_Cap); + break; + } + case LineCap::Square: { + paint.setStrokeCap(SkPaint::Cap::kSquare_Cap); + break; + } + default: { + paint.setStrokeCap(SkPaint::Cap::kSquare_Cap); + break; + } + } + + if (!dashPattern.empty()) { + std::vector intervals; + intervals.reserve(dashPattern.size()); + for (auto value : dashPattern) { + intervals.push_back(value); + } + paint.setPathEffect(SkDashPathEffect::Make(intervals.data(), (int)intervals.size(), dashPhase)); + } + + SkPath nativePath; + skPath(enumeratePath, nativePath); + + _canvas->drawPath(nativePath, paint); +} + +void SkiaCanvasImpl::linearGradientStrokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) { + assert(false); +} + +void SkiaCanvasImpl::radialGradientStrokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) { + assert(false); +} + +void SkiaCanvasImpl::fill(lottie::CGRect const &rect, lottie::Color const &fillColor) { + SkPaint paint; + paint.setAntiAlias(true); + paint.setColor(skColor(fillColor)); + paint.setAlphaf(_alpha); + paint.setBlendMode(_blendMode); + + _canvas->drawRect(SkRect::MakeXYWH(rect.x, rect.y, rect.width, rect.height), paint); +} + +void SkiaCanvasImpl::setBlendMode(BlendMode blendMode) { + switch (blendMode) { + case BlendMode::Normal: { + _blendMode = SkBlendMode::kSrcOver; + break; + } + case BlendMode::DestinationIn: { + _blendMode = SkBlendMode::kDstIn; + break; + } + case BlendMode::DestinationOut: { + _blendMode = SkBlendMode::kDstOut; + break; + } + default: { + _blendMode = SkBlendMode::kSrcOver; + break; + } + } +} + +void SkiaCanvasImpl::setAlpha(float alpha) { + _alpha = alpha; +} + +void SkiaCanvasImpl::concatenate(lottie::Transform2D const &transform) { + SkScalar m9[9] = { + transform.rows().columns[0][0], transform.rows().columns[1][0], transform.rows().columns[2][0], + transform.rows().columns[0][1], transform.rows().columns[1][1], transform.rows().columns[2][1], + transform.rows().columns[0][2], transform.rows().columns[1][2], transform.rows().columns[2][2] + }; + SkMatrix matrix; + matrix.set9(m9); + _canvas->concat(matrix); +} + +void SkiaCanvasImpl::draw(std::shared_ptr const &other, lottie::CGRect const &rect) { + SkiaCanvasImpl *impl = (SkiaCanvasImpl *)other.get(); + auto image = impl->surface()->makeImageSnapshot(); + SkPaint paint; + paint.setBlendMode(_blendMode); + paint.setAlphaf(_alpha); + _canvas->drawImageRect(image.get(), SkRect::MakeXYWH(rect.x, rect.y, rect.width, rect.height), SkSamplingOptions(SkFilterMode::kLinear), &paint); +} + +void SkiaCanvasImpl::flush() { +} + +sk_sp SkiaCanvasImpl::surface() const { + return _surface; +} + +} diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SkiaCanvasImpl.h b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SkiaCanvasImpl.h new file mode 100644 index 0000000000..6d7a2f35ef --- /dev/null +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SkiaCanvasImpl.h @@ -0,0 +1,57 @@ +#ifndef SkiaCanvasImpl_h +#define SkiaCanvasImpl_h + +#include + +#include "include/core/SkCanvas.h" +#include "include/core/SkSurface.h" + +namespace lottie { + +class SkiaCanvasImpl: public Canvas { +public: + SkiaCanvasImpl(int width, int height); + SkiaCanvasImpl(int width, int height, int bytesPerRow, void *pixelData); + virtual ~SkiaCanvasImpl(); + + virtual int width() const override; + virtual int height() const override; + + virtual std::shared_ptr makeLayer(int width, int height) override; + + virtual void saveState() override; + virtual void restoreState() override; + + virtual void fillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, lottie::Color const &color) override; + virtual void linearGradientFillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, lottie::Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) override; + virtual void radialGradientFillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, lottie::Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) override; + virtual void strokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, lottie::Color const &color) override; + virtual void linearGradientStrokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) override; + virtual void radialGradientStrokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) override; + virtual void fill(lottie::CGRect const &rect, lottie::Color const &fillColor) override; + + virtual void setBlendMode(BlendMode blendMode) override; + + virtual void setAlpha(float alpha) override; + + virtual void concatenate(lottie::Transform2D const &transform) override; + + virtual void draw(std::shared_ptr const &other, lottie::CGRect const &rect) override; + + void flush(); + sk_sp surface() const; + +private: + void *_pixelData = nullptr; + bool _ownsPixelData = false; + int _width = 0; + int _height = 0; + sk_sp _surface; + SkCanvas *_canvas = nullptr; + SkBlendMode _blendMode = SkBlendMode::kSrcOver; + double _alpha = 1.0; +}; + +} + +#endif diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm index 85cf5b7a85..8476ccd457 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm @@ -5,11 +5,14 @@ #import "CoreGraphicsCanvasImpl.h" #import "ThorVGCanvasImpl.h" +#import "SkiaCanvasImpl.h" #include #include #include +#import + CGRect getPathNativeBoundingBox(CGPathRef _Nonnull path) { auto rect = calculatePathBoundingBox(path); return CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height); @@ -69,7 +72,84 @@ CGRect getPathNativeBoundingBox(CGPathRef _Nonnull path) { return [[UIImage alloc] initWithCGImage:std::static_pointer_cast(image)->nativeImage()]; } else { - if ((int64_t)"" < 0) { + if ((int64_t)"" > 0) { + /*auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul((int)size.width, (int)size.height)); + + int bytesPerRow = ((int)size.width) * 4; + void *pixelData = malloc(bytesPerRow * (int)size.height); + + sk_sp surface2 = SkSurfaces::WrapPixels( + SkImageInfo::MakeN32Premul((int)size.width, (int)size.height), + pixelData, + bytesPerRow, + nullptr + ); + + SkCanvas *canvas = surface->getCanvas(); + + SkPaint paint; + paint.setAntiAlias(true); + SkPath path; + path.moveTo(124, 108); + path.lineTo(172, 24); + path.addCircle(50, 50, 30); + path.moveTo(36, 148); + path.quadTo(66, 188, 120, 136); + canvas->drawPath(path, paint); + paint.setStyle(SkPaint::kStroke_Style); + paint.setColor(SK_ColorBLUE); + paint.setStrokeWidth(3); + canvas->drawPath(path, paint); + + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host; + + CGContextRef targetContext = CGBitmapContextCreate(pixelData, (int)size.width, (int)size.height, 8, bytesPerRow, colorSpace, bitmapInfo); + CGColorSpaceRelease(colorSpace); + + CGImageRef bitmapImage = CGBitmapContextCreateImage(targetContext); + UIImage *image = [[UIImage alloc] initWithCGImage:bitmapImage scale:1.0f orientation:UIImageOrientationDownMirrored]; + CGImageRelease(bitmapImage); + + CGContextRelease(targetContext); + + free(pixelData); + + return image;*/ + + int bytesPerRow = ((int)size.width) * 4; + void *pixelData = malloc(bytesPerRow * (int)size.height); + auto context = std::make_shared((int)size.width, (int)size.height, bytesPerRow, pixelData); + + _canvasRenderer->render(_renderer, context, lottie::Vector2D(size.width, size.height)); + + context->flush(); + + vImage_Buffer src; + src.data = (void *)pixelData; + src.width = (int)size.width; + src.height = (int)size.height; + src.rowBytes = bytesPerRow; + + uint8_t permuteMap[4] = {2, 1, 0, 3}; + vImagePermuteChannels_ARGB8888(&src, &src, permuteMap, kvImageDoNotTile); + + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host; + + CGContextRef targetContext = CGBitmapContextCreate(pixelData, (int)size.width, (int)size.height, 8, bytesPerRow, colorSpace, bitmapInfo); + CGColorSpaceRelease(colorSpace); + + CGImageRef bitmapImage = CGBitmapContextCreateImage(targetContext); + UIImage *image = [[UIImage alloc] initWithCGImage:bitmapImage scale:1.0f orientation:UIImageOrientationDownMirrored]; + CGImageRelease(bitmapImage); + + CGContextRelease(targetContext); + + free(pixelData); + + return image; + } else if ((int64_t)"" < 0) { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ lottie::ThorVGCanvasImpl::initializeOnce(); diff --git a/Tests/LottieMetalTest/Sources/ViewController.swift b/Tests/LottieMetalTest/Sources/ViewController.swift index 34393712aa..0bd665e3a9 100644 --- a/Tests/LottieMetalTest/Sources/ViewController.swift +++ b/Tests/LottieMetalTest/Sources/ViewController.swift @@ -113,15 +113,15 @@ public final class ViewController: UIViewController { SharedDisplayLinkDriver.shared.updateForegroundState(true) let bundlePath = Bundle.main.path(forResource: "TestDataBundle", ofType: "bundle")! - let filePath = bundlePath + "/fireworks.json" + let filePath = bundlePath + "/fire.json" - let performanceFrameSize = 8 + let performanceFrameSize = 512 self.view.layer.addSublayer(MetalEngine.shared.rootLayer) - if "".isEmpty { + if !"".isEmpty { if #available(iOS 13.0, *) { - self.test = ReferenceCompareTest(view: self.view, testNonReference: false) + self.test = ReferenceCompareTest(view: self.view, testNonReference: true) } } else if !"".isEmpty { /*let cachedAnimation = cacheLottieMetalAnimation(path: filePath)! diff --git a/Tests/LottieMetalTest/skia/BUILD b/Tests/LottieMetalTest/skia/BUILD new file mode 100644 index 0000000000..2444ddde2c --- /dev/null +++ b/Tests/LottieMetalTest/skia/BUILD @@ -0,0 +1,43 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") +load("@build_bazel_rules_apple//apple:apple.bzl", + "apple_dynamic_framework_import", +) + + +apple_dynamic_framework_import( + name = "libskia", + framework_imports = glob([ + "libskia.framework/**" + ]), + visibility = ["//visibility:public"], +) + +objc_library( + name = "skia", + enable_modules = True, + module_name = "skia", + srcs = glob([ + ]), + copts = [ + "-I{}/PublicHeaders/skia".format(package_name()), + ], + linkopts = [ + "-Wl,-rpath,@loader_path/Frameworks/libskia.framework", + ], + hdrs = glob([ + "PublicHeaders/**/*.h", + ]), + includes = [ + "PublicHeaders", + "PublicHeaders/skia", + ], + deps = [ + ":libskia", + ], + sdk_frameworks = [ + "Foundation", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/OWNERS b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/OWNERS new file mode 100644 index 0000000000..0d7fbad28a --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/OWNERS @@ -0,0 +1,15 @@ +set noparent + +# Include one of the following reviewers for CLs that add or change Skia's public API: +brianosman@google.com +egdaniel@google.com +fmalita@google.com +fmalita@chromium.org +hcm@google.com +herb@google.com +robertphillips@google.com + +per-file BUILD.bazel=bungeman@google.com +per-file BUILD.bazel=jcgregorio@google.com +per-file BUILD.bazel=kjlubick@google.com +per-file BUILD.bazel=lovisolo@google.com diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkAndroidCodec.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkAndroidCodec.h new file mode 100644 index 0000000000..2b8a79751c --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkAndroidCodec.h @@ -0,0 +1,297 @@ +/* + * 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 SkAndroidCodec_DEFINED +#define SkAndroidCodec_DEFINED + +#include "include/codec/SkCodec.h" +#include "include/core/SkColorSpace.h" +#include "include/core/SkImageInfo.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkSize.h" +#include "include/core/SkTypes.h" +#include "include/private/SkEncodedInfo.h" +#include "include/private/base/SkNoncopyable.h" +#include "modules/skcms/skcms.h" + +// TODO(kjlubick, bungeman) Replace these includes with forward declares +#include "include/codec/SkEncodedImageFormat.h" // IWYU pragma: keep +#include "include/core/SkAlphaType.h" // IWYU pragma: keep +#include "include/core/SkColorType.h" // IWYU pragma: keep + +#include +#include + +class SkData; +class SkPngChunkReader; +class SkStream; +struct SkGainmapInfo; +struct SkIRect; + +/** + * Abstract interface defining image codec functionality that is necessary for + * Android. + */ +class SK_API SkAndroidCodec : SkNoncopyable { +public: + /** + * Deprecated. + * + * Now that SkAndroidCodec supports multiframe images, there are multiple + * ways to handle compositing an oriented frame on top of an oriented frame + * with different tradeoffs. SkAndroidCodec now ignores the orientation and + * forces the client to handle it. + */ + enum class ExifOrientationBehavior { + kIgnore, + kRespect, + }; + + /** + * Pass ownership of an SkCodec to a newly-created SkAndroidCodec. + */ + static std::unique_ptr MakeFromCodec(std::unique_ptr); + + /** + * If this stream represents an encoded image that we know how to decode, + * return an SkAndroidCodec that can decode it. Otherwise return NULL. + * + * The SkPngChunkReader handles unknown chunks in PNGs. + * See SkCodec.h for more details. + * + * If NULL is returned, the stream is deleted immediately. Otherwise, the + * SkCodec takes ownership of it, and will delete it when done with it. + */ + static std::unique_ptr MakeFromStream(std::unique_ptr, + SkPngChunkReader* = nullptr); + + /** + * If this data represents an encoded image that we know how to decode, + * return an SkAndroidCodec that can decode it. Otherwise return NULL. + * + * The SkPngChunkReader handles unknown chunks in PNGs. + * See SkCodec.h for more details. + */ + static std::unique_ptr MakeFromData(sk_sp, SkPngChunkReader* = nullptr); + + virtual ~SkAndroidCodec(); + + // TODO: fInfo is now just a cache of SkCodec's SkImageInfo. No need to + // cache and return a reference here, once Android call-sites are updated. + const SkImageInfo& getInfo() const { return fInfo; } + + /** + * Return the ICC profile of the encoded data. + */ + const skcms_ICCProfile* getICCProfile() const { + return fCodec->getEncodedInfo().profile(); + } + + /** + * Format of the encoded data. + */ + SkEncodedImageFormat getEncodedFormat() const { return fCodec->getEncodedFormat(); } + + /** + * @param requestedColorType Color type requested by the client + * + * |requestedColorType| may be overriden. We will default to kF16 + * for high precision images. + * + * In the general case, if it is possible to decode to + * |requestedColorType|, this returns |requestedColorType|. + * Otherwise, this returns a color type that is an appropriate + * match for the the encoded data. + */ + SkColorType computeOutputColorType(SkColorType requestedColorType); + + /** + * @param requestedUnpremul Indicates if the client requested + * unpremultiplied output + * + * Returns the appropriate alpha type to decode to. If the image + * has alpha, the value of requestedUnpremul will be honored. + */ + SkAlphaType computeOutputAlphaType(bool requestedUnpremul); + + /** + * @param outputColorType Color type that the client will decode to. + * @param prefColorSpace Preferred color space to decode to. + * This may not return |prefColorSpace| for + * specific color types. + * + * Returns the appropriate color space to decode to. + */ + sk_sp computeOutputColorSpace(SkColorType outputColorType, + sk_sp prefColorSpace = nullptr); + + /** + * Compute the appropriate sample size to get to |size|. + * + * @param size As an input parameter, the desired output size of + * the decode. As an output parameter, the smallest sampled size + * larger than the input. + * @return the sample size to set AndroidOptions::fSampleSize to decode + * to the output |size|. + */ + int computeSampleSize(SkISize* size) const; + + /** + * Returns the dimensions of the scaled output image, for an input + * sampleSize. + * + * When the sample size divides evenly into the original dimensions, the + * scaled output dimensions will simply be equal to the original + * dimensions divided by the sample size. + * + * When the sample size does not divide even into the original + * dimensions, the codec may round up or down, depending on what is most + * efficient to decode. + * + * Finally, the codec will always recommend a non-zero output, so the output + * dimension will always be one if the sampleSize is greater than the + * original dimension. + */ + SkISize getSampledDimensions(int sampleSize) const; + + /** + * Return (via desiredSubset) a subset which can decoded from this codec, + * or false if the input subset is invalid. + * + * @param desiredSubset in/out parameter + * As input, a desired subset of the original bounds + * (as specified by getInfo). + * As output, if true is returned, desiredSubset may + * have been modified to a subset which is + * supported. Although a particular change may have + * been made to desiredSubset to create something + * supported, it is possible other changes could + * result in a valid subset. If false is returned, + * desiredSubset's value is undefined. + * @return true If the input desiredSubset is valid. + * desiredSubset may be modified to a subset + * supported by the codec. + * false If desiredSubset is invalid (NULL or not fully + * contained within the image). + */ + bool getSupportedSubset(SkIRect* desiredSubset) const; + // TODO: Rename SkCodec::getValidSubset() to getSupportedSubset() + + /** + * Returns the dimensions of the scaled, partial output image, for an + * input sampleSize and subset. + * + * @param sampleSize Factor to scale down by. + * @param subset Must be a valid subset of the original image + * dimensions and a subset supported by SkAndroidCodec. + * getSubset() can be used to obtain a subset supported + * by SkAndroidCodec. + * @return Size of the scaled partial image. Or zero size + * if either of the inputs is invalid. + */ + SkISize getSampledSubsetDimensions(int sampleSize, const SkIRect& subset) const; + + /** + * Additional options to pass to getAndroidPixels(). + */ + // FIXME: It's a bit redundant to name these AndroidOptions when this class is already + // called SkAndroidCodec. On the other hand, it's may be a bit confusing to call + // these Options when SkCodec has a slightly different set of Options. Maybe these + // should be DecodeOptions or SamplingOptions? + struct AndroidOptions : public SkCodec::Options { + AndroidOptions() + : SkCodec::Options() + , fSampleSize(1) + {} + + /** + * The client may provide an integer downscale factor for the decode. + * The codec may implement this downscaling by sampling or another + * method if it is more efficient. + * + * The default is 1, representing no downscaling. + */ + int fSampleSize; + }; + + /** + * Decode into the given pixels, a block of memory of size at + * least (info.fHeight - 1) * rowBytes + (info.fWidth * + * bytesPerPixel) + * + * Repeated calls to this function should give the same results, + * allowing the PixelRef to be immutable. + * + * @param info A description of the format (config, size) + * expected by the caller. This can simply be identical + * to the info returned by getInfo(). + * + * This contract also allows the caller to specify + * different output-configs, which the implementation can + * decide to support or not. + * + * A size that does not match getInfo() implies a request + * to scale or subset. If the codec cannot perform this + * scaling or subsetting, it will return an error code. + * + * The AndroidOptions object is also used to specify any requested scaling or subsetting + * using options->fSampleSize and options->fSubset. If NULL, the defaults (as specified above + * for AndroidOptions) are used. + * + * @return Result kSuccess, or another value explaining the type of failure. + */ + // FIXME: It's a bit redundant to name this getAndroidPixels() when this class is already + // called SkAndroidCodec. On the other hand, it's may be a bit confusing to call + // this getPixels() when it is a slightly different API than SkCodec's getPixels(). + // Maybe this should be decode() or decodeSubset()? + SkCodec::Result getAndroidPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, + const AndroidOptions* options); + + /** + * Simplified version of getAndroidPixels() where we supply the default AndroidOptions as + * specified above for AndroidOptions. It will not perform any scaling or subsetting. + */ + SkCodec::Result getAndroidPixels(const SkImageInfo& info, void* pixels, size_t rowBytes); + + SkCodec::Result getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes) { + return this->getAndroidPixels(info, pixels, rowBytes); + } + + SkCodec* codec() const { return fCodec.get(); } + + /** + * Retrieve the gainmap for an image. + * + * @param outInfo On success, this is populated with the parameters for + * rendering this gainmap. This parameter must be non-nullptr. + * + * @param outGainmapImageStream On success, this is populated with a stream from which the + * gainmap image may be decoded. This parameter is optional, and + * may be set to nullptr. + * + * @return If this has a gainmap image and that gainmap image was + * successfully extracted then return true. Otherwise return + * false. + */ + bool getAndroidGainmap(SkGainmapInfo* outInfo, + std::unique_ptr* outGainmapImageStream); + +protected: + SkAndroidCodec(SkCodec*); + + virtual SkISize onGetSampledDimensions(int sampleSize) const = 0; + + virtual bool onGetSupportedSubset(SkIRect* desiredSubset) const = 0; + + virtual SkCodec::Result onGetAndroidPixels(const SkImageInfo& info, void* pixels, + size_t rowBytes, const AndroidOptions& options) = 0; + +private: + const SkImageInfo fInfo; + std::unique_ptr fCodec; +}; +#endif // SkAndroidCodec_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkAvifDecoder.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkAvifDecoder.h new file mode 100644 index 0000000000..840a600a31 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkAvifDecoder.h @@ -0,0 +1,44 @@ +/* + * Copyright 2023 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SkAvifDecoder_DEFINED +#define SkAvifDecoder_DEFINED + +#include "include/codec/SkCodec.h" +#include "include/core/SkRefCnt.h" +#include "include/private/base/SkAPI.h" + +class SkData; +class SkStream; + +#include + +namespace SkAvifDecoder { + +/** Returns true if this data claims to be a AVIF image. */ +SK_API bool IsAvif(const void*, size_t); + +/** + * Attempts to decode the given bytes as a AVIF. + * + * If the bytes are not a AVIF, returns nullptr. + * + * DecodeContext is ignored + */ +SK_API std::unique_ptr Decode(std::unique_ptr, + SkCodec::Result*, + SkCodecs::DecodeContext = nullptr); +SK_API std::unique_ptr Decode(sk_sp, + SkCodec::Result*, + SkCodecs::DecodeContext = nullptr); + +inline constexpr SkCodecs::Decoder Decoder() { + return { "avif", IsAvif, Decode }; +} + +} // namespace SkAvifDecoder + +#endif // SkAvifDecoder_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkBmpDecoder.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkBmpDecoder.h new file mode 100644 index 0000000000..104decf3cf --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkBmpDecoder.h @@ -0,0 +1,44 @@ +/* + * Copyright 2023 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SkBmpDecoder_DEFINED +#define SkBmpDecoder_DEFINED + +#include "include/codec/SkCodec.h" +#include "include/core/SkRefCnt.h" +#include "include/private/base/SkAPI.h" + +class SkData; +class SkStream; + +#include + +namespace SkBmpDecoder { + +/** Returns true if this data claims to be a BMP image. */ +SK_API bool IsBmp(const void*, size_t); + +/** + * Attempts to decode the given bytes as a BMP. + * + * If the bytes are not a BMP, returns nullptr. + * + * DecodeContext is ignored + */ +SK_API std::unique_ptr Decode(std::unique_ptr, + SkCodec::Result*, + SkCodecs::DecodeContext = nullptr); +SK_API std::unique_ptr Decode(sk_sp, + SkCodec::Result*, + SkCodecs::DecodeContext = nullptr); + +inline constexpr SkCodecs::Decoder Decoder() { + return { "bmp", IsBmp, Decode }; +} + +} // namespace SkBmpDecoder + +#endif // SkBmpDecoder_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkCodec.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkCodec.h new file mode 100644 index 0000000000..782ea69f32 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkCodec.h @@ -0,0 +1,1085 @@ +/* + * 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 SkCodec_DEFINED +#define SkCodec_DEFINED + +#include "include/codec/SkEncodedOrigin.h" +#include "include/core/SkImageInfo.h" +#include "include/core/SkPixmap.h" +#include "include/core/SkRect.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkSize.h" +#include "include/core/SkSpan.h" +#include "include/core/SkTypes.h" +#include "include/core/SkYUVAPixmaps.h" +#include "include/private/SkEncodedInfo.h" +#include "include/private/base/SkNoncopyable.h" +#include "modules/skcms/skcms.h" + +#include +#include +#include +#include +#include +#include +#include + +class SkData; +class SkFrameHolder; +class SkImage; +class SkPngChunkReader; +class SkSampler; +class SkStream; +struct SkGainmapInfo; +enum SkAlphaType : int; +enum class SkEncodedImageFormat; + +namespace SkCodecAnimation { +enum class Blend; +enum class DisposalMethod; +} + +namespace DM { +class CodecSrc; +} // namespace DM + +namespace SkCodecs { +struct Decoder; +} + +/** + * Abstraction layer directly on top of an image codec. + */ +class SK_API SkCodec : SkNoncopyable { +public: + /** + * Minimum number of bytes that must be buffered in SkStream input. + * + * An SkStream passed to NewFromStream must be able to use this many + * bytes to determine the image type. Then the same SkStream must be + * passed to the correct decoder to read from the beginning. + * + * This can be accomplished by implementing peek() to support peeking + * this many bytes, or by implementing rewind() to be able to rewind() + * after reading this many bytes. + */ + static constexpr size_t MinBufferedBytesNeeded() { return 32; } + + /** + * Error codes for various SkCodec methods. + */ + enum Result { + /** + * General return value for success. + */ + kSuccess, + /** + * The input is incomplete. A partial image was generated. + */ + kIncompleteInput, + /** + * Like kIncompleteInput, except the input had an error. + * + * If returned from an incremental decode, decoding cannot continue, + * even with more data. + */ + kErrorInInput, + /** + * The generator cannot convert to match the request, ignoring + * dimensions. + */ + kInvalidConversion, + /** + * The generator cannot scale to requested size. + */ + kInvalidScale, + /** + * Parameters (besides info) are invalid. e.g. NULL pixels, rowBytes + * too small, etc. + */ + kInvalidParameters, + /** + * The input did not contain a valid image. + */ + kInvalidInput, + /** + * Fulfilling this request requires rewinding the input, which is not + * supported for this input. + */ + kCouldNotRewind, + /** + * An internal error, such as OOM. + */ + kInternalError, + /** + * This method is not implemented by this codec. + * FIXME: Perhaps this should be kUnsupported? + */ + kUnimplemented, + }; + + /** + * Readable string representing the error code. + */ + static const char* ResultToString(Result); + + /** + * For container formats that contain both still images and image sequences, + * instruct the decoder how the output should be selected. (Refer to comments + * for each value for more details.) + */ + enum class SelectionPolicy { + /** + * If the container format contains both still images and image sequences, + * SkCodec should choose one of the still images. This is the default. + * Note that kPreferStillImage may prevent use of the animation features + * if the input is not rewindable. + */ + kPreferStillImage, + /** + * If the container format contains both still images and image sequences, + * SkCodec should choose one of the image sequences for animation. + */ + kPreferAnimation, + }; + + /** + * If this stream represents an encoded image that we know how to decode, + * return an SkCodec that can decode it. Otherwise return NULL. + * + * As stated above, this call must be able to peek or read + * MinBufferedBytesNeeded to determine the correct format, and then start + * reading from the beginning. First it will attempt to peek, and it + * assumes that if less than MinBufferedBytesNeeded bytes (but more than + * zero) are returned, this is because the stream is shorter than this, + * so falling back to reading would not provide more data. If peek() + * returns zero bytes, this call will instead attempt to read(). This + * will require that the stream can be rewind()ed. + * + * If Result is not NULL, it will be set to either kSuccess if an SkCodec + * is returned or a reason for the failure if NULL is returned. + * + * If SkPngChunkReader is not NULL, take a ref and pass it to libpng if + * the image is a png. + * + * If the SkPngChunkReader is not NULL then: + * If the image is not a PNG, the SkPngChunkReader will be ignored. + * If the image is a PNG, the SkPngChunkReader will be reffed. + * If the PNG has unknown chunks, the SkPngChunkReader will be used + * to handle these chunks. SkPngChunkReader will be called to read + * any unknown chunk at any point during the creation of the codec + * or the decode. Note that if SkPngChunkReader fails to read a + * chunk, this could result in a failure to create the codec or a + * failure to decode the image. + * If the PNG does not contain unknown chunks, the SkPngChunkReader + * will not be used or modified. + * + * If NULL is returned, the stream is deleted immediately. Otherwise, the + * SkCodec takes ownership of it, and will delete it when done with it. + */ + static std::unique_ptr MakeFromStream( + std::unique_ptr, + SkSpan decoders, + Result* = nullptr, + SkPngChunkReader* = nullptr, + SelectionPolicy selectionPolicy = SelectionPolicy::kPreferStillImage); + // deprecated + static std::unique_ptr MakeFromStream( + std::unique_ptr, + Result* = nullptr, + SkPngChunkReader* = nullptr, + SelectionPolicy selectionPolicy = SelectionPolicy::kPreferStillImage); + + /** + * If this data represents an encoded image that we know how to decode, + * return an SkCodec that can decode it. Otherwise return NULL. + * + * If the SkPngChunkReader is not NULL then: + * If the image is not a PNG, the SkPngChunkReader will be ignored. + * If the image is a PNG, the SkPngChunkReader will be reffed. + * If the PNG has unknown chunks, the SkPngChunkReader will be used + * to handle these chunks. SkPngChunkReader will be called to read + * any unknown chunk at any point during the creation of the codec + * or the decode. Note that if SkPngChunkReader fails to read a + * chunk, this could result in a failure to create the codec or a + * failure to decode the image. + * If the PNG does not contain unknown chunks, the SkPngChunkReader + * will not be used or modified. + */ + static std::unique_ptr MakeFromData(sk_sp, + SkSpan decoders, + SkPngChunkReader* = nullptr); + // deprecated + static std::unique_ptr MakeFromData(sk_sp, SkPngChunkReader* = nullptr); + + virtual ~SkCodec(); + + /** + * Return a reasonable SkImageInfo to decode into. + * + * If the image has an ICC profile that does not map to an SkColorSpace, + * the returned SkImageInfo will use SRGB. + */ + SkImageInfo getInfo() const { return fEncodedInfo.makeImageInfo(); } + + SkISize dimensions() const { return {fEncodedInfo.width(), fEncodedInfo.height()}; } + SkIRect bounds() const { + return SkIRect::MakeWH(fEncodedInfo.width(), fEncodedInfo.height()); + } + + /** + * Return the ICC profile of the encoded data. + */ + const skcms_ICCProfile* getICCProfile() const { + return this->getEncodedInfo().profile(); + } + + /** + * Returns the image orientation stored in the EXIF data. + * If there is no EXIF data, or if we cannot read the EXIF data, returns kTopLeft. + */ + SkEncodedOrigin getOrigin() const { return fOrigin; } + + /** + * Return a size that approximately supports the desired scale factor. + * The codec may not be able to scale efficiently to the exact scale + * factor requested, so return a size that approximates that scale. + * The returned value is the codec's suggestion for the closest valid + * scale that it can natively support + */ + SkISize getScaledDimensions(float desiredScale) const { + // Negative and zero scales are errors. + SkASSERT(desiredScale > 0.0f); + if (desiredScale <= 0.0f) { + return SkISize::Make(0, 0); + } + + // Upscaling is not supported. Return the original size if the client + // requests an upscale. + if (desiredScale >= 1.0f) { + return this->dimensions(); + } + return this->onGetScaledDimensions(desiredScale); + } + + /** + * Return (via desiredSubset) a subset which can decoded from this codec, + * or false if this codec cannot decode subsets or anything similar to + * desiredSubset. + * + * @param desiredSubset In/out parameter. As input, a desired subset of + * the original bounds (as specified by getInfo). If true is returned, + * desiredSubset may have been modified to a subset which is + * supported. Although a particular change may have been made to + * desiredSubset to create something supported, it is possible other + * changes could result in a valid subset. + * If false is returned, desiredSubset's value is undefined. + * @return true if this codec supports decoding desiredSubset (as + * returned, potentially modified) + */ + bool getValidSubset(SkIRect* desiredSubset) const { + return this->onGetValidSubset(desiredSubset); + } + + /** + * Format of the encoded data. + */ + SkEncodedImageFormat getEncodedFormat() const { return this->onGetEncodedFormat(); } + + /** + * Return the underlying encoded data stream. This may be nullptr if the original + * stream could not be duplicated. + */ + virtual std::unique_ptr getEncodedData() const; + + /** + * Whether or not the memory passed to getPixels is zero initialized. + */ + enum ZeroInitialized { + /** + * The memory passed to getPixels is zero initialized. The SkCodec + * may take advantage of this by skipping writing zeroes. + */ + kYes_ZeroInitialized, + /** + * The memory passed to getPixels has not been initialized to zero, + * so the SkCodec must write all zeroes to memory. + * + * This is the default. It will be used if no Options struct is used. + */ + kNo_ZeroInitialized, + }; + + /** + * Additional options to pass to getPixels. + */ + struct Options { + Options() + : fZeroInitialized(kNo_ZeroInitialized) + , fSubset(nullptr) + , fFrameIndex(0) + , fPriorFrame(kNoFrame) + {} + + ZeroInitialized fZeroInitialized; + /** + * If not NULL, represents a subset of the original image to decode. + * Must be within the bounds returned by getInfo(). + * If the EncodedFormat is SkEncodedImageFormat::kWEBP (the only one which + * currently supports subsets), the top and left values must be even. + * + * In getPixels and incremental decode, we will attempt to decode the + * exact rectangular subset specified by fSubset. + * + * In a scanline decode, it does not make sense to specify a subset + * top or subset height, since the client already controls which rows + * to get and which rows to skip. During scanline decodes, we will + * require that the subset top be zero and the subset height be equal + * to the full height. We will, however, use the values of + * subset left and subset width to decode partial scanlines on calls + * to getScanlines(). + */ + const SkIRect* fSubset; + + /** + * The frame to decode. + * + * Only meaningful for multi-frame images. + */ + int fFrameIndex; + + /** + * If not kNoFrame, the dst already contains the prior frame at this index. + * + * Only meaningful for multi-frame images. + * + * If fFrameIndex needs to be blended with a prior frame (as reported by + * getFrameInfo[fFrameIndex].fRequiredFrame), the client can set this to + * any non-kRestorePrevious frame in [fRequiredFrame, fFrameIndex) to + * indicate that that frame is already in the dst. Options.fZeroInitialized + * is ignored in this case. + * + * If set to kNoFrame, the codec will decode any necessary required frame(s) first. + */ + int fPriorFrame; + }; + + /** + * Decode into the given pixels, a block of memory of size at + * least (info.fHeight - 1) * rowBytes + (info.fWidth * + * bytesPerPixel) + * + * Repeated calls to this function should give the same results, + * allowing the PixelRef to be immutable. + * + * @param info A description of the format (config, size) + * expected by the caller. This can simply be identical + * to the info returned by getInfo(). + * + * This contract also allows the caller to specify + * different output-configs, which the implementation can + * decide to support or not. + * + * A size that does not match getInfo() implies a request + * to scale. If the generator cannot perform this scale, + * it will return kInvalidScale. + * + * If the info contains a non-null SkColorSpace, the codec + * will perform the appropriate color space transformation. + * + * If the caller passes in the SkColorSpace that maps to the + * ICC profile reported by getICCProfile(), the color space + * transformation is a no-op. + * + * If the caller passes a null SkColorSpace, no color space + * transformation will be done. + * + * If a scanline decode is in progress, scanline mode will end, requiring the client to call + * startScanlineDecode() in order to return to decoding scanlines. + * + * @return Result kSuccess, or another value explaining the type of failure. + */ + Result getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, const Options*); + + /** + * Simplified version of getPixels() that uses the default Options. + */ + Result getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes) { + return this->getPixels(info, pixels, rowBytes, nullptr); + } + + Result getPixels(const SkPixmap& pm, const Options* opts = nullptr) { + return this->getPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), opts); + } + + /** + * Return an image containing the pixels. If the codec's origin is not "upper left", + * This will rotate the output image accordingly. + */ + std::tuple, SkCodec::Result> getImage(const SkImageInfo& info, + const Options* opts = nullptr); + std::tuple, SkCodec::Result> getImage(); + + /** + * If decoding to YUV is supported, this returns true. Otherwise, this + * returns false and the caller will ignore output parameter yuvaPixmapInfo. + * + * @param supportedDataTypes Indicates the data type/planar config combinations that are + * supported by the caller. If the generator supports decoding to + * YUV(A), but not as a type in supportedDataTypes, this method + * returns false. + * @param yuvaPixmapInfo Output parameter that specifies the planar configuration, subsampling, + * orientation, chroma siting, plane color types, and row bytes. + */ + bool queryYUVAInfo(const SkYUVAPixmapInfo::SupportedDataTypes& supportedDataTypes, + SkYUVAPixmapInfo* yuvaPixmapInfo) const; + + /** + * Returns kSuccess, or another value explaining the type of failure. + * This always attempts to perform a full decode. To get the planar + * configuration without decoding use queryYUVAInfo(). + * + * @param yuvaPixmaps Contains preallocated pixmaps configured according to a successful call + * to queryYUVAInfo(). + */ + Result getYUVAPlanes(const SkYUVAPixmaps& yuvaPixmaps); + + /** + * Prepare for an incremental decode with the specified options. + * + * This may require a rewind. + * + * If kIncompleteInput is returned, may be called again after more data has + * been provided to the source SkStream. + * + * @param dstInfo Info of the destination. If the dimensions do not match + * those of getInfo, this implies a scale. + * @param dst Memory to write to. Needs to be large enough to hold the subset, + * if present, or the full image as described in dstInfo. + * @param options Contains decoding options, including if memory is zero + * initialized and whether to decode a subset. + * @return Enum representing success or reason for failure. + */ + Result startIncrementalDecode(const SkImageInfo& dstInfo, void* dst, size_t rowBytes, + const Options*); + + Result startIncrementalDecode(const SkImageInfo& dstInfo, void* dst, size_t rowBytes) { + return this->startIncrementalDecode(dstInfo, dst, rowBytes, nullptr); + } + + /** + * Start/continue the incremental decode. + * + * Not valid to call before a call to startIncrementalDecode() returns + * kSuccess. + * + * If kIncompleteInput is returned, may be called again after more data has + * been provided to the source SkStream. + * + * Unlike getPixels and getScanlines, this does not do any filling. This is + * left up to the caller, since they may be skipping lines or continuing the + * decode later. In the latter case, they may choose to initialize all lines + * first, or only initialize the remaining lines after the first call. + * + * @param rowsDecoded Optional output variable returning the total number of + * lines initialized. Only meaningful if this method returns kIncompleteInput. + * Otherwise the implementation may not set it. + * Note that some implementations may have initialized this many rows, but + * not necessarily finished those rows (e.g. interlaced PNG). This may be + * useful for determining what rows the client needs to initialize. + * @return kSuccess if all lines requested in startIncrementalDecode have + * been completely decoded. kIncompleteInput otherwise. + */ + Result incrementalDecode(int* rowsDecoded = nullptr) { + if (!fStartedIncrementalDecode) { + return kInvalidParameters; + } + return this->onIncrementalDecode(rowsDecoded); + } + + /** + * The remaining functions revolve around decoding scanlines. + */ + + /** + * Prepare for a scanline decode with the specified options. + * + * After this call, this class will be ready to decode the first scanline. + * + * This must be called in order to call getScanlines or skipScanlines. + * + * This may require rewinding the stream. + * + * Not all SkCodecs support this. + * + * @param dstInfo Info of the destination. If the dimensions do not match + * those of getInfo, this implies a scale. + * @param options Contains decoding options, including if memory is zero + * initialized. + * @return Enum representing success or reason for failure. + */ + Result startScanlineDecode(const SkImageInfo& dstInfo, const Options* options); + + /** + * Simplified version of startScanlineDecode() that uses the default Options. + */ + Result startScanlineDecode(const SkImageInfo& dstInfo) { + return this->startScanlineDecode(dstInfo, nullptr); + } + + /** + * Write the next countLines scanlines into dst. + * + * Not valid to call before calling startScanlineDecode(). + * + * @param dst Must be non-null, and large enough to hold countLines + * scanlines of size rowBytes. + * @param countLines Number of lines to write. + * @param rowBytes Number of bytes per row. Must be large enough to hold + * a scanline based on the SkImageInfo used to create this object. + * @return the number of lines successfully decoded. If this value is + * less than countLines, this will fill the remaining lines with a + * default value. + */ + int getScanlines(void* dst, int countLines, size_t rowBytes); + + /** + * Skip count scanlines. + * + * Not valid to call before calling startScanlineDecode(). + * + * The default version just calls onGetScanlines and discards the dst. + * NOTE: If skipped lines are the only lines with alpha, this default + * will make reallyHasAlpha return true, when it could have returned + * false. + * + * @return true if the scanlines were successfully skipped + * false on failure, possible reasons for failure include: + * An incomplete input image stream. + * Calling this function before calling startScanlineDecode(). + * If countLines is less than zero or so large that it moves + * the current scanline past the end of the image. + */ + bool skipScanlines(int countLines); + + /** + * The order in which rows are output from the scanline decoder is not the + * same for all variations of all image types. This explains the possible + * output row orderings. + */ + enum SkScanlineOrder { + /* + * By far the most common, this indicates that the image can be decoded + * reliably using the scanline decoder, and that rows will be output in + * the logical order. + */ + kTopDown_SkScanlineOrder, + + /* + * This indicates that the scanline decoder reliably outputs rows, but + * they will be returned in reverse order. If the scanline format is + * kBottomUp, the nextScanline() API can be used to determine the actual + * y-coordinate of the next output row, but the client is not forced + * to take advantage of this, given that it's not too tough to keep + * track independently. + * + * For full image decodes, it is safe to get all of the scanlines at + * once, since the decoder will handle inverting the rows as it + * decodes. + * + * For subset decodes and sampling, it is simplest to get and skip + * scanlines one at a time, using the nextScanline() API. It is + * possible to ask for larger chunks at a time, but this should be used + * with caution. As with full image decodes, the decoder will handle + * inverting the requested rows, but rows will still be delivered + * starting from the bottom of the image. + * + * Upside down bmps are an example. + */ + kBottomUp_SkScanlineOrder, + }; + + /** + * An enum representing the order in which scanlines will be returned by + * the scanline decoder. + * + * This is undefined before startScanlineDecode() is called. + */ + SkScanlineOrder getScanlineOrder() const { return this->onGetScanlineOrder(); } + + /** + * Returns the y-coordinate of the next row to be returned by the scanline + * decoder. + * + * This will equal fCurrScanline, except in the case of strangely + * encoded image types (bottom-up bmps). + * + * Results are undefined when not in scanline decoding mode. + */ + int nextScanline() const { return this->outputScanline(fCurrScanline); } + + /** + * Returns the output y-coordinate of the row that corresponds to an input + * y-coordinate. The input y-coordinate represents where the scanline + * is located in the encoded data. + * + * This will equal inputScanline, except in the case of strangely + * encoded image types (bottom-up bmps, interlaced gifs). + */ + int outputScanline(int inputScanline) const; + + /** + * Return the number of frames in the image. + * + * May require reading through the stream. + */ + int getFrameCount() { + return this->onGetFrameCount(); + } + + // Sentinel value used when a frame index implies "no frame": + // - FrameInfo::fRequiredFrame set to this value means the frame + // is independent. + // - Options::fPriorFrame set to this value means no (relevant) prior frame + // is residing in dst's memory. + static constexpr int kNoFrame = -1; + + // This transitional definition was added in August 2018, and will eventually be removed. +#ifdef SK_LEGACY_SKCODEC_NONE_ENUM + static constexpr int kNone = kNoFrame; +#endif + + /** + * Information about individual frames in a multi-framed image. + */ + struct FrameInfo { + /** + * The frame that this frame needs to be blended with, or + * kNoFrame if this frame is independent (so it can be + * drawn over an uninitialized buffer). + * + * Note that this is the *earliest* frame that can be used + * for blending. Any frame from [fRequiredFrame, i) can be + * used, unless its fDisposalMethod is kRestorePrevious. + */ + int fRequiredFrame; + + /** + * Number of milliseconds to show this frame. + */ + int fDuration; + + /** + * Whether the end marker for this frame is contained in the stream. + * + * Note: this does not guarantee that an attempt to decode will be complete. + * There could be an error in the stream. + */ + bool fFullyReceived; + + /** + * This is conservative; it will still return non-opaque if e.g. a + * color index-based frame has a color with alpha but does not use it. + */ + SkAlphaType fAlphaType; + + /** + * Whether the updated rectangle contains alpha. + * + * This is conservative; it will still be set to true if e.g. a color + * index-based frame has a color with alpha but does not use it. In + * addition, it may be set to true, even if the final frame, after + * blending, is opaque. + */ + bool fHasAlphaWithinBounds; + + /** + * How this frame should be modified before decoding the next one. + */ + SkCodecAnimation::DisposalMethod fDisposalMethod; + + /** + * How this frame should blend with the prior frame. + */ + SkCodecAnimation::Blend fBlend; + + /** + * The rectangle updated by this frame. + * + * It may be empty, if the frame does not change the image. It will + * always be contained by SkCodec::dimensions(). + */ + SkIRect fFrameRect; + }; + + /** + * Return info about a single frame. + * + * Does not read through the stream, so it should be called after + * getFrameCount() to parse any frames that have not already been parsed. + * + * Only supported by animated (multi-frame) codecs. Note that this is a + * property of the codec (the SkCodec subclass), not the image. + * + * To elaborate, some codecs support animation (e.g. GIF). Others do not + * (e.g. BMP). Animated codecs can still represent single frame images. + * Calling getFrameInfo(0, etc) will return true for a single frame GIF + * even if the overall image is not animated (in that the pixels on screen + * do not change over time). When incrementally decoding a GIF image, we + * might only know that there's a single frame *so far*. + * + * For non-animated SkCodec subclasses, it's sufficient but not necessary + * for this method to always return false. + */ + bool getFrameInfo(int index, FrameInfo* info) const { + if (index < 0) { + return false; + } + return this->onGetFrameInfo(index, info); + } + + /** + * Return info about all the frames in the image. + * + * May require reading through the stream to determine info about the + * frames (including the count). + * + * As such, future decoding calls may require a rewind. + * + * This may return an empty vector for non-animated codecs. See the + * getFrameInfo(int, FrameInfo*) comment. + */ + std::vector getFrameInfo(); + + static constexpr int kRepetitionCountInfinite = -1; + + /** + * Return the number of times to repeat, if this image is animated. This number does not + * include the first play through of each frame. For example, a repetition count of 4 means + * that each frame is played 5 times and then the animation stops. + * + * It can return kRepetitionCountInfinite, a negative number, meaning that the animation + * should loop forever. + * + * May require reading the stream to find the repetition count. + * + * As such, future decoding calls may require a rewind. + * + * For still (non-animated) image codecs, this will return 0. + */ + int getRepetitionCount() { + return this->onGetRepetitionCount(); + } + + // 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. + // Not thread safe. + static void Register( + bool (*peek)(const void*, size_t), + std::unique_ptr (*make)(std::unique_ptr, SkCodec::Result*)); + +protected: + const SkEncodedInfo& getEncodedInfo() const { return fEncodedInfo; } + + using XformFormat = skcms_PixelFormat; + + SkCodec(SkEncodedInfo&&, + XformFormat srcFormat, + std::unique_ptr, + SkEncodedOrigin = kTopLeft_SkEncodedOrigin); + + void setSrcXformFormat(XformFormat pixelFormat); + + XformFormat getSrcXformFormat() const { + return fSrcXformFormat; + } + + virtual bool onGetGainmapInfo(SkGainmapInfo*, std::unique_ptr*) { return false; } + + virtual SkISize onGetScaledDimensions(float /*desiredScale*/) const { + // By default, scaling is not supported. + return this->dimensions(); + } + + // FIXME: What to do about subsets?? + /** + * Subclasses should override if they support dimensions other than the + * srcInfo's. + */ + virtual bool onDimensionsSupported(const SkISize&) { + return false; + } + + virtual SkEncodedImageFormat onGetEncodedFormat() const = 0; + + /** + * @param rowsDecoded When the encoded image stream is incomplete, this function + * will return kIncompleteInput and rowsDecoded will be set to + * the number of scanlines that were successfully decoded. + * This will allow getPixels() to fill the uninitialized memory. + */ + virtual Result onGetPixels(const SkImageInfo& info, + void* pixels, size_t rowBytes, const Options&, + int* rowsDecoded) = 0; + + virtual bool onQueryYUVAInfo(const SkYUVAPixmapInfo::SupportedDataTypes&, + SkYUVAPixmapInfo*) const { return false; } + + virtual Result onGetYUVAPlanes(const SkYUVAPixmaps&) { return kUnimplemented; } + + virtual bool onGetValidSubset(SkIRect* /*desiredSubset*/) const { + // By default, subsets are not supported. + return false; + } + + /** + * If the stream was previously read, attempt to rewind. + * + * If the stream needed to be rewound, call onRewind. + * @returns true if the codec is at the right position and can be used. + * false if there was a failure to rewind. + * + * This is called by getPixels(), getYUV8Planes(), startIncrementalDecode() and + * startScanlineDecode(). Subclasses may call if they need to rewind at another time. + */ + [[nodiscard]] bool rewindIfNeeded(); + + /** + * Called by rewindIfNeeded, if the stream needed to be rewound. + * + * Subclasses should do any set up needed after a rewind. + */ + virtual bool onRewind() { + return true; + } + + /** + * Get method for the input stream + */ + SkStream* stream() { + return fStream.get(); + } + + /** + * The remaining functions revolve around decoding scanlines. + */ + + /** + * Most images types will be kTopDown and will not need to override this function. + */ + virtual SkScanlineOrder onGetScanlineOrder() const { return kTopDown_SkScanlineOrder; } + + const SkImageInfo& dstInfo() const { return fDstInfo; } + + const Options& options() const { return fOptions; } + + /** + * Returns the number of scanlines that have been decoded so far. + * This is unaffected by the SkScanlineOrder. + * + * Returns -1 if we have not started a scanline decode. + */ + int currScanline() const { return fCurrScanline; } + + virtual int onOutputScanline(int inputScanline) const; + + /** + * Return whether we can convert to dst. + * + * Will be called for the appropriate frame, prior to initializing the colorXform. + */ + virtual bool conversionSupported(const SkImageInfo& dst, bool srcIsOpaque, + bool needsColorXform); + + // Some classes never need a colorXform e.g. + // - ICO uses its embedded codec's colorXform + // - WBMP is just Black/White + virtual bool usesColorXform() const { return true; } + void applyColorXform(void* dst, const void* src, int count) const; + + bool colorXform() const { return fXformTime != kNo_XformTime; } + bool xformOnDecode() const { return fXformTime == kDecodeRow_XformTime; } + + virtual int onGetFrameCount() { + return 1; + } + + virtual bool onGetFrameInfo(int, FrameInfo*) const { + return false; + } + + virtual int onGetRepetitionCount() { + return 0; + } + +private: + const SkEncodedInfo fEncodedInfo; + XformFormat fSrcXformFormat; + std::unique_ptr fStream; + bool fNeedsRewind = false; + const SkEncodedOrigin fOrigin; + + SkImageInfo fDstInfo; + Options fOptions; + + enum XformTime { + kNo_XformTime, + kPalette_XformTime, + kDecodeRow_XformTime, + }; + XformTime fXformTime; + XformFormat fDstXformFormat; // Based on fDstInfo. + skcms_ICCProfile fDstProfile; + skcms_AlphaFormat fDstXformAlphaFormat; + + // Only meaningful during scanline decodes. + int fCurrScanline = -1; + + bool fStartedIncrementalDecode = false; + + // Allows SkAndroidCodec to call handleFrameIndex (potentially decoding a prior frame and + // clearing to transparent) without SkCodec itself calling it, too. + bool fUsingCallbackForHandleFrameIndex = false; + + bool initializeColorXform(const SkImageInfo& dstInfo, SkEncodedInfo::Alpha, bool srcIsOpaque); + + /** + * Return whether these dimensions are supported as a scale. + * + * The codec may choose to cache the information about scale and subset. + * Either way, the same information will be passed to onGetPixels/onStart + * on success. + * + * This must return true for a size returned from getScaledDimensions. + */ + bool dimensionsSupported(const SkISize& dim) { + return dim == this->dimensions() || this->onDimensionsSupported(dim); + } + + /** + * For multi-framed images, return the object with information about the frames. + */ + virtual const SkFrameHolder* getFrameHolder() const { + return nullptr; + } + + // Callback for decoding a prior frame. The `Options::fFrameIndex` is ignored, + // being replaced by frameIndex. This allows opts to actually be a subclass of + // SkCodec::Options which SkCodec itself does not know how to copy or modify, + // but just passes through to the caller (where it can be reinterpret_cast'd). + using GetPixelsCallback = std::function; + + /** + * Check for a valid Options.fFrameIndex, and decode prior frames if necessary. + * + * If GetPixelsCallback is not null, it will be used to decode a prior frame instead + * of using this SkCodec directly. It may also be used recursively, if that in turn + * depends on a prior frame. This is used by SkAndroidCodec. + */ + Result handleFrameIndex(const SkImageInfo&, void* pixels, size_t rowBytes, const Options&, + GetPixelsCallback = nullptr); + + // Methods for scanline decoding. + virtual Result onStartScanlineDecode(const SkImageInfo& /*dstInfo*/, + const Options& /*options*/) { + return kUnimplemented; + } + + virtual Result onStartIncrementalDecode(const SkImageInfo& /*dstInfo*/, void*, size_t, + const Options&) { + return kUnimplemented; + } + + virtual Result onIncrementalDecode(int*) { + return kUnimplemented; + } + + + virtual bool onSkipScanlines(int /*countLines*/) { return false; } + + virtual int onGetScanlines(void* /*dst*/, int /*countLines*/, size_t /*rowBytes*/) { return 0; } + + /** + * On an incomplete decode, getPixels() and getScanlines() will call this function + * to fill any uinitialized memory. + * + * @param dstInfo Contains the destination color type + * Contains the destination alpha type + * Contains the destination width + * The height stored in this info is unused + * @param dst Pointer to the start of destination pixel memory + * @param rowBytes Stride length in destination pixel memory + * @param zeroInit Indicates if memory is zero initialized + * @param linesRequested Number of lines that the client requested + * @param linesDecoded Number of lines that were successfully decoded + */ + void fillIncompleteImage(const SkImageInfo& dstInfo, void* dst, size_t rowBytes, + ZeroInitialized zeroInit, int linesRequested, int linesDecoded); + + /** + * Return an object which will allow forcing scanline decodes to sample in X. + * + * May create a sampler, if one is not currently being used. Otherwise, does + * not affect ownership. + * + * Only valid during scanline decoding or incremental decoding. + */ + virtual SkSampler* getSampler(bool /*createIfNecessary*/) { return nullptr; } + + friend class DM::CodecSrc; // for fillIncompleteImage + friend class PNGCodecGM; // for fillIncompleteImage + friend class SkSampledCodec; + friend class SkIcoCodec; + friend class SkAndroidCodec; // for fEncodedInfo + friend class SkPDFBitmap; // for fEncodedInfo +}; + +namespace SkCodecs { + +using DecodeContext = void*; +using IsFormatCallback = bool (*)(const void* data, size_t len); +using MakeFromStreamCallback = std::unique_ptr (*)(std::unique_ptr, + SkCodec::Result*, + DecodeContext); + +struct SK_API Decoder { + // By convention, we use all lowercase letters and go with the primary filename extension. + // For example "png", "jpg", "ico", "webp", etc + std::string_view id; + IsFormatCallback isFormat; + MakeFromStreamCallback makeFromStream; +}; + +// Add the decoder to the end of a linked list of decoders, which will be used to identify calls to +// SkCodec::MakeFromStream. If a decoder with the same id already exists, this new decoder +// will replace the existing one (in the same position). This is not thread-safe, so make sure all +// initialization is done before the first call. +void SK_API Register(Decoder d); + +/** + * Return a SkImage produced by the codec, but attempts to defer image allocation until the + * image is actually used/drawn. This deferral allows the system to cache the result, either on the + * CPU or on the GPU, depending on where the image is drawn. If memory is low, the cache may + * be purged, causing the next draw of the image to have to re-decode. + * + * If alphaType is nullopt, the image's alpha type will be chosen automatically based on the + * image format. Transparent images will default to kPremul_SkAlphaType. If alphaType contains + * kPremul_SkAlphaType or kUnpremul_SkAlphaType, that alpha type will be used. Forcing opaque + * (passing kOpaque_SkAlphaType) is not allowed, and will return nullptr. + * + * @param codec A non-null codec (e.g. from SkPngDecoder::Decode) + * @return created SkImage, or nullptr + */ +SK_API sk_sp DeferredImage(std::unique_ptr codec, + std::optional alphaType = std::nullopt); +} + +#endif // SkCodec_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkCodecAnimation.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkCodecAnimation.h new file mode 100644 index 0000000000..c5883e2af2 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkCodecAnimation.h @@ -0,0 +1,61 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkCodecAnimation_DEFINED +#define SkCodecAnimation_DEFINED + +namespace SkCodecAnimation { + /** + * This specifies how the next frame is based on this frame. + * + * Names are based on the GIF 89a spec. + * + * The numbers correspond to values in a GIF. + */ + enum class DisposalMethod { + /** + * The next frame should be drawn on top of this one. + * + * In a GIF, a value of 0 (not specified) is also treated as Keep. + */ + kKeep = 1, + + /** + * Similar to Keep, except the area inside this frame's rectangle + * should be cleared to the BackGround color (transparent) before + * drawing the next frame. + */ + kRestoreBGColor = 2, + + /** + * The next frame should be drawn on top of the previous frame - i.e. + * disregarding this one. + * + * In a GIF, a value of 4 is also treated as RestorePrevious. + */ + kRestorePrevious = 3, + }; + + /** + * How to blend the current frame. + */ + enum class Blend { + /** + * Blend with the prior frame as if using SkBlendMode::kSrcOver. + */ + kSrcOver, + + /** + * Blend with the prior frame as if using SkBlendMode::kSrc. + * + * This frame's pixels replace the destination pixels. + */ + kSrc, + }; + +} // namespace SkCodecAnimation +#endif // SkCodecAnimation_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkEncodedImageFormat.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkEncodedImageFormat.h new file mode 100644 index 0000000000..e664c7db02 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkEncodedImageFormat.h @@ -0,0 +1,33 @@ +/* + * 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 SkEncodedImageFormat_DEFINED +#define SkEncodedImageFormat_DEFINED + +#include + +/** + * Enum describing format of encoded data. + */ +enum class SkEncodedImageFormat { + kBMP, + kGIF, + kICO, + kJPEG, + kPNG, + kWBMP, + kWEBP, + kPKM, + kKTX, + kASTC, + kDNG, + kHEIF, + kAVIF, + kJPEGXL, +}; + +#endif // SkEncodedImageFormat_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkEncodedOrigin.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkEncodedOrigin.h new file mode 100644 index 0000000000..19d083672f --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkEncodedOrigin.h @@ -0,0 +1,54 @@ +/* + * 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 SkEncodedOrigin_DEFINED +#define SkEncodedOrigin_DEFINED + +#include "include/core/SkMatrix.h" + +// These values match the orientation www.exif.org/Exif2-2.PDF. +enum SkEncodedOrigin { + kTopLeft_SkEncodedOrigin = 1, // Default + kTopRight_SkEncodedOrigin = 2, // Reflected across y-axis + kBottomRight_SkEncodedOrigin = 3, // Rotated 180 + kBottomLeft_SkEncodedOrigin = 4, // Reflected across x-axis + kLeftTop_SkEncodedOrigin = 5, // Reflected across x-axis, Rotated 90 CCW + kRightTop_SkEncodedOrigin = 6, // Rotated 90 CW + kRightBottom_SkEncodedOrigin = 7, // Reflected across x-axis, Rotated 90 CW + kLeftBottom_SkEncodedOrigin = 8, // Rotated 90 CCW + kDefault_SkEncodedOrigin = kTopLeft_SkEncodedOrigin, + kLast_SkEncodedOrigin = kLeftBottom_SkEncodedOrigin, +}; + +/** + * Given an encoded origin and the width and height of the source data, returns a matrix + * that transforms the source rectangle with upper left corner at [0, 0] and origin to a correctly + * oriented destination rectangle of [0, 0, w, h]. + */ +static inline SkMatrix SkEncodedOriginToMatrix(SkEncodedOrigin origin, int w, int h) { + switch (origin) { + case kTopLeft_SkEncodedOrigin: return SkMatrix::I(); + case kTopRight_SkEncodedOrigin: return SkMatrix::MakeAll(-1, 0, w, 0, 1, 0, 0, 0, 1); + case kBottomRight_SkEncodedOrigin: return SkMatrix::MakeAll(-1, 0, w, 0, -1, h, 0, 0, 1); + case kBottomLeft_SkEncodedOrigin: return SkMatrix::MakeAll( 1, 0, 0, 0, -1, h, 0, 0, 1); + case kLeftTop_SkEncodedOrigin: return SkMatrix::MakeAll( 0, 1, 0, 1, 0, 0, 0, 0, 1); + case kRightTop_SkEncodedOrigin: return SkMatrix::MakeAll( 0, -1, w, 1, 0, 0, 0, 0, 1); + case kRightBottom_SkEncodedOrigin: return SkMatrix::MakeAll( 0, -1, w, -1, 0, h, 0, 0, 1); + case kLeftBottom_SkEncodedOrigin: return SkMatrix::MakeAll( 0, 1, 0, -1, 0, h, 0, 0, 1); + } + SK_ABORT("Unexpected origin"); +} + +/** + * Return true if the encoded origin includes a 90 degree rotation, in which case the width + * and height of the source data are swapped relative to a correctly oriented destination. + */ +static inline bool SkEncodedOriginSwapsWidthHeight(SkEncodedOrigin origin) { + return origin >= kLeftTop_SkEncodedOrigin; +} + +#endif // SkEncodedOrigin_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkGifDecoder.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkGifDecoder.h new file mode 100644 index 0000000000..7344b26647 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkGifDecoder.h @@ -0,0 +1,44 @@ +/* + * Copyright 2023 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SkGifDecoder_DEFINED +#define SkGifDecoder_DEFINED + +#include "include/codec/SkCodec.h" +#include "include/core/SkRefCnt.h" +#include "include/private/base/SkAPI.h" + +class SkData; +class SkStream; + +#include + +namespace SkGifDecoder { + +/** Returns true if this data claims to be a GIF image. */ +SK_API bool IsGif(const void*, size_t); + +/** + * Attempts to decode the given bytes as a GIF. + * + * If the bytes are not a GIF, returns nullptr. + * + * DecodeContext is ignored + */ +SK_API std::unique_ptr Decode(std::unique_ptr, + SkCodec::Result*, + SkCodecs::DecodeContext = nullptr); +SK_API std::unique_ptr Decode(sk_sp, + SkCodec::Result*, + SkCodecs::DecodeContext = nullptr); + +inline constexpr SkCodecs::Decoder Decoder() { + return { "gif", IsGif, Decode }; +} + +} // namespace SkGifDecoder + +#endif // SkGifDecoder_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkIcoDecoder.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkIcoDecoder.h new file mode 100644 index 0000000000..e0d361d685 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkIcoDecoder.h @@ -0,0 +1,44 @@ +/* + * Copyright 2023 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SkIcoDecoder_DEFINED +#define SkIcoDecoder_DEFINED + +#include "include/codec/SkCodec.h" +#include "include/core/SkRefCnt.h" +#include "include/private/base/SkAPI.h" + +class SkData; +class SkStream; + +#include + +namespace SkIcoDecoder { + +/** Returns true if this data claims to be a ICO image. */ +SK_API bool IsIco(const void*, size_t); + +/** + * Attempts to decode the given bytes as a ICO. + * + * If the bytes are not a ICO, returns nullptr. + * + * DecodeContext is ignored + */ +SK_API std::unique_ptr Decode(std::unique_ptr, + SkCodec::Result*, + SkCodecs::DecodeContext = nullptr); +SK_API std::unique_ptr Decode(sk_sp, + SkCodec::Result*, + SkCodecs::DecodeContext = nullptr); + +inline constexpr SkCodecs::Decoder Decoder() { + return { "ico", IsIco, Decode }; +} + +} // namespace SkIcoDecoder + +#endif // SkIcoDecoder_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkJpegDecoder.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkJpegDecoder.h new file mode 100644 index 0000000000..10a340f5bf --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkJpegDecoder.h @@ -0,0 +1,44 @@ +/* + * Copyright 2023 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SkJpegDecoder_DEFINED +#define SkJpegDecoder_DEFINED + +#include "include/codec/SkCodec.h" +#include "include/core/SkRefCnt.h" +#include "include/private/base/SkAPI.h" + +class SkData; +class SkStream; + +#include + +namespace SkJpegDecoder { + +/** Returns true if this data claims to be a JPEG image. */ +SK_API bool IsJpeg(const void*, size_t); + +/** + * Attempts to decode the given bytes as a JPEG. + * + * If the bytes are not a JPEG, returns nullptr. + * + * DecodeContext is ignored + */ +SK_API std::unique_ptr Decode(std::unique_ptr, + SkCodec::Result*, + SkCodecs::DecodeContext = nullptr); +SK_API std::unique_ptr Decode(sk_sp, + SkCodec::Result*, + SkCodecs::DecodeContext = nullptr); + +inline constexpr SkCodecs::Decoder Decoder() { + return { "jpeg", IsJpeg, Decode }; +} + +} // namespace SkJpegDecoder + +#endif // SkJpegDecoder_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkJpegxlDecoder.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkJpegxlDecoder.h new file mode 100644 index 0000000000..4ab73f8270 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkJpegxlDecoder.h @@ -0,0 +1,44 @@ +/* + * Copyright 2023 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SkJpegxlDecoder_DEFINED +#define SkJpegxlDecoder_DEFINED + +#include "include/codec/SkCodec.h" +#include "include/core/SkRefCnt.h" +#include "include/private/base/SkAPI.h" + +class SkData; +class SkStream; + +#include + +namespace SkJpegxlDecoder { + +/** Returns true if this data claims to be a JPEGXL image. */ +SK_API bool IsJpegxl(const void*, size_t); + +/** + * Attempts to decode the given bytes as a JPEGXL. + * + * If the bytes are not a JPEGXL, returns nullptr. + * + * DecodeContext is ignored + */ +SK_API std::unique_ptr Decode(std::unique_ptr, + SkCodec::Result*, + SkCodecs::DecodeContext = nullptr); +SK_API std::unique_ptr Decode(sk_sp, + SkCodec::Result*, + SkCodecs::DecodeContext = nullptr); + +inline constexpr SkCodecs::Decoder Decoder() { + return { "jpegxl", IsJpegxl, Decode }; +} + +} // namespace SkJpegxlDecoder + +#endif // SkJpegxlDecoder_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkPixmapUtils.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkPixmapUtils.h new file mode 100644 index 0000000000..0df4a36f0c --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkPixmapUtils.h @@ -0,0 +1,31 @@ +/* + * Copyright 2023 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkPixmapUtils_DEFINED +#define SkPixmapUtils_DEFINED + +#include "include/codec/SkEncodedOrigin.h" +#include "include/core/SkImageInfo.h" +#include "include/private/base/SkAPI.h" + +class SkPixmap; + +namespace SkPixmapUtils { +/** + * Copy the pixels in src into dst, applying the orientation transformations specified + * by origin. If the inputs are invalid, this returns false and no copy is made. + */ +SK_API bool Orient(const SkPixmap& dst, const SkPixmap& src, SkEncodedOrigin origin); + +/** + * Return a copy of the provided ImageInfo with the width and height swapped. + */ +SK_API SkImageInfo SwapWidthHeight(const SkImageInfo& info); + +} // namespace SkPixmapUtils + +#endif // SkPixmapUtils_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkPngChunkReader.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkPngChunkReader.h new file mode 100644 index 0000000000..0ee8a9ecc7 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkPngChunkReader.h @@ -0,0 +1,45 @@ +/* + * 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 SkPngChunkReader_DEFINED +#define SkPngChunkReader_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkTypes.h" + +/** + * SkPngChunkReader + * + * Base class for optional callbacks to retrieve meta/chunk data out of a PNG + * encoded image as it is being decoded. + * Used by SkCodec. + */ +class SkPngChunkReader : public SkRefCnt { +public: + /** + * This will be called by the decoder when it sees an unknown chunk. + * + * Use by SkCodec: + * Depending on the location of the unknown chunks, this callback may be + * called by + * - the factory (NewFromStream/NewFromData) + * - getPixels + * - startScanlineDecode + * - the first call to getScanlines/skipScanlines + * The callback may be called from a different thread (e.g. if the SkCodec + * is passed to another thread), and it may be called multiple times, if + * the SkCodec is used multiple times. + * + * @param tag Name for this type of chunk. + * @param data Data to be interpreted by the subclass. + * @param length Number of bytes of data in the chunk. + * @return true to continue decoding, or false to indicate an error, which + * will cause the decoder to not return the image. + */ + virtual bool readChunk(const char tag[], const void* data, size_t length) = 0; +}; +#endif // SkPngChunkReader_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkPngDecoder.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkPngDecoder.h new file mode 100644 index 0000000000..e761d2e216 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkPngDecoder.h @@ -0,0 +1,44 @@ +/* + * Copyright 2023 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SkPngDecoder_DEFINED +#define SkPngDecoder_DEFINED + +#include "include/codec/SkCodec.h" +#include "include/core/SkRefCnt.h" +#include "include/private/base/SkAPI.h" + +class SkData; +class SkStream; + +#include + +namespace SkPngDecoder { + +/** Returns true if this data claims to be a PNG image. */ +SK_API bool IsPng(const void*, size_t); + +/** + * Attempts to decode the given bytes as a PNG. + * + * If the bytes are not a PNG, returns nullptr. + * + * DecodeContext, if non-null, is expected to be a SkPngChunkReader* + */ +SK_API std::unique_ptr Decode(std::unique_ptr, + SkCodec::Result*, + SkCodecs::DecodeContext = nullptr); +SK_API std::unique_ptr Decode(sk_sp, + SkCodec::Result*, + SkCodecs::DecodeContext = nullptr); + +inline constexpr SkCodecs::Decoder Decoder() { + return { "png", IsPng, Decode }; +} + +} // namespace SkPngDecoder + +#endif // SkPngDecoder_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkRawDecoder.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkRawDecoder.h new file mode 100644 index 0000000000..3f56012212 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkRawDecoder.h @@ -0,0 +1,50 @@ +/* + * Copyright 2023 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SkRawDecoder_DEFINED +#define SkRawDecoder_DEFINED + +#include "include/codec/SkCodec.h" +#include "include/core/SkRefCnt.h" +#include "include/private/base/SkAPI.h" + +class SkData; +class SkStream; + +#include + +namespace SkRawDecoder { + +inline bool IsRaw(const void*, size_t) { + // Raw formats are tricky to detect just by reading in the first several bytes. + // For example, PIEX might need to read 10k bytes to detect Sony's arw format + // https://github.com/google/piex/blob/f1e15dd837c04347504149f71db67a78fbeddc73/src/image_type_recognition/image_type_recognition_lite.cc#L152 + // Thus, we just assume everything might be a RAW file and check it last. + return true; +} + +/** + * Attempts to decode the given bytes as a raw image. + * + * If the bytes are not a raw, returns nullptr. + * + * DecodeContext is ignored + */ +SK_API std::unique_ptr Decode(std::unique_ptr, + SkCodec::Result*, + SkCodecs::DecodeContext = nullptr); +SK_API std::unique_ptr Decode(sk_sp, + SkCodec::Result*, + SkCodecs::DecodeContext = nullptr); + +// This decoder will always be checked last, no matter when it is registered. +inline constexpr SkCodecs::Decoder Decoder() { + return { "raw", IsRaw, Decode }; +} + +} // namespace SkRawDecoder + +#endif // SkRawDecoder_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkWbmpDecoder.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkWbmpDecoder.h new file mode 100644 index 0000000000..7e5e7706df --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkWbmpDecoder.h @@ -0,0 +1,44 @@ +/* + * Copyright 2023 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SkWbmpDecoder_DEFINED +#define SkWbmpDecoder_DEFINED + +#include "include/codec/SkCodec.h" +#include "include/core/SkRefCnt.h" +#include "include/private/base/SkAPI.h" + +class SkData; +class SkStream; + +#include + +namespace SkWbmpDecoder { + +/** Returns true if this data claims to be a WBMP image. */ +SK_API bool IsWbmp(const void*, size_t); + +/** + * Attempts to decode the given bytes as a WBMP. + * + * If the bytes are not a WBMP, returns nullptr. + * + * DecodeContext is ignored + */ +SK_API std::unique_ptr Decode(std::unique_ptr, + SkCodec::Result*, + SkCodecs::DecodeContext = nullptr); +SK_API std::unique_ptr Decode(sk_sp, + SkCodec::Result*, + SkCodecs::DecodeContext = nullptr); + +inline constexpr SkCodecs::Decoder Decoder() { + return { "wbmp", IsWbmp, Decode }; +} + +} // namespace SkWbmpDecoder + +#endif // SkWbmpDecoder_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkWebpDecoder.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkWebpDecoder.h new file mode 100644 index 0000000000..5f8032f0fe --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/codec/SkWebpDecoder.h @@ -0,0 +1,44 @@ +/* + * Copyright 2023 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SkWebpDecoder_DEFINED +#define SkWebpDecoder_DEFINED + +#include "include/codec/SkCodec.h" +#include "include/core/SkRefCnt.h" +#include "include/private/base/SkAPI.h" + +class SkData; +class SkStream; + +#include + +namespace SkWebpDecoder { + +/** Returns true if this data claims to be a WEBP image. */ +SK_API bool IsWebp(const void*, size_t); + +/** + * Attempts to decode the given bytes as a WEBP. + * + * If the bytes are not a WEBP, returns nullptr. + * + * DecodeContext is ignored + */ +SK_API std::unique_ptr Decode(std::unique_ptr, + SkCodec::Result*, + SkCodecs::DecodeContext = nullptr); +SK_API std::unique_ptr Decode(sk_sp, + SkCodec::Result*, + SkCodecs::DecodeContext = nullptr); + +inline constexpr SkCodecs::Decoder Decoder() { + return { "webp", IsWebp, Decode }; +} + +} // namespace SkWebpDecoder + +#endif // SkWebpDecoder_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/config/OWNERS b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/config/OWNERS new file mode 100644 index 0000000000..25b714bb27 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/config/OWNERS @@ -0,0 +1,2 @@ +bungeman@google.com +kjlubick@google.com \ No newline at end of file diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/config/SkUserConfig.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/config/SkUserConfig.h new file mode 100644 index 0000000000..8ac155b481 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/config/SkUserConfig.h @@ -0,0 +1,121 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkUserConfig_DEFINED +#define SkUserConfig_DEFINED + +/* SkTypes.h, the root of the public header files, includes this file + SkUserConfig.h after first initializing certain Skia defines, letting + this file change or augment those flags. + + Below are optional defines that add, subtract, or change default behavior + in Skia. Your port can locally edit this file to enable/disable flags as + you choose, or these can be declared on your command line (i.e. -Dfoo). + + By default, this #include file will always default to having all the flags + commented out, so including it will have no effect. +*/ + +/////////////////////////////////////////////////////////////////////////////// + +/* Skia has lots of debug-only code. Often this is just null checks or other + parameter checking, but sometimes it can be quite intrusive (e.g. check that + each 32bit pixel is in premultiplied form). This code can be very useful + during development, but will slow things down in a shipping product. + + By default, these mutually exclusive flags are defined in SkTypes.h, + based on the presence or absence of NDEBUG, but that decision can be changed + here. +*/ +//#define SK_DEBUG +//#define SK_RELEASE + +/* To write debug messages to a console, skia will call SkDebugf(...) following + printf conventions (e.g. const char* format, ...). If you want to redirect + this to something other than printf, define yours here +*/ +//#define SkDebugf(...) MyFunction(__VA_ARGS__) + +/* Skia has both debug and release asserts. When an assert fails SK_ABORT will + be used to report an abort message. SK_ABORT is expected not to return. Skia + provides a default implementation which will print the message with SkDebugf + and then call sk_abort_no_print. +*/ +//#define SK_ABORT(message, ...) + +/* To specify a different default font strike cache memory limit, define this. If this is + undefined, skia will use a built-in value. +*/ +//#define SK_DEFAULT_FONT_CACHE_LIMIT (1024 * 1024) + +/* To specify a different default font strike cache count limit, define this. If this is + undefined, skia will use a built-in value. +*/ +// #define SK_DEFAULT_FONT_CACHE_COUNT_LIMIT 2048 + +/* To specify the default size of the image cache, undefine this and set it to + the desired value (in bytes). SkGraphics.h as a runtime API to set this + value as well. If this is undefined, a built-in value will be used. +*/ +//#define SK_DEFAULT_IMAGE_CACHE_LIMIT (1024 * 1024) + +/* Define this to set the upper limit for text to support LCD. Values that + are very large increase the cost in the font cache and draw slower, without + improving readability. If this is undefined, Skia will use its default + value (e.g. 48) +*/ +//#define SK_MAX_SIZE_FOR_LCDTEXT 48 + +/* Change the kN32_SkColorType ordering to BGRA to work in X windows. +*/ +//#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. +*/ +//#define SK_GANESH + +/* Skia makes use of histogram logging macros to trace the frequency of + events. By default, Skia provides no-op versions of these macros. + Skia consumers can provide their own definitions of these macros to + integrate with their histogram collection backend. +*/ +//#define SK_HISTOGRAM_BOOLEAN(name, sample) +//#define SK_HISTOGRAM_ENUMERATION(name, sample, enum_size) +//#define SK_HISTOGRAM_EXACT_LINEAR(name, sample, value_max) +//#define SK_HISTOGRAM_MEMORY_KB(name, sample) + +// To use smaller but slower mipmap builder +//#define SK_USE_DRAWING_MIPMAP_DOWNSAMPLER + +/* Skia tries to make use of some non-standard C++ language extensions. + By default, Skia provides msvc and clang/gcc versions of these macros. + Skia consumers can provide their own definitions of these macros to + integrate with their own compilers and build system. +*/ +//#define SK_ALWAYS_INLINE inline __attribute__((always_inline)) +//#define SK_NEVER_INLINE __attribute__((noinline)) +//#define SK_PRINTF_LIKE(A, B) __attribute__((format(printf, (A), (B)))) +//#define SK_NO_SANITIZE(A) __attribute__((no_sanitize(A))) +//#define SK_TRIVIAL_ABI [[clang::trivial_abi]] + +/* + * If compiling Skia as a DLL, public APIs should be exported. Skia will set + * SK_API to something sensible for Clang and MSVC, but if clients need to + * customize it for their build system or compiler, they may. + * If a client needs to use SK_API (e.g. overriding SK_ABORT), then they + * *must* define their own, the default will not be defined prior to loading + * this file. + */ +//#define SK_API __declspec(dllexport) + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkAlphaType.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkAlphaType.h new file mode 100644 index 0000000000..0c99906dfd --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkAlphaType.h @@ -0,0 +1,45 @@ +/* + * 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 SkAlphaType_DEFINED +#define SkAlphaType_DEFINED + +/** \enum SkAlphaType + Describes how to interpret the alpha component of a pixel. A pixel may + be opaque, or alpha, describing multiple levels of transparency. + + In simple blending, alpha weights the draw color and the destination + color to create a new color. If alpha describes a weight from zero to one: + + new color = draw color * alpha + destination color * (1 - alpha) + + In practice alpha is encoded in two or more bits, where 1.0 equals all bits set. + + RGB may have alpha included in each component value; the stored + value is the original RGB multiplied by alpha. Premultiplied color + components improve performance. +*/ +enum SkAlphaType : int { + kUnknown_SkAlphaType, //!< uninitialized + kOpaque_SkAlphaType, //!< pixel is opaque + kPremul_SkAlphaType, //!< pixel components are premultiplied by alpha + kUnpremul_SkAlphaType, //!< pixel components are independent of alpha + kLastEnum_SkAlphaType = kUnpremul_SkAlphaType, //!< last valid value +}; + +/** Returns true if SkAlphaType equals kOpaque_SkAlphaType. + + kOpaque_SkAlphaType is a hint that the SkColorType is opaque, or that all + alpha values are set to their 1.0 equivalent. If SkAlphaType is + kOpaque_SkAlphaType, and SkColorType is not opaque, then the result of + drawing any pixel with a alpha value less than 1.0 is undefined. +*/ +static inline bool SkAlphaTypeIsOpaque(SkAlphaType at) { + return kOpaque_SkAlphaType == at; +} + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkAnnotation.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkAnnotation.h new file mode 100644 index 0000000000..2006f309e9 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkAnnotation.h @@ -0,0 +1,52 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkAnnotation_DEFINED +#define SkAnnotation_DEFINED + +#include "include/core/SkTypes.h" + +class SkData; +struct SkPoint; +struct SkRect; +class SkCanvas; + +/** + * Annotate the canvas by associating the specified URL with the + * specified rectangle (in local coordinates, just like drawRect). + * + * The URL is expected to be escaped and be valid 7-bit ASCII. + * + * If the backend of this canvas does not support annotations, this call is + * safely ignored. + * + * The caller is responsible for managing its ownership of the SkData. + */ +SK_API void SkAnnotateRectWithURL(SkCanvas*, const SkRect&, SkData*); + +/** + * Annotate the canvas by associating a name with the specified point. + * + * If the backend of this canvas does not support annotations, this call is + * safely ignored. + * + * The caller is responsible for managing its ownership of the SkData. + */ +SK_API void SkAnnotateNamedDestination(SkCanvas*, const SkPoint&, SkData*); + +/** + * Annotate the canvas by making the specified rectangle link to a named + * destination. + * + * If the backend of this canvas does not support annotations, this call is + * safely ignored. + * + * The caller is responsible for managing its ownership of the SkData. + */ +SK_API void SkAnnotateLinkToDestination(SkCanvas*, const SkRect&, SkData*); + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkArc.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkArc.h new file mode 100644 index 0000000000..9e530ebd2b --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkArc.h @@ -0,0 +1,69 @@ +/* + * 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 SkArc_DEFINED +#define SkArc_DEFINED + +#include "include/core/SkRect.h" +#include "include/core/SkScalar.h" + +// Represents an arc along an oval boundary, or a closed wedge of the oval. +struct SkArc { + enum class Type : bool { + kArc, // An arc along the perimeter of the oval + kWedge // A closed wedge that includes the oval's center + }; + + SkArc() = default; + SkArc(const SkArc& arc) = default; + SkArc& operator=(const SkArc& arc) = default; + + const SkRect& oval() const { return fOval; } + SkScalar startAngle() const { return fStartAngle; } + SkScalar sweepAngle() const { return fSweepAngle; } + bool isWedge() const { return fType == Type::kWedge; } + + friend bool operator==(const SkArc& a, const SkArc& b) { + return a.fOval == b.fOval && a.fStartAngle == b.fStartAngle && + a.fSweepAngle == b.fSweepAngle && a.fType == b.fType; + } + + friend bool operator!=(const SkArc& a, const SkArc& b) { return !(a == b); } + + // Preferred factory that explicitly states which type of arc + static SkArc Make(const SkRect& oval, + SkScalar startAngleDegrees, + SkScalar sweepAngleDegrees, + Type type) { + return SkArc(oval, startAngleDegrees, sweepAngleDegrees, type); + } + + // Deprecated factory to assist with legacy code based on `useCenter` + static SkArc Make(const SkRect& oval, + SkScalar startAngleDegrees, + SkScalar sweepAngleDegrees, + bool useCenter) { + return SkArc( + oval, startAngleDegrees, sweepAngleDegrees, useCenter ? Type::kWedge : Type::kArc); + } + + // Bounds of oval containing the arc. + SkRect fOval = SkRect::MakeEmpty(); + + // Angle in degrees where the arc begins. Zero means horizontally to the right. + SkScalar fStartAngle = 0; + // Sweep angle in degrees; positive is clockwise. + SkScalar fSweepAngle = 0; + + Type fType = Type::kArc; + +private: + SkArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, Type type) + : fOval(oval), fStartAngle(startAngle), fSweepAngle(sweepAngle), fType(type) {} +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkBBHFactory.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkBBHFactory.h new file mode 100644 index 0000000000..5d9f9009ac --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkBBHFactory.h @@ -0,0 +1,67 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkBBHFactory_DEFINED +#define SkBBHFactory_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkTypes.h" + +// TODO(kjlubick) fix client users and then make this a forward declare +#include "include/core/SkRect.h" // IWYU pragma: keep + +#include +#include + +class SkBBoxHierarchy : public SkRefCnt { +public: + struct Metadata { + bool isDraw; // The corresponding SkRect bounds a draw command, not a pure state change. + }; + + /** + * Insert N bounding boxes into the hierarchy. + */ + virtual void insert(const SkRect[], int N) = 0; + virtual void insert(const SkRect[], const Metadata[], int N); + + /** + * Populate results with the indices of bounding boxes intersecting that query. + */ + virtual void search(const SkRect& query, std::vector* results) const = 0; + + /** + * Return approximate size in memory of *this. + */ + virtual size_t bytesUsed() const = 0; + +protected: + SkBBoxHierarchy() = default; + SkBBoxHierarchy(const SkBBoxHierarchy&) = delete; + SkBBoxHierarchy& operator=(const SkBBoxHierarchy&) = delete; +}; + +class SK_API SkBBHFactory { +public: + /** + * Allocate a new SkBBoxHierarchy. Return NULL on failure. + */ + virtual sk_sp operator()() const = 0; + virtual ~SkBBHFactory() {} + +protected: + SkBBHFactory() = default; + SkBBHFactory(const SkBBHFactory&) = delete; + SkBBHFactory& operator=(const SkBBHFactory&) = delete; +}; + +class SK_API SkRTreeFactory : public SkBBHFactory { +public: + sk_sp operator()() const override; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkBitmap.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkBitmap.h new file mode 100644 index 0000000000..1875c5f37e --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkBitmap.h @@ -0,0 +1,1275 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkBitmap_DEFINED +#define SkBitmap_DEFINED + +#include "include/core/SkAlphaType.h" +#include "include/core/SkColor.h" +#include "include/core/SkImageInfo.h" +#include "include/core/SkPixmap.h" +#include "include/core/SkPoint.h" +#include "include/core/SkRect.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkSamplingOptions.h" +#include "include/core/SkSize.h" +#include "include/core/SkTypes.h" +#include "include/private/base/SkCPUTypes.h" +#include "include/private/base/SkDebug.h" + +#include +#include + +class SkColorSpace; +class SkImage; +class SkMatrix; +class SkMipmap; +class SkPaint; +class SkPixelRef; +class SkShader; +enum SkColorType : int; +enum class SkTileMode; +struct SkMaskBuilder; + +/** \class SkBitmap + SkBitmap describes a two-dimensional raster pixel array. SkBitmap is built on + SkImageInfo, containing integer width and height, SkColorType and SkAlphaType + describing the pixel format, and SkColorSpace describing the range of colors. + SkBitmap points to SkPixelRef, which describes the physical array of pixels. + SkImageInfo bounds may be located anywhere fully inside SkPixelRef bounds. + + SkBitmap can be drawn using SkCanvas. SkBitmap can be a drawing destination for SkCanvas + draw member functions. SkBitmap flexibility as a pixel container limits some + optimizations available to the target platform. + + If pixel array is primarily read-only, use SkImage for better performance. + If pixel array is primarily written to, use SkSurface for better performance. + + Declaring SkBitmap const prevents altering SkImageInfo: the SkBitmap height, width, + and so on cannot change. It does not affect SkPixelRef: a caller may write its + pixels. Declaring SkBitmap const affects SkBitmap configuration, not its contents. + + SkBitmap is not thread safe. Each thread must have its own copy of SkBitmap fields, + although threads may share the underlying pixel array. +*/ +class SK_API SkBitmap { +public: + class SK_API Allocator; + + /** Creates an empty SkBitmap without pixels, with kUnknown_SkColorType, + kUnknown_SkAlphaType, and with a width and height of zero. SkPixelRef origin is + set to (0, 0). + + Use setInfo() to associate SkColorType, SkAlphaType, width, and height + after SkBitmap has been created. + + @return empty SkBitmap + + example: https://fiddle.skia.org/c/@Bitmap_empty_constructor + */ + SkBitmap(); + + /** Copies settings from src to returned SkBitmap. Shares pixels if src has pixels + allocated, so both bitmaps reference the same pixels. + + @param src SkBitmap to copy SkImageInfo, and share SkPixelRef + @return copy of src + + example: https://fiddle.skia.org/c/@Bitmap_copy_const_SkBitmap + */ + SkBitmap(const SkBitmap& src); + + /** Copies settings from src to returned SkBitmap. Moves ownership of src pixels to + SkBitmap. + + @param src SkBitmap to copy SkImageInfo, and reassign SkPixelRef + @return copy of src + + example: https://fiddle.skia.org/c/@Bitmap_move_SkBitmap + */ + SkBitmap(SkBitmap&& src); + + /** Decrements SkPixelRef reference count, if SkPixelRef is not nullptr. + */ + ~SkBitmap(); + + /** Copies settings from src to returned SkBitmap. Shares pixels if src has pixels + allocated, so both bitmaps reference the same pixels. + + @param src SkBitmap to copy SkImageInfo, and share SkPixelRef + @return copy of src + + example: https://fiddle.skia.org/c/@Bitmap_copy_operator + */ + SkBitmap& operator=(const SkBitmap& src); + + /** Copies settings from src to returned SkBitmap. Moves ownership of src pixels to + SkBitmap. + + @param src SkBitmap to copy SkImageInfo, and reassign SkPixelRef + @return copy of src + + example: https://fiddle.skia.org/c/@Bitmap_move_operator + */ + SkBitmap& operator=(SkBitmap&& src); + + /** Swaps the fields of the two bitmaps. + + @param other SkBitmap exchanged with original + + example: https://fiddle.skia.org/c/@Bitmap_swap + */ + void swap(SkBitmap& other); + + /** Returns a constant reference to the SkPixmap holding the SkBitmap pixel + address, row bytes, and SkImageInfo. + + @return reference to SkPixmap describing this SkBitmap + */ + const SkPixmap& pixmap() const { return fPixmap; } + + /** Returns width, height, SkAlphaType, SkColorType, and SkColorSpace. + + @return reference to SkImageInfo + */ + const SkImageInfo& info() const { return fPixmap.info(); } + + /** Returns pixel count in each row. Should be equal or less than + rowBytes() / info().bytesPerPixel(). + + May be less than pixelRef().width(). Will not exceed pixelRef().width() less + pixelRefOrigin().fX. + + @return pixel width in SkImageInfo + */ + int width() const { return fPixmap.width(); } + + /** Returns pixel row count. + + Maybe be less than pixelRef().height(). Will not exceed pixelRef().height() less + pixelRefOrigin().fY. + + @return pixel height in SkImageInfo + */ + int height() const { return fPixmap.height(); } + + SkColorType colorType() const { return fPixmap.colorType(); } + + SkAlphaType alphaType() const { return fPixmap.alphaType(); } + + /** Returns SkColorSpace, the range of colors, associated with SkImageInfo. The + reference count of SkColorSpace is unchanged. The returned SkColorSpace is + immutable. + + @return SkColorSpace in SkImageInfo, or nullptr + */ + SkColorSpace* colorSpace() const; + + /** Returns smart pointer to SkColorSpace, the range of colors, associated with + SkImageInfo. The smart pointer tracks the number of objects sharing this + SkColorSpace reference so the memory is released when the owners destruct. + + The returned SkColorSpace is immutable. + + @return SkColorSpace in SkImageInfo wrapped in a smart pointer + */ + sk_sp refColorSpace() const; + + /** Returns number of bytes per pixel required by SkColorType. + Returns zero if colorType( is kUnknown_SkColorType. + + @return bytes in pixel + */ + int bytesPerPixel() const { return fPixmap.info().bytesPerPixel(); } + + /** Returns number of pixels that fit on row. Should be greater than or equal to + width(). + + @return maximum pixels per row + */ + int rowBytesAsPixels() const { return fPixmap.rowBytesAsPixels(); } + + /** Returns bit shift converting row bytes to row pixels. + Returns zero for kUnknown_SkColorType. + + @return one of: 0, 1, 2, 3; left shift to convert pixels to bytes + */ + int shiftPerPixel() const { return fPixmap.shiftPerPixel(); } + + /** Returns true if either width() or height() are zero. + + Does not check if SkPixelRef is nullptr; call drawsNothing() to check width(), + height(), and SkPixelRef. + + @return true if dimensions do not enclose area + */ + bool empty() const { return fPixmap.info().isEmpty(); } + + /** Returns true if SkPixelRef is nullptr. + + Does not check if width() or height() are zero; call drawsNothing() to check + width(), height(), and SkPixelRef. + + @return true if no SkPixelRef is associated + */ + bool isNull() const { return nullptr == fPixelRef; } + + /** Returns true if width() or height() are zero, or if SkPixelRef is nullptr. + If true, SkBitmap has no effect when drawn or drawn into. + + @return true if drawing has no effect + */ + bool drawsNothing() const { + return this->empty() || this->isNull(); + } + + /** Returns row bytes, the interval from one pixel row to the next. Row bytes + is at least as large as: width() * info().bytesPerPixel(). + + Returns zero if colorType() is kUnknown_SkColorType, or if row bytes supplied to + setInfo() is not large enough to hold a row of pixels. + + @return byte length of pixel row + */ + size_t rowBytes() const { return fPixmap.rowBytes(); } + + /** Sets SkAlphaType, if alphaType is compatible with SkColorType. + Returns true unless alphaType is kUnknown_SkAlphaType and current SkAlphaType + is not kUnknown_SkAlphaType. + + Returns true if SkColorType is kUnknown_SkColorType. alphaType is ignored, and + SkAlphaType remains kUnknown_SkAlphaType. + + Returns true if SkColorType is kRGB_565_SkColorType or kGray_8_SkColorType. + alphaType is ignored, and SkAlphaType remains kOpaque_SkAlphaType. + + If SkColorType is kARGB_4444_SkColorType, kRGBA_8888_SkColorType, + kBGRA_8888_SkColorType, or kRGBA_F16_SkColorType: returns true unless + alphaType is kUnknown_SkAlphaType and SkAlphaType is not kUnknown_SkAlphaType. + If SkAlphaType is kUnknown_SkAlphaType, alphaType is ignored. + + If SkColorType is kAlpha_8_SkColorType, returns true unless + alphaType is kUnknown_SkAlphaType and SkAlphaType is not kUnknown_SkAlphaType. + If SkAlphaType is kUnknown_SkAlphaType, alphaType is ignored. If alphaType is + kUnpremul_SkAlphaType, it is treated as kPremul_SkAlphaType. + + This changes SkAlphaType in SkPixelRef; all bitmaps sharing SkPixelRef + are affected. + + @return true if SkAlphaType is set + + example: https://fiddle.skia.org/c/@Bitmap_setAlphaType + */ + bool setAlphaType(SkAlphaType alphaType); + + /** Sets the SkColorSpace associated with this SkBitmap. + + The raw pixel data is not altered by this call; no conversion is + performed. + + This changes SkColorSpace in SkPixelRef; all bitmaps sharing SkPixelRef + are affected. + */ + void setColorSpace(sk_sp colorSpace); + + /** Returns pixel address, the base address corresponding to the pixel origin. + + @return pixel address + */ + void* getPixels() const { return fPixmap.writable_addr(); } + + /** Returns minimum memory required for pixel storage. + Does not include unused memory on last row when rowBytesAsPixels() exceeds width(). + Returns SIZE_MAX if result does not fit in size_t. + Returns zero if height() or width() is 0. + Returns height() times rowBytes() if colorType() is kUnknown_SkColorType. + + @return size in bytes of image buffer + */ + size_t computeByteSize() const { return fPixmap.computeByteSize(); } + + /** Returns true if pixels can not change. + + Most immutable SkBitmap checks trigger an assert only on debug builds. + + @return true if pixels are immutable + + example: https://fiddle.skia.org/c/@Bitmap_isImmutable + */ + bool isImmutable() const; + + /** Sets internal flag to mark SkBitmap as immutable. Once set, pixels can not change. + Any other bitmap sharing the same SkPixelRef are also marked as immutable. + Once SkPixelRef is marked immutable, the setting cannot be cleared. + + Writing to immutable SkBitmap pixels triggers an assert on debug builds. + + example: https://fiddle.skia.org/c/@Bitmap_setImmutable + */ + void setImmutable(); + + /** Returns true if SkAlphaType is set to hint that all pixels are opaque; their + alpha value is implicitly or explicitly 1.0. If true, and all pixels are + not opaque, Skia may draw incorrectly. + + Does not check if SkColorType allows alpha, or if any pixel value has + transparency. + + @return true if SkImageInfo SkAlphaType is kOpaque_SkAlphaType + */ + bool isOpaque() const { + return SkAlphaTypeIsOpaque(this->alphaType()); + } + + /** Resets to its initial state; all fields are set to zero, as if SkBitmap had + been initialized by SkBitmap(). + + Sets width, height, row bytes to zero; pixel address to nullptr; SkColorType to + kUnknown_SkColorType; and SkAlphaType to kUnknown_SkAlphaType. + + If SkPixelRef is allocated, its reference count is decreased by one, releasing + its memory if SkBitmap is the sole owner. + + example: https://fiddle.skia.org/c/@Bitmap_reset + */ + void reset(); + + /** Returns true if all pixels are opaque. SkColorType determines how pixels + are encoded, and whether pixel describes alpha. Returns true for SkColorType + without alpha in each pixel; for other SkColorType, returns true if all + pixels have alpha values equivalent to 1.0 or greater. + + For SkColorType kRGB_565_SkColorType or kGray_8_SkColorType: always + returns true. For SkColorType kAlpha_8_SkColorType, kBGRA_8888_SkColorType, + kRGBA_8888_SkColorType: returns true if all pixel alpha values are 255. + For SkColorType kARGB_4444_SkColorType: returns true if all pixel alpha values are 15. + For kRGBA_F16_SkColorType: returns true if all pixel alpha values are 1.0 or + greater. + + Returns false for kUnknown_SkColorType. + + @param bm SkBitmap to check + @return true if all pixels have opaque values or SkColorType is opaque + */ + static bool ComputeIsOpaque(const SkBitmap& bm) { + return bm.pixmap().computeIsOpaque(); + } + + /** Returns SkRect { 0, 0, width(), height() }. + + @param bounds container for floating point rectangle + + example: https://fiddle.skia.org/c/@Bitmap_getBounds + */ + void getBounds(SkRect* bounds) const; + + /** Returns SkIRect { 0, 0, width(), height() }. + + @param bounds container for integral rectangle + + example: https://fiddle.skia.org/c/@Bitmap_getBounds_2 + */ + void getBounds(SkIRect* bounds) const; + + /** Returns SkIRect { 0, 0, width(), height() }. + + @return integral rectangle from origin to width() and height() + */ + SkIRect bounds() const { return fPixmap.info().bounds(); } + + /** Returns SkISize { width(), height() }. + + @return integral size of width() and height() + */ + SkISize dimensions() const { return fPixmap.info().dimensions(); } + + /** Returns the bounds of this bitmap, offset by its SkPixelRef origin. + + @return bounds within SkPixelRef bounds + */ + SkIRect getSubset() const { + SkIPoint origin = this->pixelRefOrigin(); + return SkIRect::MakeXYWH(origin.x(), origin.y(), this->width(), this->height()); + } + + /** Sets width, height, SkAlphaType, SkColorType, SkColorSpace, and optional + rowBytes. Frees pixels, and returns true if successful. + + imageInfo.alphaType() may be altered to a value permitted by imageInfo.colorSpace(). + If imageInfo.colorType() is kUnknown_SkColorType, imageInfo.alphaType() is + set to kUnknown_SkAlphaType. + If imageInfo.colorType() is kAlpha_8_SkColorType and imageInfo.alphaType() is + kUnpremul_SkAlphaType, imageInfo.alphaType() is replaced by kPremul_SkAlphaType. + If imageInfo.colorType() is kRGB_565_SkColorType or kGray_8_SkColorType, + imageInfo.alphaType() is set to kOpaque_SkAlphaType. + If imageInfo.colorType() is kARGB_4444_SkColorType, kRGBA_8888_SkColorType, + kBGRA_8888_SkColorType, or kRGBA_F16_SkColorType: imageInfo.alphaType() remains + unchanged. + + rowBytes must equal or exceed imageInfo.minRowBytes(). If imageInfo.colorSpace() is + kUnknown_SkColorType, rowBytes is ignored and treated as zero; for all other + SkColorSpace values, rowBytes of zero is treated as imageInfo.minRowBytes(). + + Calls reset() and returns false if: + - rowBytes exceeds 31 bits + - imageInfo.width() is negative + - imageInfo.height() is negative + - rowBytes is positive and less than imageInfo.width() times imageInfo.bytesPerPixel() + + @param imageInfo contains width, height, SkAlphaType, SkColorType, SkColorSpace + @param rowBytes imageInfo.minRowBytes() or larger; or zero + @return true if SkImageInfo set successfully + + example: https://fiddle.skia.org/c/@Bitmap_setInfo + */ + bool setInfo(const SkImageInfo& imageInfo, size_t rowBytes = 0); + + /** \enum SkBitmap::AllocFlags + AllocFlags is obsolete. We always zero pixel memory when allocated. + */ + enum AllocFlags { + kZeroPixels_AllocFlag = 1 << 0, //!< zero pixel memory. No effect. This is the default. + }; + + /** Sets SkImageInfo to info following the rules in setInfo() and allocates pixel + memory. Memory is zeroed. + + Returns false and calls reset() if SkImageInfo could not be set, or memory could + not be allocated, or memory could not optionally be zeroed. + + On most platforms, allocating pixel memory may succeed even though there is + not sufficient memory to hold pixels; allocation does not take place + until the pixels are written to. The actual behavior depends on the platform + implementation of calloc(). + + @param info contains width, height, SkAlphaType, SkColorType, SkColorSpace + @param flags kZeroPixels_AllocFlag, or zero + @return true if pixels allocation is successful + */ + [[nodiscard]] bool tryAllocPixelsFlags(const SkImageInfo& info, uint32_t flags); + + /** Sets SkImageInfo to info following the rules in setInfo() and allocates pixel + memory. Memory is zeroed. + + Aborts execution if SkImageInfo could not be set, or memory could + not be allocated, or memory could not optionally + be zeroed. Abort steps may be provided by the user at compile time by defining + SK_ABORT. + + On most platforms, allocating pixel memory may succeed even though there is + not sufficient memory to hold pixels; allocation does not take place + until the pixels are written to. The actual behavior depends on the platform + implementation of calloc(). + + @param info contains width, height, SkAlphaType, SkColorType, SkColorSpace + @param flags kZeroPixels_AllocFlag, or zero + + example: https://fiddle.skia.org/c/@Bitmap_allocPixelsFlags + */ + void allocPixelsFlags(const SkImageInfo& info, uint32_t flags); + + /** Sets SkImageInfo to info following the rules in setInfo() and allocates pixel + memory. rowBytes must equal or exceed info.width() times info.bytesPerPixel(), + or equal zero. Pass in zero for rowBytes to compute the minimum valid value. + + Returns false and calls reset() if SkImageInfo could not be set, or memory could + not be allocated. + + On most platforms, allocating pixel memory may succeed even though there is + not sufficient memory to hold pixels; allocation does not take place + until the pixels are written to. The actual behavior depends on the platform + implementation of malloc(). + + @param info contains width, height, SkAlphaType, SkColorType, SkColorSpace + @param rowBytes size of pixel row or larger; may be zero + @return true if pixel storage is allocated + */ + [[nodiscard]] bool tryAllocPixels(const SkImageInfo& info, size_t rowBytes); + + /** Sets SkImageInfo to info following the rules in setInfo() and allocates pixel + memory. rowBytes must equal or exceed info.width() times info.bytesPerPixel(), + or equal zero. Pass in zero for rowBytes to compute the minimum valid value. + + Aborts execution if SkImageInfo could not be set, or memory could + not be allocated. Abort steps may be provided by + the user at compile time by defining SK_ABORT. + + On most platforms, allocating pixel memory may succeed even though there is + not sufficient memory to hold pixels; allocation does not take place + until the pixels are written to. The actual behavior depends on the platform + implementation of malloc(). + + @param info contains width, height, SkAlphaType, SkColorType, SkColorSpace + @param rowBytes size of pixel row or larger; may be zero + + example: https://fiddle.skia.org/c/@Bitmap_allocPixels + */ + void allocPixels(const SkImageInfo& info, size_t rowBytes); + + /** Sets SkImageInfo to info following the rules in setInfo() and allocates pixel + memory. + + Returns false and calls reset() if SkImageInfo could not be set, or memory could + not be allocated. + + On most platforms, allocating pixel memory may succeed even though there is + not sufficient memory to hold pixels; allocation does not take place + until the pixels are written to. The actual behavior depends on the platform + implementation of malloc(). + + @param info contains width, height, SkAlphaType, SkColorType, SkColorSpace + @return true if pixel storage is allocated + */ + [[nodiscard]] bool tryAllocPixels(const SkImageInfo& info) { + return this->tryAllocPixels(info, info.minRowBytes()); + } + + /** Sets SkImageInfo to info following the rules in setInfo() and allocates pixel + memory. + + Aborts execution if SkImageInfo could not be set, or memory could + not be allocated. Abort steps may be provided by + the user at compile time by defining SK_ABORT. + + On most platforms, allocating pixel memory may succeed even though there is + not sufficient memory to hold pixels; allocation does not take place + until the pixels are written to. The actual behavior depends on the platform + implementation of malloc(). + + @param info contains width, height, SkAlphaType, SkColorType, SkColorSpace + + example: https://fiddle.skia.org/c/@Bitmap_allocPixels_2 + */ + void allocPixels(const SkImageInfo& info); + + /** Sets SkImageInfo to width, height, and native color type; and allocates + pixel memory. If isOpaque is true, sets SkImageInfo to kOpaque_SkAlphaType; + otherwise, sets to kPremul_SkAlphaType. + + Calls reset() and returns false if width exceeds 29 bits or is negative, + or height is negative. + + Returns false if allocation fails. + + Use to create SkBitmap that matches SkPMColor, the native pixel arrangement on + the platform. SkBitmap drawn to output device skips converting its pixel format. + + @param width pixel column count; must be zero or greater + @param height pixel row count; must be zero or greater + @param isOpaque true if pixels do not have transparency + @return true if pixel storage is allocated + */ + [[nodiscard]] bool tryAllocN32Pixels(int width, int height, bool isOpaque = false); + + /** Sets SkImageInfo to width, height, and the native color type; and allocates + pixel memory. If isOpaque is true, sets SkImageInfo to kOpaque_SkAlphaType; + otherwise, sets to kPremul_SkAlphaType. + + Aborts if width exceeds 29 bits or is negative, or height is negative, or + allocation fails. Abort steps may be provided by the user at compile time by + defining SK_ABORT. + + Use to create SkBitmap that matches SkPMColor, the native pixel arrangement on + the platform. SkBitmap drawn to output device skips converting its pixel format. + + @param width pixel column count; must be zero or greater + @param height pixel row count; must be zero or greater + @param isOpaque true if pixels do not have transparency + + example: https://fiddle.skia.org/c/@Bitmap_allocN32Pixels + */ + void allocN32Pixels(int width, int height, bool isOpaque = false); + + /** Sets SkImageInfo to info following the rules in setInfo(), and creates SkPixelRef + containing pixels and rowBytes. releaseProc, if not nullptr, is called + immediately on failure or when pixels are no longer referenced. context may be + nullptr. + + If SkImageInfo could not be set, or rowBytes is less than info.minRowBytes(): + calls releaseProc if present, calls reset(), and returns false. + + Otherwise, if pixels equals nullptr: sets SkImageInfo, calls releaseProc if + present, returns true. + + If SkImageInfo is set, pixels is not nullptr, and releaseProc is not nullptr: + when pixels are no longer referenced, calls releaseProc with pixels and context + as parameters. + + @param info contains width, height, SkAlphaType, SkColorType, SkColorSpace + @param pixels address or pixel storage; may be nullptr + @param rowBytes size of pixel row or larger + @param releaseProc function called when pixels can be deleted; may be nullptr + @param context caller state passed to releaseProc; may be nullptr + @return true if SkImageInfo is set to info + */ + bool installPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, + void (*releaseProc)(void* addr, void* context), void* context); + + /** Sets SkImageInfo to info following the rules in setInfo(), and creates SkPixelRef + containing pixels and rowBytes. + + If SkImageInfo could not be set, or rowBytes is less than info.minRowBytes(): + calls reset(), and returns false. + + Otherwise, if pixels equals nullptr: sets SkImageInfo, returns true. + + Caller must ensure that pixels are valid for the lifetime of SkBitmap and SkPixelRef. + + @param info contains width, height, SkAlphaType, SkColorType, SkColorSpace + @param pixels address or pixel storage; may be nullptr + @param rowBytes size of pixel row or larger + @return true if SkImageInfo is set to info + */ + bool installPixels(const SkImageInfo& info, void* pixels, size_t rowBytes) { + return this->installPixels(info, pixels, rowBytes, nullptr, nullptr); + } + + /** Sets SkImageInfo to pixmap.info() following the rules in setInfo(), and creates + SkPixelRef containing pixmap.addr() and pixmap.rowBytes(). + + If SkImageInfo could not be set, or pixmap.rowBytes() is less than + SkImageInfo::minRowBytes(): calls reset(), and returns false. + + Otherwise, if pixmap.addr() equals nullptr: sets SkImageInfo, returns true. + + Caller must ensure that pixmap is valid for the lifetime of SkBitmap and SkPixelRef. + + @param pixmap SkImageInfo, pixel address, and rowBytes() + @return true if SkImageInfo was set to pixmap.info() + + example: https://fiddle.skia.org/c/@Bitmap_installPixels_3 + */ + bool installPixels(const SkPixmap& pixmap); + + /** Deprecated. + */ + bool installMaskPixels(SkMaskBuilder& mask); + + /** Replaces SkPixelRef with pixels, preserving SkImageInfo and rowBytes(). + Sets SkPixelRef origin to (0, 0). + + If pixels is nullptr, or if info().colorType() equals kUnknown_SkColorType; + release reference to SkPixelRef, and set SkPixelRef to nullptr. + + Caller is responsible for handling ownership pixel memory for the lifetime + of SkBitmap and SkPixelRef. + + @param pixels address of pixel storage, managed by caller + + example: https://fiddle.skia.org/c/@Bitmap_setPixels + */ + void setPixels(void* pixels); + + /** Allocates pixel memory with HeapAllocator, and replaces existing SkPixelRef. + The allocation size is determined by SkImageInfo width, height, and SkColorType. + + Returns false if info().colorType() is kUnknown_SkColorType, or allocation fails. + + @return true if the allocation succeeds + */ + [[nodiscard]] bool tryAllocPixels() { + return this->tryAllocPixels((Allocator*)nullptr); + } + + /** Allocates pixel memory with HeapAllocator, and replaces existing SkPixelRef. + The allocation size is determined by SkImageInfo width, height, and SkColorType. + + Aborts if info().colorType() is kUnknown_SkColorType, or allocation fails. + Abort steps may be provided by the user at compile + time by defining SK_ABORT. + + example: https://fiddle.skia.org/c/@Bitmap_allocPixels_3 + */ + void allocPixels(); + + /** Allocates pixel memory with allocator, and replaces existing SkPixelRef. + The allocation size is determined by SkImageInfo width, height, and SkColorType. + If allocator is nullptr, use HeapAllocator instead. + + Returns false if Allocator::allocPixelRef return false. + + @param allocator instance of SkBitmap::Allocator instantiation + @return true if custom allocator reports success + */ + [[nodiscard]] bool tryAllocPixels(Allocator* allocator); + + /** Allocates pixel memory with allocator, and replaces existing SkPixelRef. + The allocation size is determined by SkImageInfo width, height, and SkColorType. + If allocator is nullptr, use HeapAllocator instead. + + Aborts if Allocator::allocPixelRef return false. Abort steps may be provided by + the user at compile time by defining SK_ABORT. + + @param allocator instance of SkBitmap::Allocator instantiation + + example: https://fiddle.skia.org/c/@Bitmap_allocPixels_4 + */ + void allocPixels(Allocator* allocator); + + /** Returns SkPixelRef, which contains: pixel base address; its dimensions; and + rowBytes(), the interval from one row to the next. Does not change SkPixelRef + reference count. SkPixelRef may be shared by multiple bitmaps. + If SkPixelRef has not been set, returns nullptr. + + @return SkPixelRef, or nullptr + */ + SkPixelRef* pixelRef() const { return fPixelRef.get(); } + + /** Returns origin of pixels within SkPixelRef. SkBitmap bounds is always contained + by SkPixelRef bounds, which may be the same size or larger. Multiple SkBitmap + can share the same SkPixelRef, where each SkBitmap has different bounds. + + The returned origin added to SkBitmap dimensions equals or is smaller than the + SkPixelRef dimensions. + + Returns (0, 0) if SkPixelRef is nullptr. + + @return pixel origin within SkPixelRef + + example: https://fiddle.skia.org/c/@Bitmap_pixelRefOrigin + */ + SkIPoint pixelRefOrigin() const; + + /** Replaces pixelRef and origin in SkBitmap. dx and dy specify the offset + within the SkPixelRef pixels for the top-left corner of the bitmap. + + Asserts in debug builds if dx or dy are out of range. Pins dx and dy + to legal range in release builds. + + The caller is responsible for ensuring that the pixels match the + SkColorType and SkAlphaType in SkImageInfo. + + @param pixelRef SkPixelRef describing pixel address and rowBytes() + @param dx column offset in SkPixelRef for bitmap origin + @param dy row offset in SkPixelRef for bitmap origin + + example: https://fiddle.skia.org/c/@Bitmap_setPixelRef + */ + void setPixelRef(sk_sp pixelRef, int dx, int dy); + + /** Returns true if SkBitmap is can be drawn. + + @return true if getPixels() is not nullptr + */ + bool readyToDraw() const { + return this->getPixels() != nullptr; + } + + /** Returns a unique value corresponding to the pixels in SkPixelRef. + Returns a different value after notifyPixelsChanged() has been called. + Returns zero if SkPixelRef is nullptr. + + Determines if pixels have changed since last examined. + + @return unique value for pixels in SkPixelRef + + example: https://fiddle.skia.org/c/@Bitmap_getGenerationID + */ + uint32_t getGenerationID() const; + + /** Marks that pixels in SkPixelRef have changed. Subsequent calls to + getGenerationID() return a different value. + + example: https://fiddle.skia.org/c/@Bitmap_notifyPixelsChanged + */ + void notifyPixelsChanged() const; + + /** Replaces pixel values with c, interpreted as being in the sRGB SkColorSpace. + All pixels contained by bounds() are affected. If the colorType() is + kGray_8_SkColorType or kRGB_565_SkColorType, then alpha is ignored; RGB is + treated as opaque. If colorType() is kAlpha_8_SkColorType, then RGB is ignored. + + @param c unpremultiplied color + + example: https://fiddle.skia.org/c/@Bitmap_eraseColor + */ + void eraseColor(SkColor4f) const; + + /** Replaces pixel values with c, interpreted as being in the sRGB SkColorSpace. + All pixels contained by bounds() are affected. If the colorType() is + kGray_8_SkColorType or kRGB_565_SkColorType, then alpha is ignored; RGB is + treated as opaque. If colorType() is kAlpha_8_SkColorType, then RGB is ignored. + + Input color is ultimately converted to an SkColor4f, so eraseColor(SkColor4f c) + will have higher color resolution. + + @param c unpremultiplied color. + + example: https://fiddle.skia.org/c/@Bitmap_eraseColor + */ + void eraseColor(SkColor c) const; + + /** Replaces pixel values with unpremultiplied color built from a, r, g, and b, + interpreted as being in the sRGB SkColorSpace. All pixels contained by + bounds() are affected. If the colorType() is kGray_8_SkColorType or + kRGB_565_SkColorType, then a is ignored; r, g, and b are treated as opaque. + If colorType() is kAlpha_8_SkColorType, then r, g, and b are ignored. + + @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) + */ + void eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const { + this->eraseColor(SkColorSetARGB(a, r, g, b)); + } + + /** Replaces pixel values inside area with c. interpreted as being in the sRGB + SkColorSpace. If area does not intersect bounds(), call has no effect. + + If the colorType() is kGray_8_SkColorType or kRGB_565_SkColorType, then alpha + is ignored; RGB is treated as opaque. If colorType() is kAlpha_8_SkColorType, + then RGB is ignored. + + @param c unpremultiplied color + @param area rectangle to fill + + example: https://fiddle.skia.org/c/@Bitmap_erase + */ + void erase(SkColor4f c, const SkIRect& area) const; + + /** Replaces pixel values inside area with c. interpreted as being in the sRGB + SkColorSpace. If area does not intersect bounds(), call has no effect. + + If the colorType() is kGray_8_SkColorType or kRGB_565_SkColorType, then alpha + is ignored; RGB is treated as opaque. If colorType() is kAlpha_8_SkColorType, + then RGB is ignored. + + Input color is ultimately converted to an SkColor4f, so erase(SkColor4f c) + will have higher color resolution. + + @param c unpremultiplied color + @param area rectangle to fill + + example: https://fiddle.skia.org/c/@Bitmap_erase + */ + void erase(SkColor c, const SkIRect& area) const; + + /** Deprecated. + */ + void eraseArea(const SkIRect& area, SkColor c) const { + this->erase(c, area); + } + + /** Returns pixel at (x, y) as unpremultiplied color. + Returns black with alpha if SkColorType is kAlpha_8_SkColorType. + + Input is not validated: out of bounds values of x or y trigger an assert() if + built with SK_DEBUG defined; and returns undefined values or may crash if + SK_RELEASE is defined. Fails if SkColorType is kUnknown_SkColorType or + pixel address is nullptr. + + SkColorSpace in SkImageInfo is ignored. Some color precision may be lost in the + conversion to unpremultiplied color; original pixel data may have additional + precision. + + @param x column index, zero or greater, and less than width() + @param y row index, zero or greater, and less than height() + @return pixel converted to unpremultiplied color + */ + SkColor getColor(int x, int y) const { + return this->pixmap().getColor(x, y); + } + + /** Returns pixel at (x, y) as unpremultiplied float color. + Returns black with alpha if SkColorType is kAlpha_8_SkColorType. + + Input is not validated: out of bounds values of x or y trigger an assert() if + built with SK_DEBUG defined; and returns undefined values or may crash if + SK_RELEASE is defined. Fails if SkColorType is kUnknown_SkColorType or + pixel address is nullptr. + + SkColorSpace in SkImageInfo is ignored. Some color precision may be lost in the + conversion to unpremultiplied color. + + @param x column index, zero or greater, and less than width() + @param y row index, zero or greater, and less than height() + @return pixel converted to unpremultiplied color + */ + SkColor4f getColor4f(int x, int y) const { return this->pixmap().getColor4f(x, y); } + + /** Look up the pixel at (x,y) and return its alpha component, normalized to [0..1]. + This is roughly equivalent to SkGetColorA(getColor()), but can be more efficent + (and more precise if the pixels store more than 8 bits per component). + + @param x column index, zero or greater, and less than width() + @param y row index, zero or greater, and less than height() + @return alpha converted to normalized float + */ + float getAlphaf(int x, int y) const { + return this->pixmap().getAlphaf(x, y); + } + + /** Returns pixel address at (x, y). + + Input is not validated: out of bounds values of x or y, or kUnknown_SkColorType, + trigger an assert() if built with SK_DEBUG defined. Returns nullptr if + SkColorType is kUnknown_SkColorType, or SkPixelRef is nullptr. + + Performs a lookup of pixel size; for better performance, call + one of: getAddr8(), getAddr16(), or getAddr32(). + + @param x column index, zero or greater, and less than width() + @param y row index, zero or greater, and less than height() + @return generic pointer to pixel + + example: https://fiddle.skia.org/c/@Bitmap_getAddr + */ + void* getAddr(int x, int y) const; + + /** Returns address at (x, y). + + Input is not validated. Triggers an assert() if built with SK_DEBUG defined and: + - SkPixelRef is nullptr + - bytesPerPixel() is not four + - x is negative, or not less than width() + - y is negative, or not less than height() + + @param x column index, zero or greater, and less than width() + @param y row index, zero or greater, and less than height() + @return unsigned 32-bit pointer to pixel at (x, y) + */ + inline uint32_t* getAddr32(int x, int y) const; + + /** Returns address at (x, y). + + Input is not validated. Triggers an assert() if built with SK_DEBUG defined and: + - SkPixelRef is nullptr + - bytesPerPixel() is not two + - x is negative, or not less than width() + - y is negative, or not less than height() + + @param x column index, zero or greater, and less than width() + @param y row index, zero or greater, and less than height() + @return unsigned 16-bit pointer to pixel at (x, y) + */ + inline uint16_t* getAddr16(int x, int y) const; + + /** Returns address at (x, y). + + Input is not validated. Triggers an assert() if built with SK_DEBUG defined and: + - SkPixelRef is nullptr + - bytesPerPixel() is not one + - x is negative, or not less than width() + - y is negative, or not less than height() + + @param x column index, zero or greater, and less than width() + @param y row index, zero or greater, and less than height() + @return unsigned 8-bit pointer to pixel at (x, y) + */ + inline uint8_t* getAddr8(int x, int y) const; + + /** Shares SkPixelRef with dst. Pixels are not copied; SkBitmap and dst point + to the same pixels; dst bounds() are set to the intersection of subset + and the original bounds(). + + subset may be larger than bounds(). Any area outside of bounds() is ignored. + + Any contents of dst are discarded. + + Return false if: + - dst is nullptr + - SkPixelRef is nullptr + - subset does not intersect bounds() + + @param dst SkBitmap set to subset + @param subset rectangle of pixels to reference + @return true if dst is replaced by subset + + example: https://fiddle.skia.org/c/@Bitmap_extractSubset + */ + bool extractSubset(SkBitmap* dst, const SkIRect& subset) const; + + /** Copies a SkRect of pixels from SkBitmap to dstPixels. Copy starts at (srcX, srcY), + and does not exceed SkBitmap (width(), height()). + + dstInfo specifies width, height, SkColorType, SkAlphaType, and SkColorSpace of + destination. dstRowBytes specifics the gap from one destination row to the next. + Returns true if pixels are copied. Returns false if: + - dstInfo has no address + - dstRowBytes is less than dstInfo.minRowBytes() + - SkPixelRef is nullptr + + Pixels are copied only if pixel conversion is possible. If SkBitmap colorType() is + kGray_8_SkColorType, or kAlpha_8_SkColorType; dstInfo.colorType() must match. + If SkBitmap colorType() is kGray_8_SkColorType, dstInfo.colorSpace() must match. + If SkBitmap alphaType() is kOpaque_SkAlphaType, dstInfo.alphaType() must + match. If SkBitmap colorSpace() is nullptr, dstInfo.colorSpace() must match. Returns + false if pixel conversion is not possible. + + srcX and srcY may be negative to copy only top or left of source. Returns + false if width() or height() is zero or negative. + Returns false if abs(srcX) >= Bitmap width(), or if abs(srcY) >= Bitmap height(). + + @param dstInfo destination width, height, SkColorType, SkAlphaType, SkColorSpace + @param dstPixels destination pixel storage + @param dstRowBytes destination row length + @param srcX column index whose absolute value is less than width() + @param srcY row index whose absolute value is less than height() + @return true if pixels are copied to dstPixels + */ + bool readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes, + int srcX, int srcY) const; + + /** Copies a SkRect of pixels from SkBitmap to dst. Copy starts at (srcX, srcY), and + does not exceed SkBitmap (width(), height()). + + dst specifies width, height, SkColorType, SkAlphaType, SkColorSpace, pixel storage, + and row bytes of destination. dst.rowBytes() specifics the gap from one destination + row to the next. Returns true if pixels are copied. Returns false if: + - dst pixel storage equals nullptr + - dst.rowBytes is less than SkImageInfo::minRowBytes() + - SkPixelRef is nullptr + + Pixels are copied only if pixel conversion is possible. If SkBitmap colorType() is + kGray_8_SkColorType, or kAlpha_8_SkColorType; dst SkColorType must match. + If SkBitmap colorType() is kGray_8_SkColorType, dst SkColorSpace must match. + If SkBitmap alphaType() is kOpaque_SkAlphaType, dst SkAlphaType must + match. If SkBitmap colorSpace() is nullptr, dst SkColorSpace must match. Returns + false if pixel conversion is not possible. + + srcX and srcY may be negative to copy only top or left of source. Returns + false if width() or height() is zero or negative. + Returns false if abs(srcX) >= Bitmap width(), or if abs(srcY) >= Bitmap height(). + + @param dst destination SkPixmap: SkImageInfo, pixels, row bytes + @param srcX column index whose absolute value is less than width() + @param srcY row index whose absolute value is less than height() + @return true if pixels are copied to dst + + example: https://fiddle.skia.org/c/@Bitmap_readPixels_2 + */ + bool readPixels(const SkPixmap& dst, int srcX, int srcY) const; + + /** Copies a SkRect of pixels from SkBitmap to dst. Copy starts at (0, 0), and + does not exceed SkBitmap (width(), height()). + + dst specifies width, height, SkColorType, SkAlphaType, SkColorSpace, pixel storage, + and row bytes of destination. dst.rowBytes() specifics the gap from one destination + row to the next. Returns true if pixels are copied. Returns false if: + - dst pixel storage equals nullptr + - dst.rowBytes is less than SkImageInfo::minRowBytes() + - SkPixelRef is nullptr + + Pixels are copied only if pixel conversion is possible. If SkBitmap colorType() is + kGray_8_SkColorType, or kAlpha_8_SkColorType; dst SkColorType must match. + If SkBitmap colorType() is kGray_8_SkColorType, dst SkColorSpace must match. + If SkBitmap alphaType() is kOpaque_SkAlphaType, dst SkAlphaType must + match. If SkBitmap colorSpace() is nullptr, dst SkColorSpace must match. Returns + false if pixel conversion is not possible. + + @param dst destination SkPixmap: SkImageInfo, pixels, row bytes + @return true if pixels are copied to dst + */ + bool readPixels(const SkPixmap& dst) const { + return this->readPixels(dst, 0, 0); + } + + /** Copies a SkRect of pixels from src. Copy starts at (dstX, dstY), and does not exceed + (src.width(), src.height()). + + src specifies width, height, SkColorType, SkAlphaType, SkColorSpace, pixel storage, + and row bytes of source. src.rowBytes() specifics the gap from one source + row to the next. Returns true if pixels are copied. Returns false if: + - src pixel storage equals nullptr + - src.rowBytes is less than SkImageInfo::minRowBytes() + - SkPixelRef is nullptr + + Pixels are copied only if pixel conversion is possible. If SkBitmap colorType() is + kGray_8_SkColorType, or kAlpha_8_SkColorType; src SkColorType must match. + If SkBitmap colorType() is kGray_8_SkColorType, src SkColorSpace must match. + If SkBitmap alphaType() is kOpaque_SkAlphaType, src SkAlphaType must + match. If SkBitmap colorSpace() is nullptr, src SkColorSpace must match. Returns + false if pixel conversion is not possible. + + dstX and dstY may be negative to copy only top or left of source. Returns + false if width() or height() is zero or negative. + Returns false if abs(dstX) >= Bitmap width(), or if abs(dstY) >= Bitmap height(). + + @param src source SkPixmap: SkImageInfo, pixels, row bytes + @param dstX column index whose absolute value is less than width() + @param dstY row index whose absolute value is less than height() + @return true if src pixels are copied to SkBitmap + + example: https://fiddle.skia.org/c/@Bitmap_writePixels + */ + bool writePixels(const SkPixmap& src, int dstX, int dstY); + + /** Copies a SkRect of pixels from src. Copy starts at (0, 0), and does not exceed + (src.width(), src.height()). + + src specifies width, height, SkColorType, SkAlphaType, SkColorSpace, pixel storage, + and row bytes of source. src.rowBytes() specifics the gap from one source + row to the next. Returns true if pixels are copied. Returns false if: + - src pixel storage equals nullptr + - src.rowBytes is less than SkImageInfo::minRowBytes() + - SkPixelRef is nullptr + + Pixels are copied only if pixel conversion is possible. If SkBitmap colorType() is + kGray_8_SkColorType, or kAlpha_8_SkColorType; src SkColorType must match. + If SkBitmap colorType() is kGray_8_SkColorType, src SkColorSpace must match. + If SkBitmap alphaType() is kOpaque_SkAlphaType, src SkAlphaType must + match. If SkBitmap colorSpace() is nullptr, src SkColorSpace must match. Returns + false if pixel conversion is not possible. + + @param src source SkPixmap: SkImageInfo, pixels, row bytes + @return true if src pixels are copied to SkBitmap + */ + bool writePixels(const SkPixmap& src) { + return this->writePixels(src, 0, 0); + } + + /** Sets dst to alpha described by pixels. Returns false if dst cannot be written to + or dst pixels cannot be allocated. + + Uses HeapAllocator to reserve memory for dst SkPixelRef. + + @param dst holds SkPixelRef to fill with alpha layer + @return true if alpha layer was constructed in dst SkPixelRef + */ + bool extractAlpha(SkBitmap* dst) const { + return this->extractAlpha(dst, nullptr, nullptr, nullptr); + } + + /** Sets dst to alpha described by pixels. Returns false if dst cannot be written to + or dst pixels cannot be allocated. + + If paint is not nullptr and contains SkMaskFilter, SkMaskFilter + generates mask alpha from SkBitmap. Uses HeapAllocator to reserve memory for dst + SkPixelRef. Sets offset to top-left position for dst for alignment with SkBitmap; + (0, 0) unless SkMaskFilter generates mask. + + @param dst holds SkPixelRef to fill with alpha layer + @param paint holds optional SkMaskFilter; may be nullptr + @param offset top-left position for dst; may be nullptr + @return true if alpha layer was constructed in dst SkPixelRef + */ + bool extractAlpha(SkBitmap* dst, const SkPaint* paint, + SkIPoint* offset) const { + return this->extractAlpha(dst, paint, nullptr, offset); + } + + /** Sets dst to alpha described by pixels. Returns false if dst cannot be written to + or dst pixels cannot be allocated. + + If paint is not nullptr and contains SkMaskFilter, SkMaskFilter + generates mask alpha from SkBitmap. allocator may reference a custom allocation + class or be set to nullptr to use HeapAllocator. Sets offset to top-left + position for dst for alignment with SkBitmap; (0, 0) unless SkMaskFilter generates + mask. + + @param dst holds SkPixelRef to fill with alpha layer + @param paint holds optional SkMaskFilter; may be nullptr + @param allocator function to reserve memory for SkPixelRef; may be nullptr + @param offset top-left position for dst; may be nullptr + @return true if alpha layer was constructed in dst SkPixelRef + */ + bool extractAlpha(SkBitmap* dst, const SkPaint* paint, Allocator* allocator, + SkIPoint* offset) const; + + /** Copies SkBitmap pixel address, row bytes, and SkImageInfo to pixmap, if address + is available, and returns true. If pixel address is not available, return + false and leave pixmap unchanged. + + pixmap contents become invalid on any future change to SkBitmap. + + @param pixmap storage for pixel state if pixels are readable; otherwise, ignored + @return true if SkBitmap has direct access to pixels + + example: https://fiddle.skia.org/c/@Bitmap_peekPixels + */ + bool peekPixels(SkPixmap* pixmap) const; + + /** + * Make a shader with the specified tiling, matrix and sampling. + */ + sk_sp makeShader(SkTileMode tmx, SkTileMode tmy, const SkSamplingOptions&, + const SkMatrix* localMatrix = nullptr) const; + sk_sp makeShader(SkTileMode tmx, SkTileMode tmy, const SkSamplingOptions& sampling, + const SkMatrix& lm) const; + /** Defaults to clamp in both X and Y. */ + sk_sp makeShader(const SkSamplingOptions& sampling, const SkMatrix& lm) const; + sk_sp makeShader(const SkSamplingOptions& sampling, + const SkMatrix* lm = nullptr) const; + + /** + * Returns a new image from the bitmap. If the bitmap is marked immutable, this will + * share the pixel buffer. If not, it will make a copy of the pixels for the image. + */ + sk_sp asImage() const; + + /** Asserts if internal values are illegal or inconsistent. Only available if + SK_DEBUG is defined at compile time. + */ + SkDEBUGCODE(void validate() const;) + + /** \class SkBitmap::Allocator + Abstract subclass of HeapAllocator. + */ + class Allocator : public SkRefCnt { + public: + + /** Allocates the pixel memory for the bitmap, given its dimensions and + SkColorType. Returns true on success, where success means either setPixels() + or setPixelRef() was called. + + @param bitmap SkBitmap containing SkImageInfo as input, and SkPixelRef as output + @return true if SkPixelRef was allocated + */ + virtual bool allocPixelRef(SkBitmap* bitmap) = 0; + private: + using INHERITED = SkRefCnt; + }; + + /** \class SkBitmap::HeapAllocator + Subclass of SkBitmap::Allocator that returns a SkPixelRef that allocates its pixel + memory from the heap. This is the default SkBitmap::Allocator invoked by + allocPixels(). + */ + class HeapAllocator : public Allocator { + public: + + /** Allocates the pixel memory for the bitmap, given its dimensions and + SkColorType. Returns true on success, where success means either setPixels() + or setPixelRef() was called. + + @param bitmap SkBitmap containing SkImageInfo as input, and SkPixelRef as output + @return true if pixels are allocated + + example: https://fiddle.skia.org/c/@Bitmap_HeapAllocator_allocPixelRef + */ + bool allocPixelRef(SkBitmap* bitmap) override; + }; + +private: + sk_sp fPixelRef; + SkPixmap fPixmap; + sk_sp fMips; + + friend class SkImage_Raster; + friend class SkReadBuffer; // unflatten + friend class GrProxyProvider; // fMips +}; + +/////////////////////////////////////////////////////////////////////////////// + +inline uint32_t* SkBitmap::getAddr32(int x, int y) const { + SkASSERT(fPixmap.addr()); + return fPixmap.writable_addr32(x, y); +} + +inline uint16_t* SkBitmap::getAddr16(int x, int y) const { + SkASSERT(fPixmap.addr()); + return fPixmap.writable_addr16(x, y); +} + +inline uint8_t* SkBitmap::getAddr8(int x, int y) const { + SkASSERT(fPixmap.addr()); + return fPixmap.writable_addr8(x, y); +} + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkBlendMode.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkBlendMode.h new file mode 100644 index 0000000000..4abe915762 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkBlendMode.h @@ -0,0 +1,112 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkBlendMode_DEFINED +#define SkBlendMode_DEFINED + +#include "include/core/SkTypes.h" + +/** + * Blends are operators that take in two colors (source, destination) and return a new color. + * Many of these operate the same on all 4 components: red, green, blue, alpha. For these, + * we just document what happens to one component, rather than naming each one separately. + * + * Different SkColorTypes have different representations for color components: + * 8-bit: 0..255 + * 6-bit: 0..63 + * 5-bit: 0..31 + * 4-bit: 0..15 + * floats: 0...1 + * + * The documentation is expressed as if the component values are always 0..1 (floats). + * + * For brevity, the documentation uses the following abbreviations + * s : source + * d : destination + * sa : source alpha + * da : destination alpha + * + * Results are abbreviated + * r : if all 4 components are computed in the same manner + * ra : result alpha component + * rc : result "color": red, green, blue components + */ +enum class SkBlendMode { + kClear, //!< r = 0 + kSrc, //!< r = s + kDst, //!< r = d + kSrcOver, //!< r = s + (1-sa)*d + kDstOver, //!< r = d + (1-da)*s + kSrcIn, //!< r = s * da + kDstIn, //!< r = d * sa + kSrcOut, //!< r = s * (1-da) + kDstOut, //!< r = d * (1-sa) + kSrcATop, //!< r = s*da + d*(1-sa) + kDstATop, //!< r = d*sa + s*(1-da) + kXor, //!< r = s*(1-da) + d*(1-sa) + kPlus, //!< r = min(s + d, 1) + kModulate, //!< r = s*d + kScreen, //!< r = s + d - s*d + + kOverlay, //!< multiply or screen, depending on destination + kDarken, //!< rc = s + d - max(s*da, d*sa), ra = kSrcOver + kLighten, //!< rc = s + d - min(s*da, d*sa), ra = kSrcOver + kColorDodge, //!< brighten destination to reflect source + kColorBurn, //!< darken destination to reflect source + kHardLight, //!< multiply or screen, depending on source + kSoftLight, //!< lighten or darken, depending on source + kDifference, //!< rc = s + d - 2*(min(s*da, d*sa)), ra = kSrcOver + kExclusion, //!< rc = s + d - two(s*d), ra = kSrcOver + kMultiply, //!< r = s*(1-da) + d*(1-sa) + s*d + + kHue, //!< hue of source with saturation and luminosity of destination + kSaturation, //!< saturation of source with hue and luminosity of destination + kColor, //!< hue and saturation of source with luminosity of destination + kLuminosity, //!< luminosity of source with hue and saturation of destination + + kLastCoeffMode = kScreen, //!< last porter duff blend mode + kLastSeparableMode = kMultiply, //!< last blend mode operating separately on components + kLastMode = kLuminosity, //!< last valid value +}; + +static constexpr int kSkBlendModeCount = static_cast(SkBlendMode::kLastMode) + 1; + +/** + * For Porter-Duff SkBlendModes (those <= kLastCoeffMode), these coefficients describe the blend + * equation used. Coefficient-based blend modes specify an equation: + * ('dstCoeff' * dst + 'srcCoeff' * src), where the coefficient values are constants, functions of + * the src or dst alpha, or functions of the src or dst color. + */ +enum class SkBlendModeCoeff { + kZero, /** 0 */ + kOne, /** 1 */ + kSC, /** src color */ + kISC, /** inverse src color (i.e. 1 - sc) */ + kDC, /** dst color */ + kIDC, /** inverse dst color (i.e. 1 - dc) */ + kSA, /** src alpha */ + kISA, /** inverse src alpha (i.e. 1 - sa) */ + kDA, /** dst alpha */ + kIDA, /** inverse dst alpha (i.e. 1 - da) */ + + kCoeffCount +}; + +/** + * Returns true if 'mode' is a coefficient-based blend mode (<= kLastCoeffMode). If true is + * returned, the mode's src and dst coefficient functions are set in 'src' and 'dst'. + */ +SK_API bool SkBlendMode_AsCoeff(SkBlendMode mode, SkBlendModeCoeff* src, SkBlendModeCoeff* dst); + + +/** Returns name of blendMode as null-terminated C string. + + @return C string +*/ +SK_API const char* SkBlendMode_Name(SkBlendMode blendMode); + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkBlender.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkBlender.h new file mode 100644 index 0000000000..741c4614df --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkBlender.h @@ -0,0 +1,31 @@ +/* + * Copyright 2021 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkBlender_DEFINED +#define SkBlender_DEFINED + +#include "include/core/SkBlendMode.h" +#include "include/core/SkFlattenable.h" + +/** + * SkBlender represents a custom blend function in the Skia pipeline. When an SkBlender is + * present in a paint, the SkBlendMode is ignored. A blender combines a source color (the + * result of our paint) and destination color (from the canvas) into a final color. + */ +class SK_API SkBlender : public SkFlattenable { +public: + /** + * Create a blender that implements the specified BlendMode. + */ + static sk_sp Mode(SkBlendMode mode); + +private: + SkBlender() = default; + friend class SkBlenderBase; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkBlurTypes.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkBlurTypes.h new file mode 100644 index 0000000000..f0dde10f25 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkBlurTypes.h @@ -0,0 +1,20 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkBlurTypes_DEFINED +#define SkBlurTypes_DEFINED + +enum SkBlurStyle : int { + kNormal_SkBlurStyle, //!< fuzzy inside and outside + kSolid_SkBlurStyle, //!< solid inside, fuzzy outside + kOuter_SkBlurStyle, //!< nothing inside, fuzzy outside + kInner_SkBlurStyle, //!< fuzzy inside, nothing outside + + kLastEnum_SkBlurStyle = kInner_SkBlurStyle, +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkCanvas.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkCanvas.h new file mode 100644 index 0000000000..02ad95aa19 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkCanvas.h @@ -0,0 +1,2686 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkCanvas_DEFINED +#define SkCanvas_DEFINED + +#include "include/core/SkArc.h" +#include "include/core/SkBlendMode.h" +#include "include/core/SkClipOp.h" +#include "include/core/SkColor.h" +#include "include/core/SkFontTypes.h" +#include "include/core/SkImageFilter.h" +#include "include/core/SkImageInfo.h" +#include "include/core/SkM44.h" +#include "include/core/SkMatrix.h" +#include "include/core/SkPaint.h" +#include "include/core/SkPoint.h" +#include "include/core/SkRasterHandleAllocator.h" +#include "include/core/SkRect.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkSamplingOptions.h" +#include "include/core/SkScalar.h" +#include "include/core/SkSize.h" +#include "include/core/SkSpan.h" +#include "include/core/SkString.h" +#include "include/core/SkSurfaceProps.h" +#include "include/core/SkTypes.h" +#include "include/private/base/SkCPUTypes.h" +#include "include/private/base/SkDeque.h" +#include "include/private/base/SkTArray.h" + +#include +#include +#include +#include + +#ifndef SK_SUPPORT_LEGACY_GETTOTALMATRIX +#define SK_SUPPORT_LEGACY_GETTOTALMATRIX +#endif + +namespace sktext { +class GlyphRunBuilder; +class GlyphRunList; +} + +class AutoLayerForImageFilter; +class GrRecordingContext; + +class SkBitmap; +class SkBlender; +class SkColorSpace; +class SkData; +class SkDevice; +class SkDrawable; +class SkFont; +class SkImage; +class SkMesh; +class SkPaintFilterCanvas; +class SkPath; +class SkPicture; +class SkPixmap; +class SkRRect; +class SkRegion; +class SkShader; +class SkSpecialImage; +class SkSurface; +class SkSurface_Base; +class SkTextBlob; +class SkVertices; +struct SkDrawShadowRec; +struct SkRSXform; + +template +class SkEnumBitMask; + +namespace skgpu::graphite { class Recorder; } +namespace sktext::gpu { class Slug; } +namespace SkRecords { class Draw; } + +/** \class SkCanvas + SkCanvas provides an interface for drawing, and how the drawing is clipped and transformed. + SkCanvas contains a stack of SkMatrix and clip values. + + SkCanvas and SkPaint together provide the state to draw into SkSurface or SkDevice. + Each SkCanvas draw call transforms the geometry of the object by the concatenation of all + SkMatrix values in the stack. The transformed geometry is clipped by the intersection + of all of clip values in the stack. The SkCanvas draw calls use SkPaint to supply drawing + state such as color, SkTypeface, text size, stroke width, SkShader and so on. + + To draw to a pixel-based destination, create raster surface or GPU surface. + Request SkCanvas from SkSurface to obtain the interface to draw. + SkCanvas generated by raster surface draws to memory visible to the CPU. + SkCanvas generated by GPU surface uses Vulkan or OpenGL to draw to the GPU. + + To draw to a document, obtain SkCanvas from SVG canvas, document PDF, or SkPictureRecorder. + SkDocument based SkCanvas and other SkCanvas subclasses reference SkDevice describing the + destination. + + SkCanvas can be constructed to draw to SkBitmap without first creating raster surface. + This approach may be deprecated in the future. +*/ +class SK_API SkCanvas { +public: + + /** Allocates raster SkCanvas that will draw directly into pixels. + + SkCanvas is returned if all parameters are valid. + Valid parameters include: + info dimensions are zero or positive; + info contains SkColorType and SkAlphaType supported by raster surface; + pixels is not nullptr; + rowBytes is zero or large enough to contain info width pixels of SkColorType. + + Pass zero for rowBytes to compute rowBytes from info width and size of pixel. + If rowBytes is greater than zero, it must be equal to or greater than + info width times bytes required for SkColorType. + + Pixel buffer size should be info height times computed rowBytes. + Pixels are not initialized. + To access pixels after drawing, call flush() or peekPixels(). + + @param info width, height, SkColorType, SkAlphaType, SkColorSpace, of raster surface; + width, or height, or both, may be zero + @param pixels pointer to destination pixels buffer + @param rowBytes interval from one SkSurface row to the next, or zero + @param props LCD striping orientation and setting for device independent fonts; + may be nullptr + @return SkCanvas if all parameters are valid; otherwise, nullptr + */ + static std::unique_ptr MakeRasterDirect(const SkImageInfo& info, void* pixels, + size_t rowBytes, + const SkSurfaceProps* props = nullptr); + + /** Allocates raster SkCanvas specified by inline image specification. Subsequent SkCanvas + calls draw into pixels. + SkColorType is set to kN32_SkColorType. + SkAlphaType is set to kPremul_SkAlphaType. + To access pixels after drawing, call flush() or peekPixels(). + + SkCanvas is returned if all parameters are valid. + Valid parameters include: + width and height are zero or positive; + pixels is not nullptr; + rowBytes is zero or large enough to contain width pixels of kN32_SkColorType. + + Pass zero for rowBytes to compute rowBytes from width and size of pixel. + If rowBytes is greater than zero, it must be equal to or greater than + width times bytes required for SkColorType. + + Pixel buffer size should be height times rowBytes. + + @param width pixel column count on raster surface created; must be zero or greater + @param height pixel row count on raster surface created; must be zero or greater + @param pixels pointer to destination pixels buffer; buffer size should be height + times rowBytes + @param rowBytes interval from one SkSurface row to the next, or zero + @return SkCanvas if all parameters are valid; otherwise, nullptr + */ + static std::unique_ptr MakeRasterDirectN32(int width, int height, SkPMColor* pixels, + size_t rowBytes) { + return MakeRasterDirect(SkImageInfo::MakeN32Premul(width, height), pixels, rowBytes); + } + + /** Creates an empty SkCanvas with no backing device or pixels, with + a width and height of zero. + + @return empty SkCanvas + + example: https://fiddle.skia.org/c/@Canvas_empty_constructor + */ + SkCanvas(); + + /** Creates SkCanvas of the specified dimensions without a SkSurface. + Used by subclasses with custom implementations for draw member functions. + + If props equals nullptr, SkSurfaceProps are created with + SkSurfaceProps::InitType settings, which choose the pixel striping + direction and order. Since a platform may dynamically change its direction when + the device is rotated, and since a platform may have multiple monitors with + different characteristics, it is best not to rely on this legacy behavior. + + @param width zero or greater + @param height zero or greater + @param props LCD striping orientation and setting for device independent fonts; + may be nullptr + @return SkCanvas placeholder with dimensions + + example: https://fiddle.skia.org/c/@Canvas_int_int_const_SkSurfaceProps_star + */ + SkCanvas(int width, int height, const SkSurfaceProps* props = nullptr); + + /** Private. For internal use only. + */ + explicit SkCanvas(sk_sp device); + + /** Constructs a canvas that draws into bitmap. + Sets kUnknown_SkPixelGeometry in constructed SkSurface. + + SkBitmap is copied so that subsequently editing bitmap will not affect + constructed SkCanvas. + + May be deprecated in the future. + + @param bitmap width, height, SkColorType, SkAlphaType, and pixel + storage of raster surface + @return SkCanvas that can be used to draw into bitmap + + example: https://fiddle.skia.org/c/@Canvas_copy_const_SkBitmap + */ + explicit SkCanvas(const SkBitmap& bitmap); + +#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK + /** Private. + */ + enum class ColorBehavior { + kLegacy, //!< placeholder + }; + + /** Private. For use by Android framework only. + + @param bitmap specifies a bitmap for the canvas to draw into + @param behavior specializes this constructor; value is unused + @return SkCanvas that can be used to draw into bitmap + */ + SkCanvas(const SkBitmap& bitmap, ColorBehavior behavior); +#endif + + /** Constructs a canvas that draws into bitmap. + Use props to match the device characteristics, like LCD striping. + + bitmap is copied so that subsequently editing bitmap will not affect + constructed SkCanvas. + + @param bitmap width, height, SkColorType, SkAlphaType, + and pixel storage of raster surface + @param props order and orientation of RGB striping; and whether to use + device independent fonts + @return SkCanvas that can be used to draw into bitmap + + example: https://fiddle.skia.org/c/@Canvas_const_SkBitmap_const_SkSurfaceProps + */ + SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props); + + /** Draws saved layers, if any. + Frees up resources used by SkCanvas. + + example: https://fiddle.skia.org/c/@Canvas_destructor + */ + virtual ~SkCanvas(); + + /** Returns SkImageInfo for SkCanvas. If SkCanvas is not associated with raster surface or + GPU surface, returned SkColorType is set to kUnknown_SkColorType. + + @return dimensions and SkColorType of SkCanvas + + example: https://fiddle.skia.org/c/@Canvas_imageInfo + */ + SkImageInfo imageInfo() const; + + /** Copies SkSurfaceProps, if SkCanvas is associated with raster surface or + GPU surface, and returns true. Otherwise, returns false and leave props unchanged. + + @param props storage for writable SkSurfaceProps + @return true if SkSurfaceProps was copied + + DEPRECATED: Replace usage with getBaseProps() or getTopProps() + + example: https://fiddle.skia.org/c/@Canvas_getProps + */ + bool getProps(SkSurfaceProps* props) const; + + /** Returns the SkSurfaceProps associated with the canvas (i.e., at the base of the layer + stack). + + @return base SkSurfaceProps + */ + SkSurfaceProps getBaseProps() const; + + /** Returns the SkSurfaceProps associated with the canvas that are currently active (i.e., at + the top of the layer stack). This can differ from getBaseProps depending on the flags + passed to saveLayer (see SaveLayerFlagsSet). + + @return SkSurfaceProps active in the current/top layer + */ + SkSurfaceProps getTopProps() const; + + /** Gets the size of the base or root layer in global canvas coordinates. The + origin of the base layer is always (0,0). The area available for drawing may be + smaller (due to clipping or saveLayer). + + @return integral width and height of base layer + + example: https://fiddle.skia.org/c/@Canvas_getBaseLayerSize + */ + virtual SkISize getBaseLayerSize() const; + + /** Creates SkSurface matching info and props, and associates it with SkCanvas. + Returns nullptr if no match found. + + If props is nullptr, matches SkSurfaceProps in SkCanvas. If props is nullptr and SkCanvas + does not have SkSurfaceProps, creates SkSurface with default SkSurfaceProps. + + @param info width, height, SkColorType, SkAlphaType, and SkColorSpace + @param props SkSurfaceProps to match; may be nullptr to match SkCanvas + @return SkSurface matching info and props, or nullptr if no match is available + + example: https://fiddle.skia.org/c/@Canvas_makeSurface + */ + sk_sp makeSurface(const SkImageInfo& info, const SkSurfaceProps* props = nullptr); + + /** Returns Ganesh context of the GPU surface associated with SkCanvas. + + @return GPU context, if available; nullptr otherwise + + example: https://fiddle.skia.org/c/@Canvas_recordingContext + */ + virtual GrRecordingContext* recordingContext() const; + + + /** Returns Recorder for the GPU surface associated with SkCanvas. + + @return Recorder, if available; nullptr otherwise + */ + virtual skgpu::graphite::Recorder* recorder() const; + + /** Sometimes a canvas is owned by a surface. If it is, getSurface() will return a bare + * pointer to that surface, else this will return nullptr. + */ + SkSurface* getSurface() const; + + /** Returns the pixel base address, SkImageInfo, rowBytes, and origin if the pixels + can be read directly. The returned address is only valid + while SkCanvas is in scope and unchanged. Any SkCanvas call or SkSurface call + may invalidate the returned address and other returned values. + + If pixels are inaccessible, info, rowBytes, and origin are unchanged. + + @param info storage for writable pixels' SkImageInfo; may be nullptr + @param rowBytes storage for writable pixels' row bytes; may be nullptr + @param origin storage for SkCanvas top layer origin, its top-left corner; + may be nullptr + @return address of pixels, or nullptr if inaccessible + + example: https://fiddle.skia.org/c/@Canvas_accessTopLayerPixels_a + example: https://fiddle.skia.org/c/@Canvas_accessTopLayerPixels_b + */ + void* accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin = nullptr); + + /** Returns custom context that tracks the SkMatrix and clip. + + Use SkRasterHandleAllocator to blend Skia drawing with custom drawing, typically performed + by the host platform user interface. The custom context returned is generated by + SkRasterHandleAllocator::MakeCanvas, which creates a custom canvas with raster storage for + the drawing destination. + + @return context of custom allocation + + example: https://fiddle.skia.org/c/@Canvas_accessTopRasterHandle + */ + SkRasterHandleAllocator::Handle accessTopRasterHandle() const; + + /** Returns true if SkCanvas has direct access to its pixels. + + Pixels are readable when SkDevice is raster. Pixels are not readable when SkCanvas + is returned from GPU surface, returned by SkDocument::beginPage, returned by + SkPictureRecorder::beginRecording, or SkCanvas is the base of a utility class + like DebugCanvas. + + pixmap is valid only while SkCanvas is in scope and unchanged. Any + SkCanvas or SkSurface call may invalidate the pixmap values. + + @param pixmap storage for pixel state if pixels are readable; otherwise, ignored + @return true if SkCanvas has direct access to pixels + + example: https://fiddle.skia.org/c/@Canvas_peekPixels + */ + bool peekPixels(SkPixmap* pixmap); + + /** Copies SkRect of pixels from SkCanvas into dstPixels. SkMatrix and clip are + ignored. + + Source SkRect corners are (srcX, srcY) and (imageInfo().width(), imageInfo().height()). + Destination SkRect corners are (0, 0) and (dstInfo.width(), dstInfo.height()). + Copies each readable pixel intersecting both rectangles, without scaling, + converting to dstInfo.colorType() and dstInfo.alphaType() if required. + + Pixels are readable when SkDevice is raster, or backed by a GPU. + Pixels are not readable when SkCanvas is returned by SkDocument::beginPage, + returned by SkPictureRecorder::beginRecording, or SkCanvas is the base of a utility + class like DebugCanvas. + + The destination pixel storage must be allocated by the caller. + + Pixel values are converted only if SkColorType and SkAlphaType + do not match. Only pixels within both source and destination rectangles + are copied. dstPixels contents outside SkRect intersection are unchanged. + + Pass negative values for srcX or srcY to offset pixels across or down destination. + + Does not copy, and returns false if: + - Source and destination rectangles do not intersect. + - SkCanvas pixels could not be converted to dstInfo.colorType() or dstInfo.alphaType(). + - SkCanvas pixels are not readable; for instance, SkCanvas is document-based. + - dstRowBytes is too small to contain one row of pixels. + + @param dstInfo width, height, SkColorType, and SkAlphaType of dstPixels + @param dstPixels storage for pixels; dstInfo.height() times dstRowBytes, or larger + @param dstRowBytes size of one destination row; dstInfo.width() times pixel size, or larger + @param srcX offset into readable pixels on x-axis; may be negative + @param srcY offset into readable pixels on y-axis; may be negative + @return true if pixels were copied + */ + bool readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes, + int srcX, int srcY); + + /** Copies SkRect of pixels from SkCanvas into pixmap. SkMatrix and clip are + ignored. + + Source SkRect corners are (srcX, srcY) and (imageInfo().width(), imageInfo().height()). + Destination SkRect corners are (0, 0) and (pixmap.width(), pixmap.height()). + Copies each readable pixel intersecting both rectangles, without scaling, + converting to pixmap.colorType() and pixmap.alphaType() if required. + + Pixels are readable when SkDevice is raster, or backed by a GPU. + Pixels are not readable when SkCanvas is returned by SkDocument::beginPage, + returned by SkPictureRecorder::beginRecording, or SkCanvas is the base of a utility + class like DebugCanvas. + + Caller must allocate pixel storage in pixmap if needed. + + Pixel values are converted only if SkColorType and SkAlphaType + do not match. Only pixels within both source and destination SkRect + are copied. pixmap pixels contents outside SkRect intersection are unchanged. + + Pass negative values for srcX or srcY to offset pixels across or down pixmap. + + Does not copy, and returns false if: + - Source and destination rectangles do not intersect. + - SkCanvas pixels could not be converted to pixmap.colorType() or pixmap.alphaType(). + - SkCanvas pixels are not readable; for instance, SkCanvas is document-based. + - SkPixmap pixels could not be allocated. + - pixmap.rowBytes() is too small to contain one row of pixels. + + @param pixmap storage for pixels copied from SkCanvas + @param srcX offset into readable pixels on x-axis; may be negative + @param srcY offset into readable pixels on y-axis; may be negative + @return true if pixels were copied + + example: https://fiddle.skia.org/c/@Canvas_readPixels_2 + */ + bool readPixels(const SkPixmap& pixmap, int srcX, int srcY); + + /** Copies SkRect of pixels from SkCanvas into bitmap. SkMatrix and clip are + ignored. + + Source SkRect corners are (srcX, srcY) and (imageInfo().width(), imageInfo().height()). + Destination SkRect corners are (0, 0) and (bitmap.width(), bitmap.height()). + Copies each readable pixel intersecting both rectangles, without scaling, + converting to bitmap.colorType() and bitmap.alphaType() if required. + + Pixels are readable when SkDevice is raster, or backed by a GPU. + Pixels are not readable when SkCanvas is returned by SkDocument::beginPage, + returned by SkPictureRecorder::beginRecording, or SkCanvas is the base of a utility + class like DebugCanvas. + + Caller must allocate pixel storage in bitmap if needed. + + SkBitmap values are converted only if SkColorType and SkAlphaType + do not match. Only pixels within both source and destination rectangles + are copied. SkBitmap pixels outside SkRect intersection are unchanged. + + Pass negative values for srcX or srcY to offset pixels across or down bitmap. + + Does not copy, and returns false if: + - Source and destination rectangles do not intersect. + - SkCanvas pixels could not be converted to bitmap.colorType() or bitmap.alphaType(). + - SkCanvas pixels are not readable; for instance, SkCanvas is document-based. + - bitmap pixels could not be allocated. + - bitmap.rowBytes() is too small to contain one row of pixels. + + @param bitmap storage for pixels copied from SkCanvas + @param srcX offset into readable pixels on x-axis; may be negative + @param srcY offset into readable pixels on y-axis; may be negative + @return true if pixels were copied + + example: https://fiddle.skia.org/c/@Canvas_readPixels_3 + */ + bool readPixels(const SkBitmap& bitmap, int srcX, int srcY); + + /** Copies SkRect from pixels to SkCanvas. SkMatrix and clip are ignored. + Source SkRect corners are (0, 0) and (info.width(), info.height()). + Destination SkRect corners are (x, y) and + (imageInfo().width(), imageInfo().height()). + + Copies each readable pixel intersecting both rectangles, without scaling, + converting to imageInfo().colorType() and imageInfo().alphaType() if required. + + Pixels are writable when SkDevice is raster, or backed by a GPU. + Pixels are not writable when SkCanvas is returned by SkDocument::beginPage, + returned by SkPictureRecorder::beginRecording, or SkCanvas is the base of a utility + class like DebugCanvas. + + Pixel values are converted only if SkColorType and SkAlphaType + do not match. Only pixels within both source and destination rectangles + are copied. SkCanvas pixels outside SkRect intersection are unchanged. + + Pass negative values for x or y to offset pixels to the left or + above SkCanvas pixels. + + Does not copy, and returns false if: + - Source and destination rectangles do not intersect. + - pixels could not be converted to SkCanvas imageInfo().colorType() or + imageInfo().alphaType(). + - SkCanvas pixels are not writable; for instance, SkCanvas is document-based. + - rowBytes is too small to contain one row of pixels. + + @param info width, height, SkColorType, and SkAlphaType of pixels + @param pixels pixels to copy, of size info.height() times rowBytes, or larger + @param rowBytes size of one row of pixels; info.width() times pixel size, or larger + @param x offset into SkCanvas writable pixels on x-axis; may be negative + @param y offset into SkCanvas writable pixels on y-axis; may be negative + @return true if pixels were written to SkCanvas + + example: https://fiddle.skia.org/c/@Canvas_writePixels + */ + bool writePixels(const SkImageInfo& info, const void* pixels, size_t rowBytes, int x, int y); + + /** Copies SkRect from pixels to SkCanvas. SkMatrix and clip are ignored. + Source SkRect corners are (0, 0) and (bitmap.width(), bitmap.height()). + + Destination SkRect corners are (x, y) and + (imageInfo().width(), imageInfo().height()). + + Copies each readable pixel intersecting both rectangles, without scaling, + converting to imageInfo().colorType() and imageInfo().alphaType() if required. + + Pixels are writable when SkDevice is raster, or backed by a GPU. + Pixels are not writable when SkCanvas is returned by SkDocument::beginPage, + returned by SkPictureRecorder::beginRecording, or SkCanvas is the base of a utility + class like DebugCanvas. + + Pixel values are converted only if SkColorType and SkAlphaType + do not match. Only pixels within both source and destination rectangles + are copied. SkCanvas pixels outside SkRect intersection are unchanged. + + Pass negative values for x or y to offset pixels to the left or + above SkCanvas pixels. + + Does not copy, and returns false if: + - Source and destination rectangles do not intersect. + - bitmap does not have allocated pixels. + - bitmap pixels could not be converted to SkCanvas imageInfo().colorType() or + imageInfo().alphaType(). + - SkCanvas pixels are not writable; for instance, SkCanvas is document based. + - bitmap pixels are inaccessible; for instance, bitmap wraps a texture. + + @param bitmap contains pixels copied to SkCanvas + @param x offset into SkCanvas writable pixels on x-axis; may be negative + @param y offset into SkCanvas writable pixels on y-axis; may be negative + @return true if pixels were written to SkCanvas + + example: https://fiddle.skia.org/c/@Canvas_writePixels_2 + example: https://fiddle.skia.org/c/@State_Stack_a + example: https://fiddle.skia.org/c/@State_Stack_b + */ + bool writePixels(const SkBitmap& bitmap, int x, int y); + + /** Saves SkMatrix and clip. + Calling restore() discards changes to SkMatrix and clip, + restoring the SkMatrix and clip to their state when save() was called. + + SkMatrix may be changed by translate(), scale(), rotate(), skew(), concat(), setMatrix(), + and resetMatrix(). Clip may be changed by clipRect(), clipRRect(), clipPath(), clipRegion(). + + Saved SkCanvas state is put on a stack; multiple calls to save() should be balance + by an equal number of calls to restore(). + + Call restoreToCount() with result to restore this and subsequent saves. + + @return depth of saved stack + + example: https://fiddle.skia.org/c/@Canvas_save + */ + int save(); + + /** Saves SkMatrix and clip, and allocates a SkSurface for subsequent drawing. + Calling restore() discards changes to SkMatrix and clip, and draws the SkSurface. + + SkMatrix may be changed by translate(), scale(), rotate(), skew(), concat(), + setMatrix(), and resetMatrix(). Clip may be changed by clipRect(), clipRRect(), + clipPath(), clipRegion(). + + SkRect bounds suggests but does not define the SkSurface size. To clip drawing to + a specific rectangle, use clipRect(). + + Optional SkPaint paint applies alpha, SkColorFilter, SkImageFilter, and + SkBlendMode when restore() is called. + + Call restoreToCount() with returned value to restore this and subsequent saves. + + @param bounds hint to limit the size of the layer; may be nullptr + @param paint graphics state for layer; may be nullptr + @return depth of saved stack + + example: https://fiddle.skia.org/c/@Canvas_saveLayer + example: https://fiddle.skia.org/c/@Canvas_saveLayer_4 + */ + int saveLayer(const SkRect* bounds, const SkPaint* paint); + + /** Saves SkMatrix and clip, and allocates a SkSurface for subsequent drawing. + Calling restore() discards changes to SkMatrix and clip, and draws the SkSurface. + + SkMatrix may be changed by translate(), scale(), rotate(), skew(), concat(), + setMatrix(), and resetMatrix(). Clip may be changed by clipRect(), clipRRect(), + clipPath(), clipRegion(). + + SkRect bounds suggests but does not define the layer size. To clip drawing to + a specific rectangle, use clipRect(). + + Optional SkPaint paint applies alpha, SkColorFilter, SkImageFilter, and + SkBlendMode when restore() is called. + + Call restoreToCount() with returned value to restore this and subsequent saves. + + @param bounds hint to limit the size of layer; may be nullptr + @param paint graphics state for layer; may be nullptr + @return depth of saved stack + */ + int saveLayer(const SkRect& bounds, const SkPaint* paint) { + return this->saveLayer(&bounds, paint); + } + + /** Saves SkMatrix and clip, and allocates SkSurface for subsequent drawing. + + Calling restore() discards changes to SkMatrix and clip, + and blends layer with alpha opacity onto prior layer. + + SkMatrix may be changed by translate(), scale(), rotate(), skew(), concat(), + setMatrix(), and resetMatrix(). Clip may be changed by clipRect(), clipRRect(), + clipPath(), clipRegion(). + + SkRect bounds suggests but does not define layer size. To clip drawing to + a specific rectangle, use clipRect(). + + alpha of zero is fully transparent, 1.0f is fully opaque. + + Call restoreToCount() with returned value to restore this and subsequent saves. + + @param bounds hint to limit the size of layer; may be nullptr + @param alpha opacity of layer + @return depth of saved stack + + example: https://fiddle.skia.org/c/@Canvas_saveLayerAlpha + */ + int saveLayerAlphaf(const SkRect* bounds, float alpha); + // Helper that accepts an int between 0 and 255, and divides it by 255.0 + int saveLayerAlpha(const SkRect* bounds, U8CPU alpha) { + return this->saveLayerAlphaf(bounds, alpha * (1.0f / 255)); + } + + /** \enum SkCanvas::SaveLayerFlagsSet + SaveLayerFlags provides options that may be used in any combination in SaveLayerRec, + defining how layer allocated by saveLayer() operates. It may be set to zero, + kPreserveLCDText_SaveLayerFlag, kInitWithPrevious_SaveLayerFlag, or both flags. + */ + enum SaveLayerFlagsSet { + kPreserveLCDText_SaveLayerFlag = 1 << 1, + kInitWithPrevious_SaveLayerFlag = 1 << 2, //!< initializes with previous contents + // instead of matching previous layer's colortype, use F16 + kF16ColorType = 1 << 4, + }; + + using SaveLayerFlags = uint32_t; + using FilterSpan = SkSpan>; + static constexpr int kMaxFiltersPerLayer = 16; + + /** \struct SkCanvas::SaveLayerRec + SaveLayerRec contains the state used to create the layer. + */ + struct SaveLayerRec { + /** Sets fBounds, fPaint, and fBackdrop to nullptr. Clears fSaveLayerFlags. + + @return empty SaveLayerRec + */ + SaveLayerRec() {} + + /** Sets fBounds, fPaint, and fSaveLayerFlags; sets fBackdrop to nullptr. + + @param bounds layer dimensions; may be nullptr + @param paint applied to layer when overlaying prior layer; may be nullptr + @param saveLayerFlags SaveLayerRec options to modify layer + @return SaveLayerRec with empty fBackdrop + */ + SaveLayerRec(const SkRect* bounds, const SkPaint* paint, SaveLayerFlags saveLayerFlags = 0) + : SaveLayerRec(bounds, paint, nullptr, nullptr, 1.f, saveLayerFlags, /*filters=*/{}) {} + + /** Sets fBounds, fPaint, fBackdrop, and fSaveLayerFlags. + + @param bounds layer dimensions; may be nullptr + @param paint applied to layer when overlaying prior layer; + may be nullptr + @param backdrop If not null, this causes the current layer to be filtered by + backdrop, and then drawn into the new layer + (respecting the current clip). + If null, the new layer is initialized with transparent-black. + @param saveLayerFlags SaveLayerRec options to modify layer + @return SaveLayerRec fully specified + */ + SaveLayerRec(const SkRect* bounds, const SkPaint* paint, const SkImageFilter* backdrop, + SaveLayerFlags saveLayerFlags) + : SaveLayerRec(bounds, paint, backdrop, nullptr, 1.f, saveLayerFlags, /*filters=*/{}) {} + + /** Sets fBounds, fColorSpace, and fSaveLayerFlags. + + @param bounds layer dimensions; may be nullptr + @param paint applied to layer when overlaying prior layer; + may be nullptr + @param backdrop If not null, this causes the current layer to be filtered by + backdrop, and then drawn into the new layer + (respecting the current clip). + If null, the new layer is initialized with transparent-black. + @param colorSpace If not null, when the layer is restored, a color space + conversion will be applied from this color space to the + parent's color space. The restore paint and backdrop filters will + be applied in this color space. + If null, the new layer will inherit the color space from its + parent. + @param saveLayerFlags SaveLayerRec options to modify layer + @return SaveLayerRec fully specified + */ + SaveLayerRec(const SkRect* bounds, const SkPaint* paint, const SkImageFilter* backdrop, + const SkColorSpace* colorSpace, SaveLayerFlags saveLayerFlags) + : SaveLayerRec(bounds, paint, backdrop, colorSpace, 1.f, saveLayerFlags, /*filters=*/{}) {} + + /** hints at layer size limit */ + const SkRect* fBounds = nullptr; + + /** modifies overlay */ + const SkPaint* fPaint = nullptr; + + FilterSpan fFilters = {}; + + /** + * If not null, this triggers the same initialization behavior as setting + * kInitWithPrevious_SaveLayerFlag on fSaveLayerFlags: the current layer is copied into + * the new layer, rather than initializing the new layer with transparent-black. + * This is then filtered by fBackdrop (respecting the current clip). + */ + const SkImageFilter* fBackdrop = nullptr; + + /** + * If not null, this triggers a color space conversion when the layer is restored. It + * will be as if the layer's contents are drawn in this color space. Filters from + * fBackdrop and fPaint will be applied in this color space. + */ + const SkColorSpace* fColorSpace = nullptr; + + /** preserves LCD text, creates with prior layer contents */ + SaveLayerFlags fSaveLayerFlags = 0; + + private: + friend class SkCanvas; + friend class SkCanvasPriv; + + SaveLayerRec(const SkRect* bounds, + const SkPaint* paint, + const SkImageFilter* backdrop, + const SkColorSpace* colorSpace, + SkScalar backdropScale, + SaveLayerFlags saveLayerFlags, + FilterSpan filters) + : fBounds(bounds) + , fPaint(paint) + , fFilters(filters) + , fBackdrop(backdrop) + , fColorSpace(colorSpace) + , fSaveLayerFlags(saveLayerFlags) + , fExperimentalBackdropScale(backdropScale) { + // We only allow the paint's image filter or the side-car list of filters -- not both. + SkASSERT(fFilters.empty() || !paint || !paint->getImageFilter()); + // To keep things reasonable (during deserialization), we limit filter list size. + SkASSERT(fFilters.size() <= kMaxFiltersPerLayer); + } + + // Relative scale factor that the image content used to initialize the layer when the + // kInitFromPrevious flag or a backdrop filter is used. + SkScalar fExperimentalBackdropScale = 1.f; + }; + + /** Saves SkMatrix and clip, and allocates SkSurface for subsequent drawing. + + Calling restore() discards changes to SkMatrix and clip, + and blends SkSurface with alpha opacity onto the prior layer. + + SkMatrix may be changed by translate(), scale(), rotate(), skew(), concat(), + setMatrix(), and resetMatrix(). Clip may be changed by clipRect(), clipRRect(), + clipPath(), clipRegion(). + + SaveLayerRec contains the state used to create the layer. + + Call restoreToCount() with returned value to restore this and subsequent saves. + + @param layerRec layer state + @return depth of save state stack before this call was made. + + example: https://fiddle.skia.org/c/@Canvas_saveLayer_3 + */ + int saveLayer(const SaveLayerRec& layerRec); + + /** Removes changes to SkMatrix and clip since SkCanvas state was + last saved. The state is removed from the stack. + + Does nothing if the stack is empty. + + example: https://fiddle.skia.org/c/@AutoCanvasRestore_restore + + example: https://fiddle.skia.org/c/@Canvas_restore + */ + void restore(); + + /** Returns the number of saved states, each containing: SkMatrix and clip. + Equals the number of save() calls less the number of restore() calls plus one. + The save count of a new canvas is one. + + @return depth of save state stack + + example: https://fiddle.skia.org/c/@Canvas_getSaveCount + */ + int getSaveCount() const; + + /** Restores state to SkMatrix and clip values when save(), saveLayer(), + saveLayerPreserveLCDTextRequests(), or saveLayerAlpha() returned saveCount. + + Does nothing if saveCount is greater than state stack count. + Restores state to initial values if saveCount is less than or equal to one. + + @param saveCount depth of state stack to restore + + example: https://fiddle.skia.org/c/@Canvas_restoreToCount + */ + void restoreToCount(int saveCount); + + /** Translates SkMatrix by dx along the x-axis and dy along the y-axis. + + Mathematically, replaces SkMatrix with a translation matrix + premultiplied with SkMatrix. + + This has the effect of moving the drawing by (dx, dy) before transforming + the result with SkMatrix. + + @param dx distance to translate on x-axis + @param dy distance to translate on y-axis + + example: https://fiddle.skia.org/c/@Canvas_translate + */ + void translate(SkScalar dx, SkScalar dy); + + /** Scales SkMatrix by sx on the x-axis and sy on the y-axis. + + Mathematically, replaces SkMatrix with a scale matrix + premultiplied with SkMatrix. + + This has the effect of scaling the drawing by (sx, sy) before transforming + the result with SkMatrix. + + @param sx amount to scale on x-axis + @param sy amount to scale on y-axis + + example: https://fiddle.skia.org/c/@Canvas_scale + */ + void scale(SkScalar sx, SkScalar sy); + + /** Rotates SkMatrix by degrees. Positive degrees rotates clockwise. + + Mathematically, replaces SkMatrix with a rotation matrix + premultiplied with SkMatrix. + + This has the effect of rotating the drawing by degrees before transforming + the result with SkMatrix. + + @param degrees amount to rotate, in degrees + + example: https://fiddle.skia.org/c/@Canvas_rotate + */ + void rotate(SkScalar degrees); + + /** Rotates SkMatrix by degrees about a point at (px, py). Positive degrees rotates + clockwise. + + Mathematically, constructs a rotation matrix; premultiplies the rotation matrix by + a translation matrix; then replaces SkMatrix with the resulting matrix + premultiplied with SkMatrix. + + This has the effect of rotating the drawing about a given point before + transforming the result with SkMatrix. + + @param degrees amount to rotate, in degrees + @param px x-axis value of the point to rotate about + @param py y-axis value of the point to rotate about + + example: https://fiddle.skia.org/c/@Canvas_rotate_2 + */ + void rotate(SkScalar degrees, SkScalar px, SkScalar py); + + /** Skews SkMatrix by sx on the x-axis and sy on the y-axis. A positive value of sx + skews the drawing right as y-axis values increase; a positive value of sy skews + the drawing down as x-axis values increase. + + Mathematically, replaces SkMatrix with a skew matrix premultiplied with SkMatrix. + + This has the effect of skewing the drawing by (sx, sy) before transforming + the result with SkMatrix. + + @param sx amount to skew on x-axis + @param sy amount to skew on y-axis + + example: https://fiddle.skia.org/c/@Canvas_skew + */ + void skew(SkScalar sx, SkScalar sy); + + /** Replaces SkMatrix with matrix premultiplied with existing SkMatrix. + + This has the effect of transforming the drawn geometry by matrix, before + transforming the result with existing SkMatrix. + + @param matrix matrix to premultiply with existing SkMatrix + + example: https://fiddle.skia.org/c/@Canvas_concat + */ + void concat(const SkMatrix& matrix); + void concat(const SkM44&); + + /** Replaces SkMatrix with matrix. + Unlike concat(), any prior matrix state is overwritten. + + @param matrix matrix to copy, replacing existing SkMatrix + + example: https://fiddle.skia.org/c/@Canvas_setMatrix + */ + void setMatrix(const SkM44& matrix); + + // DEPRECATED -- use SkM44 version + void setMatrix(const SkMatrix& matrix); + + /** Sets SkMatrix to the identity matrix. + Any prior matrix state is overwritten. + + example: https://fiddle.skia.org/c/@Canvas_resetMatrix + */ + void resetMatrix(); + + /** Replaces clip with the intersection or difference of clip and rect, + with an aliased or anti-aliased clip edge. rect is transformed by SkMatrix + before it is combined with clip. + + @param rect SkRect to combine with clip + @param op SkClipOp to apply to clip + @param doAntiAlias true if clip is to be anti-aliased + + example: https://fiddle.skia.org/c/@Canvas_clipRect + */ + void clipRect(const SkRect& rect, SkClipOp op, bool doAntiAlias); + + /** Replaces clip with the intersection or difference of clip and rect. + Resulting clip is aliased; pixels are fully contained by the clip. + rect is transformed by SkMatrix before it is combined with clip. + + @param rect SkRect to combine with clip + @param op SkClipOp to apply to clip + */ + void clipRect(const SkRect& rect, SkClipOp op) { + this->clipRect(rect, op, false); + } + + /** Replaces clip with the intersection of clip and rect. + Resulting clip is aliased; pixels are fully contained by the clip. + rect is transformed by SkMatrix + before it is combined with clip. + + @param rect SkRect to combine with clip + @param doAntiAlias true if clip is to be anti-aliased + */ + void clipRect(const SkRect& rect, bool doAntiAlias = false) { + this->clipRect(rect, SkClipOp::kIntersect, doAntiAlias); + } + + void clipIRect(const SkIRect& irect, SkClipOp op = SkClipOp::kIntersect) { + this->clipRect(SkRect::Make(irect), op, false); + } + + /** Sets the maximum clip rectangle, which can be set by clipRect(), clipRRect() and + clipPath() and intersect the current clip with the specified rect. + The maximum clip affects only future clipping operations; it is not retroactive. + The clip restriction is not recorded in pictures. + + Pass an empty rect to disable maximum clip. + This private API is for use by Android framework only. + + DEPRECATED: Replace usage with SkAndroidFrameworkUtils::replaceClip() + + @param rect maximum allowed clip in device coordinates + */ + void androidFramework_setDeviceClipRestriction(const SkIRect& rect); + + /** Replaces clip with the intersection or difference of clip and rrect, + with an aliased or anti-aliased clip edge. + rrect is transformed by SkMatrix + before it is combined with clip. + + @param rrect SkRRect to combine with clip + @param op SkClipOp to apply to clip + @param doAntiAlias true if clip is to be anti-aliased + + example: https://fiddle.skia.org/c/@Canvas_clipRRect + */ + void clipRRect(const SkRRect& rrect, SkClipOp op, bool doAntiAlias); + + /** Replaces clip with the intersection or difference of clip and rrect. + Resulting clip is aliased; pixels are fully contained by the clip. + rrect is transformed by SkMatrix before it is combined with clip. + + @param rrect SkRRect to combine with clip + @param op SkClipOp to apply to clip + */ + void clipRRect(const SkRRect& rrect, SkClipOp op) { + this->clipRRect(rrect, op, false); + } + + /** Replaces clip with the intersection of clip and rrect, + with an aliased or anti-aliased clip edge. + rrect is transformed by SkMatrix before it is combined with clip. + + @param rrect SkRRect to combine with clip + @param doAntiAlias true if clip is to be anti-aliased + */ + void clipRRect(const SkRRect& rrect, bool doAntiAlias = false) { + this->clipRRect(rrect, SkClipOp::kIntersect, doAntiAlias); + } + + /** Replaces clip with the intersection or difference of clip and path, + with an aliased or anti-aliased clip edge. SkPath::FillType determines if path + describes the area inside or outside its contours; and if path contour overlaps + itself or another path contour, whether the overlaps form part of the area. + path is transformed by SkMatrix before it is combined with clip. + + @param path SkPath to combine with clip + @param op SkClipOp to apply to clip + @param doAntiAlias true if clip is to be anti-aliased + + example: https://fiddle.skia.org/c/@Canvas_clipPath + */ + void clipPath(const SkPath& path, SkClipOp op, bool doAntiAlias); + + /** Replaces clip with the intersection or difference of clip and path. + Resulting clip is aliased; pixels are fully contained by the clip. + SkPath::FillType determines if path + describes the area inside or outside its contours; and if path contour overlaps + itself or another path contour, whether the overlaps form part of the area. + path is transformed by SkMatrix + before it is combined with clip. + + @param path SkPath to combine with clip + @param op SkClipOp to apply to clip + */ + void clipPath(const SkPath& path, SkClipOp op) { + this->clipPath(path, op, false); + } + + /** Replaces clip with the intersection of clip and path. + Resulting clip is aliased; pixels are fully contained by the clip. + SkPath::FillType determines if path + describes the area inside or outside its contours; and if path contour overlaps + itself or another path contour, whether the overlaps form part of the area. + path is transformed by SkMatrix before it is combined with clip. + + @param path SkPath to combine with clip + @param doAntiAlias true if clip is to be anti-aliased + */ + void clipPath(const SkPath& path, bool doAntiAlias = false) { + this->clipPath(path, SkClipOp::kIntersect, doAntiAlias); + } + + void clipShader(sk_sp, SkClipOp = SkClipOp::kIntersect); + + /** Replaces clip with the intersection or difference of clip and SkRegion deviceRgn. + Resulting clip is aliased; pixels are fully contained by the clip. + deviceRgn is unaffected by SkMatrix. + + @param deviceRgn SkRegion to combine with clip + @param op SkClipOp to apply to clip + + example: https://fiddle.skia.org/c/@Canvas_clipRegion + */ + void clipRegion(const SkRegion& deviceRgn, SkClipOp op = SkClipOp::kIntersect); + + /** Returns true if SkRect rect, transformed by SkMatrix, can be quickly determined to be + outside of clip. May return false even though rect is outside of clip. + + Use to check if an area to be drawn is clipped out, to skip subsequent draw calls. + + @param rect SkRect to compare with clip + @return true if rect, transformed by SkMatrix, does not intersect clip + + example: https://fiddle.skia.org/c/@Canvas_quickReject + */ + bool quickReject(const SkRect& rect) const; + + /** Returns true if path, transformed by SkMatrix, can be quickly determined to be + outside of clip. May return false even though path is outside of clip. + + Use to check if an area to be drawn is clipped out, to skip subsequent draw calls. + + @param path SkPath to compare with clip + @return true if path, transformed by SkMatrix, does not intersect clip + + example: https://fiddle.skia.org/c/@Canvas_quickReject_2 + */ + bool quickReject(const SkPath& path) const; + + /** Returns bounds of clip, transformed by inverse of SkMatrix. If clip is empty, + return SkRect::MakeEmpty, where all SkRect sides equal zero. + + SkRect returned is outset by one to account for partial pixel coverage if clip + is anti-aliased. + + @return bounds of clip in local coordinates + + example: https://fiddle.skia.org/c/@Canvas_getLocalClipBounds + */ + SkRect getLocalClipBounds() const; + + /** Returns bounds of clip, transformed by inverse of SkMatrix. If clip is empty, + return false, and set bounds to SkRect::MakeEmpty, where all SkRect sides equal zero. + + bounds is outset by one to account for partial pixel coverage if clip + is anti-aliased. + + @param bounds SkRect of clip in local coordinates + @return true if clip bounds is not empty + */ + bool getLocalClipBounds(SkRect* bounds) const { + *bounds = this->getLocalClipBounds(); + return !bounds->isEmpty(); + } + + /** Returns SkIRect bounds of clip, unaffected by SkMatrix. If clip is empty, + return SkRect::MakeEmpty, where all SkRect sides equal zero. + + Unlike getLocalClipBounds(), returned SkIRect is not outset. + + @return bounds of clip in base device coordinates + + example: https://fiddle.skia.org/c/@Canvas_getDeviceClipBounds + */ + SkIRect getDeviceClipBounds() const; + + /** Returns SkIRect bounds of clip, unaffected by SkMatrix. If clip is empty, + return false, and set bounds to SkRect::MakeEmpty, where all SkRect sides equal zero. + + Unlike getLocalClipBounds(), bounds is not outset. + + @param bounds SkRect of clip in device coordinates + @return true if clip bounds is not empty + */ + bool getDeviceClipBounds(SkIRect* bounds) const { + *bounds = this->getDeviceClipBounds(); + return !bounds->isEmpty(); + } + + /** Fills clip with color color. + mode determines how ARGB is combined with destination. + + @param color unpremultiplied ARGB + @param mode SkBlendMode used to combine source color and destination + + example: https://fiddle.skia.org/c/@Canvas_drawColor + */ + void drawColor(SkColor color, SkBlendMode mode = SkBlendMode::kSrcOver) { + this->drawColor(SkColor4f::FromColor(color), mode); + } + + /** Fills clip with color color. + mode determines how ARGB is combined with destination. + + @param color SkColor4f representing unpremultiplied color. + @param mode SkBlendMode used to combine source color and destination + */ + void drawColor(const SkColor4f& color, SkBlendMode mode = SkBlendMode::kSrcOver); + + /** Fills clip with color color using SkBlendMode::kSrc. + This has the effect of replacing all pixels contained by clip with color. + + @param color unpremultiplied ARGB + */ + void clear(SkColor color) { + this->clear(SkColor4f::FromColor(color)); + } + + /** Fills clip with color color using SkBlendMode::kSrc. + This has the effect of replacing all pixels contained by clip with color. + + @param color SkColor4f representing unpremultiplied color. + */ + void clear(const SkColor4f& color) { + this->drawColor(color, SkBlendMode::kSrc); + } + + /** Makes SkCanvas contents undefined. Subsequent calls that read SkCanvas pixels, + such as drawing with SkBlendMode, return undefined results. discard() does + not change clip or SkMatrix. + + discard() may do nothing, depending on the implementation of SkSurface or SkDevice + that created SkCanvas. + + discard() allows optimized performance on subsequent draws by removing + cached data associated with SkSurface or SkDevice. + It is not necessary to call discard() once done with SkCanvas; + any cached data is deleted when owning SkSurface or SkDevice is deleted. + */ + void discard() { this->onDiscard(); } + + /** Fills clip with SkPaint paint. SkPaint components, SkShader, + SkColorFilter, SkImageFilter, and SkBlendMode affect drawing; + SkMaskFilter and SkPathEffect in paint are ignored. + + @param paint graphics state used to fill SkCanvas + + example: https://fiddle.skia.org/c/@Canvas_drawPaint + */ + void drawPaint(const SkPaint& paint); + + /** \enum SkCanvas::PointMode + Selects if an array of points are drawn as discrete points, as lines, or as + an open polygon. + */ + enum PointMode { + kPoints_PointMode, //!< draw each point separately + kLines_PointMode, //!< draw each pair of points as a line segment + kPolygon_PointMode, //!< draw the array of points as a open polygon + }; + + /** Draws pts using clip, SkMatrix and SkPaint paint. + count is the number of points; if count is less than one, has no effect. + mode may be one of: kPoints_PointMode, kLines_PointMode, or kPolygon_PointMode. + + If mode is kPoints_PointMode, the shape of point drawn depends on paint + SkPaint::Cap. If paint is set to SkPaint::kRound_Cap, each point draws a + circle of diameter SkPaint stroke width. If paint is set to SkPaint::kSquare_Cap + or SkPaint::kButt_Cap, each point draws a square of width and height + SkPaint stroke width. + + If mode is kLines_PointMode, each pair of points draws a line segment. + One line is drawn for every two points; each point is used once. If count is odd, + the final point is ignored. + + If mode is kPolygon_PointMode, each adjacent pair of points draws a line segment. + count minus one lines are drawn; the first and last point are used once. + + Each line segment respects paint SkPaint::Cap and SkPaint stroke width. + SkPaint::Style is ignored, as if were set to SkPaint::kStroke_Style. + + Always draws each element one at a time; is not affected by + SkPaint::Join, and unlike drawPath(), does not create a mask from all points + and lines before drawing. + + @param mode whether pts draws points or lines + @param count number of points in the array + @param pts array of points to draw + @param paint stroke, blend, color, and so on, used to draw + + example: https://fiddle.skia.org/c/@Canvas_drawPoints + */ + void drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint); + + /** Draws point at (x, y) using clip, SkMatrix and SkPaint paint. + + The shape of point drawn depends on paint SkPaint::Cap. + If paint is set to SkPaint::kRound_Cap, draw a circle of diameter + SkPaint stroke width. If paint is set to SkPaint::kSquare_Cap or SkPaint::kButt_Cap, + draw a square of width and height SkPaint stroke width. + SkPaint::Style is ignored, as if were set to SkPaint::kStroke_Style. + + @param x left edge of circle or square + @param y top edge of circle or square + @param paint stroke, blend, color, and so on, used to draw + + example: https://fiddle.skia.org/c/@Canvas_drawPoint + */ + void drawPoint(SkScalar x, SkScalar y, const SkPaint& paint); + + /** Draws point p using clip, SkMatrix and SkPaint paint. + + The shape of point drawn depends on paint SkPaint::Cap. + If paint is set to SkPaint::kRound_Cap, draw a circle of diameter + SkPaint stroke width. If paint is set to SkPaint::kSquare_Cap or SkPaint::kButt_Cap, + draw a square of width and height SkPaint stroke width. + SkPaint::Style is ignored, as if were set to SkPaint::kStroke_Style. + + @param p top-left edge of circle or square + @param paint stroke, blend, color, and so on, used to draw + */ + void drawPoint(SkPoint p, const SkPaint& paint) { + this->drawPoint(p.x(), p.y(), paint); + } + + /** Draws line segment from (x0, y0) to (x1, y1) using clip, SkMatrix, and SkPaint paint. + In paint: SkPaint stroke width describes the line thickness; + SkPaint::Cap draws the end rounded or square; + SkPaint::Style is ignored, as if were set to SkPaint::kStroke_Style. + + @param x0 start of line segment on x-axis + @param y0 start of line segment on y-axis + @param x1 end of line segment on x-axis + @param y1 end of line segment on y-axis + @param paint stroke, blend, color, and so on, used to draw + + example: https://fiddle.skia.org/c/@Canvas_drawLine + */ + void drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint); + + /** Draws line segment from p0 to p1 using clip, SkMatrix, and SkPaint paint. + In paint: SkPaint stroke width describes the line thickness; + SkPaint::Cap draws the end rounded or square; + SkPaint::Style is ignored, as if were set to SkPaint::kStroke_Style. + + @param p0 start of line segment + @param p1 end of line segment + @param paint stroke, blend, color, and so on, used to draw + */ + void drawLine(SkPoint p0, SkPoint p1, const SkPaint& paint) { + this->drawLine(p0.x(), p0.y(), p1.x(), p1.y(), paint); + } + + /** Draws SkRect rect using clip, SkMatrix, and SkPaint paint. + In paint: SkPaint::Style determines if rectangle is stroked or filled; + if stroked, SkPaint stroke width describes the line thickness, and + SkPaint::Join draws the corners rounded or square. + + @param rect rectangle to draw + @param paint stroke or fill, blend, color, and so on, used to draw + + example: https://fiddle.skia.org/c/@Canvas_drawRect + */ + void drawRect(const SkRect& rect, const SkPaint& paint); + + /** Draws SkIRect rect using clip, SkMatrix, and SkPaint paint. + In paint: SkPaint::Style determines if rectangle is stroked or filled; + if stroked, SkPaint stroke width describes the line thickness, and + SkPaint::Join draws the corners rounded or square. + + @param rect rectangle to draw + @param paint stroke or fill, blend, color, and so on, used to draw + */ + void drawIRect(const SkIRect& rect, const SkPaint& paint) { + SkRect r; + r.set(rect); // promotes the ints to scalars + this->drawRect(r, paint); + } + + /** Draws SkRegion region using clip, SkMatrix, and SkPaint paint. + In paint: SkPaint::Style determines if rectangle is stroked or filled; + if stroked, SkPaint stroke width describes the line thickness, and + SkPaint::Join draws the corners rounded or square. + + @param region region to draw + @param paint SkPaint stroke or fill, blend, color, and so on, used to draw + + example: https://fiddle.skia.org/c/@Canvas_drawRegion + */ + void drawRegion(const SkRegion& region, const SkPaint& paint); + + /** Draws oval oval using clip, SkMatrix, and SkPaint. + In paint: SkPaint::Style determines if oval is stroked or filled; + if stroked, SkPaint stroke width describes the line thickness. + + @param oval SkRect bounds of oval + @param paint SkPaint stroke or fill, blend, color, and so on, used to draw + + example: https://fiddle.skia.org/c/@Canvas_drawOval + */ + void drawOval(const SkRect& oval, const SkPaint& paint); + + /** Draws SkRRect rrect using clip, SkMatrix, and SkPaint paint. + In paint: SkPaint::Style determines if rrect is stroked or filled; + if stroked, SkPaint stroke width describes the line thickness. + + rrect may represent a rectangle, circle, oval, uniformly rounded rectangle, or + may have any combination of positive non-square radii for the four corners. + + @param rrect SkRRect with up to eight corner radii to draw + @param paint SkPaint stroke or fill, blend, color, and so on, used to draw + + example: https://fiddle.skia.org/c/@Canvas_drawRRect + */ + void drawRRect(const SkRRect& rrect, const SkPaint& paint); + + /** Draws SkRRect outer and inner + using clip, SkMatrix, and SkPaint paint. + outer must contain inner or the drawing is undefined. + In paint: SkPaint::Style determines if SkRRect is stroked or filled; + if stroked, SkPaint stroke width describes the line thickness. + If stroked and SkRRect corner has zero length radii, SkPaint::Join can + draw corners rounded or square. + + GPU-backed platforms optimize drawing when both outer and inner are + concave and outer contains inner. These platforms may not be able to draw + SkPath built with identical data as fast. + + @param outer SkRRect outer bounds to draw + @param inner SkRRect inner bounds to draw + @param paint SkPaint stroke or fill, blend, color, and so on, used to draw + + example: https://fiddle.skia.org/c/@Canvas_drawDRRect_a + example: https://fiddle.skia.org/c/@Canvas_drawDRRect_b + */ + void drawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint); + + /** Draws circle at (cx, cy) with radius using clip, SkMatrix, and SkPaint paint. + If radius is zero or less, nothing is drawn. + In paint: SkPaint::Style determines if circle is stroked or filled; + if stroked, SkPaint stroke width describes the line thickness. + + @param cx circle center on the x-axis + @param cy circle center on the y-axis + @param radius half the diameter of circle + @param paint SkPaint stroke or fill, blend, color, and so on, used to draw + + example: https://fiddle.skia.org/c/@Canvas_drawCircle + */ + void drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint); + + /** Draws circle at center with radius using clip, SkMatrix, and SkPaint paint. + If radius is zero or less, nothing is drawn. + In paint: SkPaint::Style determines if circle is stroked or filled; + if stroked, SkPaint stroke width describes the line thickness. + + @param center circle center + @param radius half the diameter of circle + @param paint SkPaint stroke or fill, blend, color, and so on, used to draw + */ + void drawCircle(SkPoint center, SkScalar radius, const SkPaint& paint) { + this->drawCircle(center.x(), center.y(), radius, paint); + } + + /** Draws arc using clip, SkMatrix, and SkPaint paint. + + Arc is part of oval bounded by oval, sweeping from startAngle to startAngle plus + sweepAngle. startAngle and sweepAngle are in degrees. + + startAngle of zero places start point at the right middle edge of oval. + A positive sweepAngle places arc end point clockwise from start point; + a negative sweepAngle places arc end point counterclockwise from start point. + sweepAngle may exceed 360 degrees, a full circle. + If useCenter is true, draw a wedge that includes lines from oval + center to arc end points. If useCenter is false, draw arc between end points. + + If SkRect oval is empty or sweepAngle is zero, nothing is drawn. + + @param oval SkRect bounds of oval containing arc to draw + @param startAngle angle in degrees where arc begins + @param sweepAngle sweep angle in degrees; positive is clockwise + @param useCenter if true, include the center of the oval + @param paint SkPaint stroke or fill, blend, color, and so on, used to draw + */ + void drawArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, + bool useCenter, const SkPaint& paint); + + /** Draws arc using clip, SkMatrix, and SkPaint paint. + + Arc is part of oval bounded by oval, sweeping from startAngle to startAngle plus + sweepAngle. startAngle and sweepAngle are in degrees. + + startAngle of zero places start point at the right middle edge of oval. + A positive sweepAngle places arc end point clockwise from start point; + a negative sweepAngle places arc end point counterclockwise from start point. + sweepAngle may exceed 360 degrees, a full circle. + If useCenter is true, draw a wedge that includes lines from oval + center to arc end points. If useCenter is false, draw arc between end points. + + If SkRect oval is empty or sweepAngle is zero, nothing is drawn. + + @param arc SkArc specifying oval, startAngle, sweepAngle, and arc-vs-wedge + @param paint SkPaint stroke or fill, blend, color, and so on, used to draw + */ + void drawArc(const SkArc& arc, const SkPaint& paint) { + this->drawArc(arc.fOval, arc.fStartAngle, arc.fSweepAngle, arc.isWedge(), paint); + } + + /** Draws SkRRect bounded by SkRect rect, with corner radii (rx, ry) using clip, + SkMatrix, and SkPaint paint. + + In paint: SkPaint::Style determines if SkRRect is stroked or filled; + if stroked, SkPaint stroke width describes the line thickness. + If rx or ry are less than zero, they are treated as if they are zero. + If rx plus ry exceeds rect width or rect height, radii are scaled down to fit. + If rx and ry are zero, SkRRect is drawn as SkRect and if stroked is affected by + SkPaint::Join. + + @param rect SkRect bounds of SkRRect to draw + @param rx axis length on x-axis of oval describing rounded corners + @param ry axis length on y-axis of oval describing rounded corners + @param paint stroke, blend, color, and so on, used to draw + + example: https://fiddle.skia.org/c/@Canvas_drawRoundRect + */ + void drawRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, const SkPaint& paint); + + /** Draws SkPath path using clip, SkMatrix, and SkPaint paint. + SkPath contains an array of path contour, each of which may be open or closed. + + In paint: SkPaint::Style determines if SkRRect is stroked or filled: + if filled, SkPath::FillType determines whether path contour describes inside or + outside of fill; if stroked, SkPaint stroke width describes the line thickness, + SkPaint::Cap describes line ends, and SkPaint::Join describes how + corners are drawn. + + @param path SkPath to draw + @param paint stroke, blend, color, and so on, used to draw + + example: https://fiddle.skia.org/c/@Canvas_drawPath + */ + void drawPath(const SkPath& path, const SkPaint& paint); + + void drawImage(const SkImage* image, SkScalar left, SkScalar top) { + this->drawImage(image, left, top, SkSamplingOptions(), nullptr); + } + void drawImage(const sk_sp& image, SkScalar left, SkScalar top) { + this->drawImage(image.get(), left, top, SkSamplingOptions(), nullptr); + } + + /** \enum SkCanvas::SrcRectConstraint + SrcRectConstraint controls the behavior at the edge of source SkRect, + provided to drawImageRect() when there is any filtering. If kStrict is set, + then extra code is used to ensure it never samples outside of the src-rect. + kStrict_SrcRectConstraint disables the use of mipmaps and anisotropic filtering. + */ + enum SrcRectConstraint { + kStrict_SrcRectConstraint, //!< sample only inside bounds; slower + kFast_SrcRectConstraint, //!< sample outside bounds; faster + }; + + void drawImage(const SkImage*, SkScalar x, SkScalar y, const SkSamplingOptions&, + const SkPaint* = nullptr); + void drawImage(const sk_sp& image, SkScalar x, SkScalar y, + const SkSamplingOptions& sampling, const SkPaint* paint = nullptr) { + this->drawImage(image.get(), x, y, sampling, paint); + } + void drawImageRect(const SkImage*, const SkRect& src, const SkRect& dst, + const SkSamplingOptions&, const SkPaint*, SrcRectConstraint); + void drawImageRect(const SkImage*, const SkRect& dst, const SkSamplingOptions&, + const SkPaint* = nullptr); + void drawImageRect(const sk_sp& image, const SkRect& src, const SkRect& dst, + const SkSamplingOptions& sampling, const SkPaint* paint, + SrcRectConstraint constraint) { + this->drawImageRect(image.get(), src, dst, sampling, paint, constraint); + } + void drawImageRect(const sk_sp& image, const SkRect& dst, + const SkSamplingOptions& sampling, const SkPaint* paint = nullptr) { + this->drawImageRect(image.get(), dst, sampling, paint); + } + + /** Draws SkImage image stretched proportionally to fit into SkRect dst. + SkIRect center divides the image into nine sections: four sides, four corners, and + the center. Corners are unmodified or scaled down proportionately if their sides + are larger than dst; center and four sides are scaled to fit remaining space, if any. + + Additionally transform draw using clip, SkMatrix, and optional SkPaint paint. + + If SkPaint paint is supplied, apply SkColorFilter, alpha, SkImageFilter, and + SkBlendMode. If image is kAlpha_8_SkColorType, apply SkShader. + If paint contains SkMaskFilter, generate mask from image bounds. + Any SkMaskFilter on paint is ignored as is paint anti-aliasing state. + + If generated mask extends beyond image bounds, replicate image edge colors, just + as SkShader made from SkImage::makeShader with SkShader::kClamp_TileMode set + replicates the image edge color when it samples outside of its bounds. + + @param image SkImage containing pixels, dimensions, and format + @param center SkIRect edge of image corners and sides + @param dst destination SkRect of image to draw to + @param filter what technique to use when sampling the image + @param paint SkPaint containing SkBlendMode, SkColorFilter, SkImageFilter, + and so on; or nullptr + */ + void drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst, + SkFilterMode filter, const SkPaint* paint = nullptr); + + /** \struct SkCanvas::Lattice + SkCanvas::Lattice divides SkBitmap or SkImage into a rectangular grid. + Grid entries on even columns and even rows are fixed; these entries are + always drawn at their original size if the destination is large enough. + If the destination side is too small to hold the fixed entries, all fixed + entries are proportionately scaled down to fit. + The grid entries not on even columns and rows are scaled to fit the + remaining space, if any. + */ + struct Lattice { + + /** \enum SkCanvas::Lattice::RectType + Optional setting per rectangular grid entry to make it transparent, + or to fill the grid entry with a color. + */ + enum RectType : uint8_t { + kDefault = 0, //!< draws SkBitmap into lattice rectangle + kTransparent, //!< skips lattice rectangle by making it transparent + kFixedColor, //!< draws one of fColors into lattice rectangle + }; + + const int* fXDivs; //!< x-axis values dividing bitmap + const int* fYDivs; //!< y-axis values dividing bitmap + const RectType* fRectTypes; //!< array of fill types + int fXCount; //!< number of x-coordinates + int fYCount; //!< number of y-coordinates + const SkIRect* fBounds; //!< source bounds to draw from + const SkColor* fColors; //!< array of colors + }; + + /** Draws SkImage image stretched proportionally to fit into SkRect dst. + + SkCanvas::Lattice lattice divides image into a rectangular grid. + Each intersection of an even-numbered row and column is fixed; + fixed lattice elements never scale larger than their initial + size and shrink proportionately when all fixed elements exceed the bitmap + dimension. All other grid elements scale to fill the available space, if any. + + Additionally transform draw using clip, SkMatrix, and optional SkPaint paint. + + If SkPaint paint is supplied, apply SkColorFilter, alpha, SkImageFilter, and + SkBlendMode. If image is kAlpha_8_SkColorType, apply SkShader. + If paint contains SkMaskFilter, generate mask from image bounds. + Any SkMaskFilter on paint is ignored as is paint anti-aliasing state. + + If generated mask extends beyond bitmap bounds, replicate bitmap edge colors, + just as SkShader made from SkShader::MakeBitmapShader with + SkShader::kClamp_TileMode set replicates the bitmap edge color when it samples + outside of its bounds. + + @param image SkImage containing pixels, dimensions, and format + @param lattice division of bitmap into fixed and variable rectangles + @param dst destination SkRect of image to draw to + @param filter what technique to use when sampling the image + @param paint SkPaint containing SkBlendMode, SkColorFilter, SkImageFilter, + and so on; or nullptr + */ + void drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst, + SkFilterMode filter, const SkPaint* paint = nullptr); + void drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst) { + this->drawImageLattice(image, lattice, dst, SkFilterMode::kNearest, nullptr); + } + + /** + * Experimental. Controls anti-aliasing of each edge of images in an image-set. + */ + enum QuadAAFlags : unsigned { + kLeft_QuadAAFlag = 0b0001, + kTop_QuadAAFlag = 0b0010, + kRight_QuadAAFlag = 0b0100, + kBottom_QuadAAFlag = 0b1000, + + kNone_QuadAAFlags = 0b0000, + kAll_QuadAAFlags = 0b1111, + }; + + /** This is used by the experimental API below. */ + struct SK_API ImageSetEntry { + ImageSetEntry(sk_sp image, const SkRect& srcRect, const SkRect& dstRect, + int matrixIndex, float alpha, unsigned aaFlags, bool hasClip); + + ImageSetEntry(sk_sp image, const SkRect& srcRect, const SkRect& dstRect, + float alpha, unsigned aaFlags); + + ImageSetEntry(); + ~ImageSetEntry(); + ImageSetEntry(const ImageSetEntry&); + ImageSetEntry& operator=(const ImageSetEntry&); + + sk_sp fImage; + SkRect fSrcRect; + SkRect fDstRect; + int fMatrixIndex = -1; // Index into the preViewMatrices arg, or < 0 + float fAlpha = 1.f; + unsigned fAAFlags = kNone_QuadAAFlags; // QuadAAFlags + bool fHasClip = false; // True to use next 4 points in dstClip arg as quad + }; + + /** + * This is an experimental API for the SkiaRenderer Chromium project, and its API will surely + * evolve if it is not removed outright. + * + * This behaves very similarly to drawRect() combined with a clipPath() formed by clip + * quadrilateral. 'rect' and 'clip' are in the same coordinate space. If 'clip' is null, then it + * is as if the rectangle was not clipped (or, alternatively, clipped to itself). If not null, + * then it must provide 4 points. + * + * In addition to combining the draw and clipping into one operation, this function adds the + * additional capability of controlling each of the rectangle's edges anti-aliasing + * independently. The edges of the clip will respect the per-edge AA flags. It is required that + * 'clip' be contained inside 'rect'. In terms of mapping to edge labels, the 'clip' points + * should be ordered top-left, top-right, bottom-right, bottom-left so that the edge between [0] + * and [1] is "top", [1] and [2] is "right", [2] and [3] is "bottom", and [3] and [0] is "left". + * This ordering matches SkRect::toQuad(). + * + * This API only draws solid color, filled rectangles so it does not accept a full SkPaint. + */ + void experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4], QuadAAFlags aaFlags, + const SkColor4f& color, SkBlendMode mode); + void experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4], QuadAAFlags aaFlags, + SkColor color, SkBlendMode mode) { + this->experimental_DrawEdgeAAQuad(rect, clip, aaFlags, SkColor4f::FromColor(color), mode); + } + + /** + * This is an bulk variant of experimental_DrawEdgeAAQuad() that renders 'cnt' textured quads. + * For each entry, 'fDstRect' is rendered with its clip (determined by entry's 'fHasClip' and + * the current index in 'dstClip'). The entry's fImage is applied to the destination rectangle + * by sampling from 'fSrcRect' sub-image. The corners of 'fSrcRect' map to the corners of + * 'fDstRect', just like in drawImageRect(), and they will be properly interpolated when + * applying a clip. + * + * Like experimental_DrawEdgeAAQuad(), each entry can specify edge AA flags that apply to both + * the destination rect and its clip. + * + * If provided, the 'dstClips' array must have length equal 4 * the number of entries with + * fHasClip true. If 'dstClips' is null, every entry must have 'fHasClip' set to false. The + * destination clip coordinates will be read consecutively with the image set entries, advancing + * by 4 points every time an entry with fHasClip is passed. + * + * This entry point supports per-entry manipulations to the canvas's current matrix. If an + * entry provides 'fMatrixIndex' >= 0, it will be drawn as if the canvas's CTM was + * canvas->getTotalMatrix() * preViewMatrices[fMatrixIndex]. If 'fMatrixIndex' is less than 0, + * the pre-view matrix transform is implicitly the identity, so it will be drawn using just the + * current canvas matrix. The pre-view matrix modifies the canvas's view matrix, it does not + * affect the local coordinates of each entry. + * + * An optional paint may be provided, which supports the same subset of features usable with + * drawImageRect (i.e. assumed to be filled and no path effects). When a paint is provided, the + * image set is drawn as if each image used the applied paint independently, so each is affected + * by the image, color, and/or mask filter. + */ + void experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt, + const SkPoint dstClips[], const SkMatrix preViewMatrices[], + const SkSamplingOptions&, const SkPaint* paint = nullptr, + SrcRectConstraint constraint = kStrict_SrcRectConstraint); + + /** Draws text, with origin at (x, y), using clip, SkMatrix, SkFont font, + and SkPaint paint. + + When encoding is SkTextEncoding::kUTF8, SkTextEncoding::kUTF16, or + SkTextEncoding::kUTF32, this function uses the default + character-to-glyph mapping from the SkTypeface in font. It does not + perform typeface fallback for characters not found in the SkTypeface. + It does not perform kerning or other complex shaping; glyphs are + positioned based on their default advances. + + Text meaning depends on SkTextEncoding. + + Text size is affected by SkMatrix and SkFont text size. Default text + size is 12 point. + + All elements of paint: SkPathEffect, SkMaskFilter, SkShader, + SkColorFilter, and SkImageFilter; apply to text. By + default, draws filled black glyphs. + + @param text character code points or glyphs drawn + @param byteLength byte length of text array + @param encoding text encoding used in the text array + @param x start of text on x-axis + @param y start of text on y-axis + @param font typeface, text size and so, used to describe the text + @param paint blend, color, and so on, used to draw + */ + void drawSimpleText(const void* text, size_t byteLength, SkTextEncoding encoding, + SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint); + + /** Draws null terminated string, with origin at (x, y), using clip, SkMatrix, + SkFont font, and SkPaint paint. + + This function uses the default character-to-glyph mapping from the + SkTypeface in font. It does not perform typeface fallback for + characters not found in the SkTypeface. It does not perform kerning; + glyphs are positioned based on their default advances. + + String str is encoded as UTF-8. + + Text size is affected by SkMatrix and font text size. Default text + size is 12 point. + + All elements of paint: SkPathEffect, SkMaskFilter, SkShader, + SkColorFilter, and SkImageFilter; apply to text. By + default, draws filled black glyphs. + + @param str character code points drawn, + ending with a char value of zero + @param x start of string on x-axis + @param y start of string on y-axis + @param font typeface, text size and so, used to describe the text + @param paint blend, color, and so on, used to draw + */ + void drawString(const char str[], SkScalar x, SkScalar y, const SkFont& font, + const SkPaint& paint) { + this->drawSimpleText(str, strlen(str), SkTextEncoding::kUTF8, x, y, font, paint); + } + + /** Draws SkString, with origin at (x, y), using clip, SkMatrix, SkFont font, + and SkPaint paint. + + This function uses the default character-to-glyph mapping from the + SkTypeface in font. It does not perform typeface fallback for + characters not found in the SkTypeface. It does not perform kerning; + glyphs are positioned based on their default advances. + + SkString str is encoded as UTF-8. + + Text size is affected by SkMatrix and SkFont text size. Default text + size is 12 point. + + All elements of paint: SkPathEffect, SkMaskFilter, SkShader, + SkColorFilter, and SkImageFilter; apply to text. By + default, draws filled black glyphs. + + @param str character code points drawn, + ending with a char value of zero + @param x start of string on x-axis + @param y start of string on y-axis + @param font typeface, text size and so, used to describe the text + @param paint blend, color, and so on, used to draw + */ + void drawString(const SkString& str, SkScalar x, SkScalar y, const SkFont& font, + const SkPaint& paint) { + this->drawSimpleText(str.c_str(), str.size(), SkTextEncoding::kUTF8, x, y, font, paint); + } + + /** Draws count glyphs, at positions relative to origin styled with font and paint with + supporting utf8 and cluster information. + + This function draw glyphs at the given positions relative to the given origin. + It does not perform typeface fallback for glyphs not found in the SkTypeface in font. + + The drawing obeys the current transform matrix and clipping. + + All elements of paint: SkPathEffect, SkMaskFilter, SkShader, + SkColorFilter, and SkImageFilter; apply to text. By + default, draws filled black glyphs. + + @param count number of glyphs to draw + @param glyphs the array of glyphIDs to draw + @param positions where to draw each glyph relative to origin + @param clusters array of size count of cluster information + @param textByteCount size of the utf8text + @param utf8text utf8text supporting information for the glyphs + @param origin the origin of all the positions + @param font typeface, text size and so, used to describe the text + @param paint blend, color, and so on, used to draw + */ + void drawGlyphs(int count, const SkGlyphID glyphs[], const SkPoint positions[], + const uint32_t clusters[], int textByteCount, const char utf8text[], + SkPoint origin, const SkFont& font, const SkPaint& paint); + + /** Draws count glyphs, at positions relative to origin styled with font and paint. + + This function draw glyphs at the given positions relative to the given origin. + It does not perform typeface fallback for glyphs not found in the SkTypeface in font. + + The drawing obeys the current transform matrix and clipping. + + All elements of paint: SkPathEffect, SkMaskFilter, SkShader, + SkColorFilter, and SkImageFilter; apply to text. By + default, draws filled black glyphs. + + @param count number of glyphs to draw + @param glyphs the array of glyphIDs to draw + @param positions where to draw each glyph relative to origin + @param origin the origin of all the positions + @param font typeface, text size and so, used to describe the text + @param paint blend, color, and so on, used to draw + */ + void drawGlyphs(int count, const SkGlyphID glyphs[], const SkPoint positions[], + SkPoint origin, const SkFont& font, const SkPaint& paint); + + /** Draws count glyphs, at positions relative to origin styled with font and paint. + + This function draw glyphs using the given scaling and rotations. They are positioned + relative to the given origin. It does not perform typeface fallback for glyphs not found + in the SkTypeface in font. + + The drawing obeys the current transform matrix and clipping. + + All elements of paint: SkPathEffect, SkMaskFilter, SkShader, + SkColorFilter, and SkImageFilter; apply to text. By + default, draws filled black glyphs. + + @param count number of glyphs to draw + @param glyphs the array of glyphIDs to draw + @param xforms where to draw and orient each glyph + @param origin the origin of all the positions + @param font typeface, text size and so, used to describe the text + @param paint blend, color, and so on, used to draw + */ + void drawGlyphs(int count, const SkGlyphID glyphs[], const SkRSXform xforms[], + SkPoint origin, const SkFont& font, const SkPaint& paint); + + /** Draws SkTextBlob blob at (x, y), using clip, SkMatrix, and SkPaint paint. + + blob contains glyphs, their positions, and paint attributes specific to text: + SkTypeface, SkPaint text size, SkPaint text scale x, + SkPaint text skew x, SkPaint::Align, SkPaint::Hinting, anti-alias, SkPaint fake bold, + SkPaint font embedded bitmaps, SkPaint full hinting spacing, LCD text, SkPaint linear text, + and SkPaint subpixel text. + + SkTextEncoding must be set to SkTextEncoding::kGlyphID. + + Elements of paint: anti-alias, SkBlendMode, color including alpha, + SkColorFilter, SkPaint dither, SkMaskFilter, SkPathEffect, SkShader, and + SkPaint::Style; apply to blob. If SkPaint contains SkPaint::kStroke_Style: + SkPaint miter limit, SkPaint::Cap, SkPaint::Join, and SkPaint stroke width; + apply to SkPath created from blob. + + @param blob glyphs, positions, and their paints' text size, typeface, and so on + @param x horizontal offset applied to blob + @param y vertical offset applied to blob + @param paint blend, color, stroking, and so on, used to draw + + example: https://fiddle.skia.org/c/@Canvas_drawTextBlob + */ + void drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint); + + /** Draws SkTextBlob blob at (x, y), using clip, SkMatrix, and SkPaint paint. + + blob contains glyphs, their positions, and paint attributes specific to text: + SkTypeface, SkPaint text size, SkPaint text scale x, + SkPaint text skew x, SkPaint::Align, SkPaint::Hinting, anti-alias, SkPaint fake bold, + SkPaint font embedded bitmaps, SkPaint full hinting spacing, LCD text, SkPaint linear text, + and SkPaint subpixel text. + + SkTextEncoding must be set to SkTextEncoding::kGlyphID. + + Elements of paint: SkPathEffect, SkMaskFilter, SkShader, SkColorFilter, + and SkImageFilter; apply to blob. + + @param blob glyphs, positions, and their paints' text size, typeface, and so on + @param x horizontal offset applied to blob + @param y vertical offset applied to blob + @param paint blend, color, stroking, and so on, used to draw + */ + void drawTextBlob(const sk_sp& blob, SkScalar x, SkScalar y, const SkPaint& paint) { + this->drawTextBlob(blob.get(), x, y, paint); + } + + /** Draws SkPicture picture, using clip and SkMatrix. + Clip and SkMatrix are unchanged by picture contents, as if + save() was called before and restore() was called after drawPicture(). + + SkPicture records a series of draw commands for later playback. + + @param picture recorded drawing commands to play + */ + void drawPicture(const SkPicture* picture) { + this->drawPicture(picture, nullptr, nullptr); + } + + /** Draws SkPicture picture, using clip and SkMatrix. + Clip and SkMatrix are unchanged by picture contents, as if + save() was called before and restore() was called after drawPicture(). + + SkPicture records a series of draw commands for later playback. + + @param picture recorded drawing commands to play + */ + void drawPicture(const sk_sp& picture) { + this->drawPicture(picture.get()); + } + + /** Draws SkPicture picture, using clip and SkMatrix; transforming picture with + SkMatrix matrix, if provided; and use SkPaint paint alpha, SkColorFilter, + SkImageFilter, and SkBlendMode, if provided. + + If paint is non-null, then the picture is always drawn into a temporary layer before + actually landing on the canvas. Note that drawing into a layer can also change its + appearance if there are any non-associative blendModes inside any of the pictures elements. + + @param picture recorded drawing commands to play + @param matrix SkMatrix to rotate, scale, translate, and so on; may be nullptr + @param paint SkPaint to apply transparency, filtering, and so on; may be nullptr + + example: https://fiddle.skia.org/c/@Canvas_drawPicture_3 + */ + void drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint); + + /** Draws SkPicture picture, using clip and SkMatrix; transforming picture with + SkMatrix matrix, if provided; and use SkPaint paint alpha, SkColorFilter, + SkImageFilter, and SkBlendMode, if provided. + + If paint is non-null, then the picture is always drawn into a temporary layer before + actually landing on the canvas. Note that drawing into a layer can also change its + appearance if there are any non-associative blendModes inside any of the pictures elements. + + @param picture recorded drawing commands to play + @param matrix SkMatrix to rotate, scale, translate, and so on; may be nullptr + @param paint SkPaint to apply transparency, filtering, and so on; may be nullptr + */ + void drawPicture(const sk_sp& picture, const SkMatrix* matrix, + const SkPaint* paint) { + this->drawPicture(picture.get(), matrix, paint); + } + + /** Draws SkVertices vertices, a triangle mesh, using clip and SkMatrix. + If paint contains an SkShader and vertices does not contain texCoords, the shader + is mapped using the vertices' positions. + + SkBlendMode is ignored if SkVertices does not have colors. Otherwise, it combines + - the SkShader if SkPaint contains SkShader + - or the opaque SkPaint color if SkPaint does not contain SkShader + as the src of the blend and the interpolated vertex colors as the dst. + + SkMaskFilter, SkPathEffect, and antialiasing on SkPaint are ignored. + + @param vertices triangle mesh to draw + @param mode combines vertices' colors with SkShader if present or SkPaint opaque color + if not. Ignored if the vertices do not contain color. + @param paint specifies the SkShader, used as SkVertices texture, and SkColorFilter. + + example: https://fiddle.skia.org/c/@Canvas_drawVertices + */ + void drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint); + + /** Draws SkVertices vertices, a triangle mesh, using clip and SkMatrix. + If paint contains an SkShader and vertices does not contain texCoords, the shader + is mapped using the vertices' positions. + + SkBlendMode is ignored if SkVertices does not have colors. Otherwise, it combines + - the SkShader if SkPaint contains SkShader + - or the opaque SkPaint color if SkPaint does not contain SkShader + as the src of the blend and the interpolated vertex colors as the dst. + + SkMaskFilter, SkPathEffect, and antialiasing on SkPaint are ignored. + + @param vertices triangle mesh to draw + @param mode combines vertices' colors with SkShader if present or SkPaint opaque color + if not. Ignored if the vertices do not contain color. + @param paint specifies the SkShader, used as SkVertices texture, may be nullptr + + example: https://fiddle.skia.org/c/@Canvas_drawVertices_2 + */ + void drawVertices(const sk_sp& vertices, SkBlendMode mode, const SkPaint& paint); + + /** + Experimental, under active development, and subject to change without notice. + + Draws a mesh using a user-defined specification (see SkMeshSpecification). Requires + a GPU backend or SkSL to be compiled in. + + SkBlender is ignored if SkMesh's specification does not output fragment shader color. + Otherwise, it combines + - the SkShader if SkPaint contains SkShader + - or the opaque SkPaint color if SkPaint does not contain SkShader + as the src of the blend and the mesh's fragment color as the dst. + + SkMaskFilter, SkPathEffect, and antialiasing on SkPaint are ignored. + + @param mesh the mesh vertices and compatible specification. + @param blender combines vertices colors with SkShader if present or SkPaint opaque color + if not. Ignored if the custom mesh does not output color. Defaults to + SkBlendMode::kModulate if nullptr. + @param paint specifies the SkShader, used as SkVertices texture, may be nullptr + */ + void drawMesh(const SkMesh& mesh, sk_sp blender, const SkPaint& paint); + + /** Draws a Coons patch: the interpolation of four cubics with shared corners, + associating a color, and optionally a texture SkPoint, with each corner. + + SkPoint array cubics specifies four SkPath cubic starting at the top-left corner, + in clockwise order, sharing every fourth point. The last SkPath cubic ends at the + first point. + + Color array color associates colors with corners in top-left, top-right, + bottom-right, bottom-left order. + + If paint contains SkShader, SkPoint array texCoords maps SkShader as texture to + corners in top-left, top-right, bottom-right, bottom-left order. If texCoords is + nullptr, SkShader is mapped using positions (derived from cubics). + + SkBlendMode is ignored if colors is null. Otherwise, it combines + - the SkShader if SkPaint contains SkShader + - or the opaque SkPaint color if SkPaint does not contain SkShader + as the src of the blend and the interpolated patch colors as the dst. + + SkMaskFilter, SkPathEffect, and antialiasing on SkPaint are ignored. + + @param cubics SkPath cubic array, sharing common points + @param colors color array, one for each corner + @param texCoords SkPoint array of texture coordinates, mapping SkShader to corners; + may be nullptr + @param mode combines patch's colors with SkShader if present or SkPaint opaque color + if not. Ignored if colors is null. + @param paint SkShader, SkColorFilter, SkBlendMode, used to draw + */ + void drawPatch(const SkPoint cubics[12], const SkColor colors[4], + const SkPoint texCoords[4], SkBlendMode mode, const SkPaint& paint); + + /** Draws a set of sprites from atlas, using clip, SkMatrix, and optional SkPaint paint. + paint uses anti-alias, alpha, SkColorFilter, SkImageFilter, and SkBlendMode + to draw, if present. For each entry in the array, SkRect tex locates sprite in + atlas, and SkRSXform xform transforms it into destination space. + + SkMaskFilter and SkPathEffect on paint are ignored. + + xform, tex, and colors if present, must contain count entries. + Optional colors are applied for each sprite using SkBlendMode mode, treating + sprite as source and colors as destination. + Optional cullRect is a conservative bounds of all transformed sprites. + If cullRect is outside of clip, canvas can skip drawing. + + If atlas is nullptr, this draws nothing. + + @param atlas SkImage containing sprites + @param xform SkRSXform mappings for sprites in atlas + @param tex SkRect locations of sprites in atlas + @param colors one per sprite, blended with sprite using SkBlendMode; may be nullptr + @param count number of sprites to draw + @param mode SkBlendMode combining colors and sprites + @param sampling SkSamplingOptions used when sampling from the atlas image + @param cullRect bounds of transformed sprites for efficient clipping; may be nullptr + @param paint SkColorFilter, SkImageFilter, SkBlendMode, and so on; may be nullptr + */ + void drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[], + const SkColor colors[], int count, SkBlendMode mode, + const SkSamplingOptions& sampling, const SkRect* cullRect, const SkPaint* paint); + + /** Draws SkDrawable drawable using clip and SkMatrix, concatenated with + optional matrix. + + If SkCanvas has an asynchronous implementation, as is the case + when it is recording into SkPicture, then drawable will be referenced, + so that SkDrawable::draw() can be called when the operation is finalized. To force + immediate drawing, call SkDrawable::draw() instead. + + @param drawable custom struct encapsulating drawing commands + @param matrix transformation applied to drawing; may be nullptr + + example: https://fiddle.skia.org/c/@Canvas_drawDrawable + */ + void drawDrawable(SkDrawable* drawable, const SkMatrix* matrix = nullptr); + + /** Draws SkDrawable drawable using clip and SkMatrix, offset by (x, y). + + If SkCanvas has an asynchronous implementation, as is the case + when it is recording into SkPicture, then drawable will be referenced, + so that SkDrawable::draw() can be called when the operation is finalized. To force + immediate drawing, call SkDrawable::draw() instead. + + @param drawable custom struct encapsulating drawing commands + @param x offset into SkCanvas writable pixels on x-axis + @param y offset into SkCanvas writable pixels on y-axis + + example: https://fiddle.skia.org/c/@Canvas_drawDrawable_2 + */ + void drawDrawable(SkDrawable* drawable, SkScalar x, SkScalar y); + + /** Associates SkRect on SkCanvas with an annotation; a key-value pair, where the key is + a null-terminated UTF-8 string, and optional value is stored as SkData. + + Only some canvas implementations, such as recording to SkPicture, or drawing to + document PDF, use annotations. + + @param rect SkRect extent of canvas to annotate + @param key string used for lookup + @param value data holding value stored in annotation + + example: https://fiddle.skia.org/c/@Canvas_drawAnnotation_2 + */ + void drawAnnotation(const SkRect& rect, const char key[], SkData* value); + + /** Associates SkRect on SkCanvas when an annotation; a key-value pair, where the key is + a null-terminated UTF-8 string, and optional value is stored as SkData. + + Only some canvas implementations, such as recording to SkPicture, or drawing to + document PDF, use annotations. + + @param rect SkRect extent of canvas to annotate + @param key string used for lookup + @param value data holding value stored in annotation + */ + void drawAnnotation(const SkRect& rect, const char key[], const sk_sp& value) { + this->drawAnnotation(rect, key, value.get()); + } + + /** Returns true if clip is empty; that is, nothing will draw. + + May do work when called; it should not be called + more often than needed. However, once called, subsequent calls perform no + work until clip changes. + + @return true if clip is empty + + example: https://fiddle.skia.org/c/@Canvas_isClipEmpty + */ + virtual bool isClipEmpty() const; + + /** Returns true if clip is SkRect and not empty. + Returns false if the clip is empty, or if it is not SkRect. + + @return true if clip is SkRect and not empty + + example: https://fiddle.skia.org/c/@Canvas_isClipRect + */ + virtual bool isClipRect() const; + + /** Returns the current transform from local coordinates to the 'device', which for most + * purposes means pixels. + * + * @return transformation from local coordinates to device / pixels. + */ + SkM44 getLocalToDevice() const; + + /** + * Throws away the 3rd row and column in the matrix, so be warned. + */ + SkMatrix getLocalToDeviceAs3x3() const { + return this->getLocalToDevice().asM33(); + } + +#ifdef SK_SUPPORT_LEGACY_GETTOTALMATRIX + /** DEPRECATED + * Legacy version of getLocalToDevice(), which strips away any Z information, and + * just returns a 3x3 version. + * + * @return 3x3 version of getLocalToDevice() + * + * example: https://fiddle.skia.org/c/@Canvas_getTotalMatrix + * example: https://fiddle.skia.org/c/@Clip + */ + SkMatrix getTotalMatrix() const; +#endif + + /////////////////////////////////////////////////////////////////////////// + + /** + * Returns the global clip as a region. If the clip contains AA, then only the bounds + * of the clip may be returned. + */ + void temporary_internal_getRgnClip(SkRegion* region); + + void private_draw_shadow_rec(const SkPath&, const SkDrawShadowRec&); + + +protected: + // default impl defers to getDevice()->newSurface(info) + virtual sk_sp onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props); + + // default impl defers to its device + virtual bool onPeekPixels(SkPixmap* pixmap); + virtual bool onAccessTopLayerPixels(SkPixmap* pixmap); + virtual SkImageInfo onImageInfo() const; + virtual bool onGetProps(SkSurfaceProps* props, bool top) const; + + // Subclass save/restore notifiers. + // Overriders should call the corresponding INHERITED method up the inheritance chain. + // getSaveLayerStrategy()'s return value may suppress full layer allocation. + enum SaveLayerStrategy { + kFullLayer_SaveLayerStrategy, + kNoLayer_SaveLayerStrategy, + }; + + virtual void willSave() {} + // Overriders should call the corresponding INHERITED method up the inheritance chain. + virtual SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& ) { + return kFullLayer_SaveLayerStrategy; + } + + // returns true if we should actually perform the saveBehind, or false if we should just save. + virtual bool onDoSaveBehind(const SkRect*) { return true; } + virtual void willRestore() {} + virtual void didRestore() {} + + virtual void didConcat44(const SkM44&) {} + virtual void didSetM44(const SkM44&) {} + virtual void didTranslate(SkScalar, SkScalar) {} + virtual void didScale(SkScalar, SkScalar) {} + + // NOTE: If you are adding a new onDraw virtual to SkCanvas, PLEASE add an override to + // SkCanvasVirtualEnforcer (in SkCanvasVirtualEnforcer.h). This ensures that subclasses using + // that mechanism will be required to implement the new function. + virtual void onDrawPaint(const SkPaint& paint); + virtual void onDrawBehind(const SkPaint& paint); + virtual void onDrawRect(const SkRect& rect, const SkPaint& paint); + virtual void onDrawRRect(const SkRRect& rrect, const SkPaint& paint); + virtual void onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint); + virtual void onDrawOval(const SkRect& rect, const SkPaint& paint); + virtual void onDrawArc(const SkRect& rect, SkScalar startAngle, SkScalar sweepAngle, + bool useCenter, const SkPaint& paint); + virtual void onDrawPath(const SkPath& path, const SkPaint& paint); + virtual void onDrawRegion(const SkRegion& region, const SkPaint& paint); + + virtual void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, + const SkPaint& paint); + + virtual void onDrawGlyphRunList(const sktext::GlyphRunList& glyphRunList, const SkPaint& paint); + + virtual void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], + const SkPoint texCoords[4], SkBlendMode mode, const SkPaint& paint); + virtual void onDrawPoints(PointMode mode, size_t count, const SkPoint pts[], + const SkPaint& paint); + + virtual void onDrawImage2(const SkImage*, SkScalar dx, SkScalar dy, const SkSamplingOptions&, + const SkPaint*); + virtual void onDrawImageRect2(const SkImage*, const SkRect& src, const SkRect& dst, + const SkSamplingOptions&, const SkPaint*, SrcRectConstraint); + virtual void onDrawImageLattice2(const SkImage*, const Lattice&, const SkRect& dst, + SkFilterMode, const SkPaint*); + virtual void onDrawAtlas2(const SkImage*, const SkRSXform[], const SkRect src[], + const SkColor[], int count, SkBlendMode, const SkSamplingOptions&, + const SkRect* cull, const SkPaint*); + virtual void onDrawEdgeAAImageSet2(const ImageSetEntry imageSet[], int count, + const SkPoint dstClips[], const SkMatrix preViewMatrices[], + const SkSamplingOptions&, const SkPaint*, + SrcRectConstraint); + + virtual void onDrawVerticesObject(const SkVertices* vertices, SkBlendMode mode, + const SkPaint& paint); + virtual void onDrawMesh(const SkMesh&, sk_sp, const SkPaint&); + virtual void onDrawAnnotation(const SkRect& rect, const char key[], SkData* value); + virtual void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&); + + virtual void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix); + virtual void onDrawPicture(const SkPicture* picture, const SkMatrix* matrix, + const SkPaint* paint); + + virtual void onDrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4], QuadAAFlags aaFlags, + const SkColor4f& color, SkBlendMode mode); + + enum ClipEdgeStyle { + kHard_ClipEdgeStyle, + kSoft_ClipEdgeStyle + }; + + virtual void onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle); + virtual void onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle); + virtual void onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle); + virtual void onClipShader(sk_sp, SkClipOp); + virtual void onClipRegion(const SkRegion& deviceRgn, SkClipOp op); + virtual void onResetClip(); + + virtual void onDiscard(); + + /** + */ + virtual sk_sp onConvertGlyphRunListToSlug( + const sktext::GlyphRunList& glyphRunList, const SkPaint& paint); + + /** + */ + virtual void onDrawSlug(const sktext::gpu::Slug* slug, const SkPaint& paint); + +private: + enum class PredrawFlags : unsigned { + kNone = 0, + kOpaqueShaderOverride = 1, // The paint's shader is overridden with an opaque image + kNonOpaqueShaderOverride = 2, // The paint's shader is overridden but is not opaque + kCheckForOverwrite = 4, // Check if the draw would overwrite the entire surface + kSkipMaskFilterAutoLayer = 8, // Do not apply mask filters in the AutoLayer + }; + // Inlined SK_DECL_BITMASK_OPS_FRIENDS to avoid including SkEnumBitMask.h + friend constexpr SkEnumBitMask operator|(PredrawFlags, PredrawFlags); + friend constexpr SkEnumBitMask operator&(PredrawFlags, PredrawFlags); + friend constexpr SkEnumBitMask operator^(PredrawFlags, PredrawFlags); + friend constexpr SkEnumBitMask operator~(PredrawFlags); + + // notify our surface (if we have one) that we are about to draw, so it + // can perform copy-on-write or invalidate any cached images + // returns false if the copy failed + [[nodiscard]] bool predrawNotify(bool willOverwritesEntireSurface = false); + [[nodiscard]] bool predrawNotify(const SkRect*, const SkPaint*, SkEnumBitMask); + + // call the appropriate predrawNotify and create a layer if needed. + std::optional aboutToDraw( + const SkPaint& paint, + const SkRect* rawBounds, + SkEnumBitMask flags); + std::optional aboutToDraw( + const SkPaint& paint, + const SkRect* rawBounds = nullptr); + + // The bottom-most device in the stack, only changed by init(). Image properties and the final + // canvas pixels are determined by this device. + SkDevice* rootDevice() const { + SkASSERT(fRootDevice); + return fRootDevice.get(); + } + + // The top-most device in the stack, will change within saveLayer()'s. All drawing and clipping + // operations should route to this device. + SkDevice* topDevice() const; + + // Canvases maintain a sparse stack of layers, where the top-most layer receives the drawing, + // clip, and matrix commands. There is a layer per call to saveLayer() using the + // kFullLayer_SaveLayerStrategy. + struct Layer { + sk_sp fDevice; + skia_private::STArray<1, sk_sp> fImageFilters; + SkPaint fPaint; + bool fIsCoverage; + bool fDiscard; + + Layer(sk_sp device, + FilterSpan imageFilters, + const SkPaint& paint, + bool isCoverage); + }; + + // Encapsulate state needed to restore from saveBehind() + struct BackImage { + // Out of line to avoid including SkSpecialImage.h + BackImage(sk_sp, SkIPoint); + BackImage(const BackImage&); + BackImage(BackImage&&); + BackImage& operator=(const BackImage&); + ~BackImage(); + + sk_sp fImage; + SkIPoint fLoc; + }; + + class MCRec { + public: + // If not null, this MCRec corresponds with the saveLayer() record that made the layer. + // The base "layer" is not stored here, since it is stored inline in SkCanvas and has no + // restoration behavior. + std::unique_ptr fLayer; + + // This points to the device of the top-most layer (which may be lower in the stack), or + // to the canvas's fRootDevice. The MCRec does not own the device. + SkDevice* fDevice; + + std::unique_ptr fBackImage; + SkM44 fMatrix; + int fDeferredSaveCount = 0; + + MCRec(SkDevice* device); + MCRec(const MCRec* prev); + ~MCRec(); + + void newLayer(sk_sp layerDevice, + FilterSpan filters, + const SkPaint& restorePaint, + bool layerIsCoverage); + + 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 + static constexpr int kMCRecCount = 32; // common depth for save/restores + + intptr_t fMCRecStorage[kMCRecSize * kMCRecCount / sizeof(intptr_t)]; + + SkDeque fMCStack; + // points to top of stack + MCRec* fMCRec; + + // Installed via init() + sk_sp fRootDevice; + const SkSurfaceProps fProps; + + int fSaveCount; // value returned by getSaveCount() + + std::unique_ptr fAllocator; + + SkSurface_Base* fSurfaceBase; + SkSurface_Base* getSurfaceBase() const { return fSurfaceBase; } + void setSurfaceBase(SkSurface_Base* sb) { + fSurfaceBase = sb; + } + friend class SkSurface_Base; + friend class SkSurface_Ganesh; + + SkIRect fClipRestrictionRect = SkIRect::MakeEmpty(); + int fClipRestrictionSaveCount = -1; + + void doSave(); + void checkForDeferredSave(); + void internalSetMatrix(const SkM44&); + + friend class SkAndroidFrameworkUtils; + friend class SkCanvasPriv; // needs to expose android functions for testing outside android + friend class AutoLayerForImageFilter; + friend class SkSurface_Raster; // needs getDevice() + friend class SkNoDrawCanvas; // needs resetForNextPicture() + friend class SkNWayCanvas; + friend class SkPictureRecord; // predrawNotify (why does it need it? ) + friend class SkOverdrawCanvas; + friend class SkRasterHandleAllocator; + friend class SkRecords::Draw; + template + friend class SkTestCanvas; + +protected: + // For use by SkNoDrawCanvas (via SkCanvasVirtualEnforcer, which can't be a friend) + SkCanvas(const SkIRect& bounds); +private: + SkCanvas(const SkBitmap&, std::unique_ptr, + SkRasterHandleAllocator::Handle, const SkSurfaceProps* props); + + SkCanvas(SkCanvas&&) = delete; + SkCanvas(const SkCanvas&) = delete; + SkCanvas& operator=(SkCanvas&&) = delete; + SkCanvas& operator=(const SkCanvas&) = delete; + + friend class sktext::gpu::Slug; + friend class SkPicturePlayback; + /** + * Convert a SkTextBlob to a sktext::gpu::Slug using the current canvas state. + */ + sk_sp convertBlobToSlug(const SkTextBlob& blob, SkPoint origin, + const SkPaint& paint); + + /** + * Draw an sktext::gpu::Slug given the current canvas state. + */ + void drawSlug(const sktext::gpu::Slug* slug, const SkPaint& paint); + + /** Experimental + * Saves the specified subset of the current pixels in the current layer, + * and then clears those pixels to transparent black. + * Restores the pixels on restore() by drawing them in SkBlendMode::kDstOver. + * + * @param subset conservative bounds of the area to be saved / restored. + * @return depth of save state stack before this call was made. + */ + int only_axis_aligned_saveBehind(const SkRect* subset); + + /** + * Like drawPaint, but magically clipped to the most recent saveBehind buffer rectangle. + * If there is no active saveBehind, then this draws nothing. + */ + void drawClippedToSaveBehind(const SkPaint&); + + void resetForNextPicture(const SkIRect& bounds); + + // needs gettotalclip() + friend class SkCanvasStateUtils; + + void init(sk_sp); + + // All base onDrawX() functions should call this and skip drawing if it returns true. + // If 'matrix' is non-null, it maps the paint's fast bounds before checking for quick rejection + bool internalQuickReject(const SkRect& bounds, const SkPaint& paint, + const SkMatrix* matrix = nullptr); + + void internalDrawPaint(const SkPaint& paint); + void internalSaveLayer(const SaveLayerRec&, SaveLayerStrategy, bool coverageOnly=false); + void internalSaveBehind(const SkRect*); + + void internalConcat44(const SkM44&); + + // shared by save() and saveLayer() + void internalSave(); + void internalRestore(); + + enum class DeviceCompatibleWithFilter : bool { + // Check the src device's local-to-device matrix for compatibility with the filter, and if + // it is not compatible, introduce an intermediate image and transformation that allows the + // filter to be evaluated on the modified src content. + kUnknown = false, + // Assume that the src device's local-to-device matrix is compatible with the filter. + kYes = true + }; + /** + * Filters the contents of 'src' and draws the result into 'dst'. The filter is evaluated + * relative to the current canvas matrix, and src is drawn to dst using their relative transform + * 'paint' is applied after the filter and must not have a mask or image filter of its own. + * A null 'filter' behaves as if the identity filter were used. + * + * 'scaleFactor' is an extra uniform scale transform applied to downscale the 'src' image + * before any filtering, or as part of the copy, and is then drawn with 1/scaleFactor to 'dst'. + * Must be 1.0 if 'compat' is kYes (i.e. any scale factor has already been baked into the + * relative transforms between the devices). + */ + void internalDrawDeviceWithFilter(SkDevice* src, SkDevice* dst, + FilterSpan filters, const SkPaint& paint, + DeviceCompatibleWithFilter compat, + const SkColorInfo& filterColorInfo, + SkScalar scaleFactor = 1.f, + bool srcIsCoverageLayer = false); + + /* + * Returns true if drawing the specified rect (or all if it is null) with the specified + * paint (or default if null) would overwrite the entire root device of the canvas + * (i.e. the canvas' surface if it had one). + */ + bool wouldOverwriteEntireSurface(const SkRect*, const SkPaint*, + SkEnumBitMask) const; + + /** + * Returns true if the clip (for any active layer) contains antialiasing. + * If the clip is empty, this will return false. + */ + bool androidFramework_isClipAA() const; + + /** + * Reset the clip to be wide-open (modulo any separately specified device clip restriction). + * This operate within the save/restore clip stack so it can be undone by restoring to an + * earlier save point. + */ + void internal_private_resetClip(); + + virtual SkPaintFilterCanvas* internal_private_asPaintFilterCanvas() const { return nullptr; } + + // Keep track of the device clip bounds in the canvas' global space to reject draws before + // invoking the top-level device. + SkRect fQuickRejectBounds; + + // Compute the clip's bounds based on all clipped SkDevice's reported device bounds transformed + // 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. + std::optional attemptBlurredRRectDraw(const SkRRect&, + const SkPaint&, + SkEnumBitMask); + + class AutoUpdateQRBounds; + void validateClip() const; + + std::unique_ptr fScratchGlyphRunBuilder; +}; + +/** \class SkAutoCanvasRestore + Stack helper class calls SkCanvas::restoreToCount when SkAutoCanvasRestore + goes out of scope. Use this to guarantee that the canvas is restored to a known + state. +*/ +class SkAutoCanvasRestore { +public: + + /** Preserves SkCanvas::save() count. Optionally saves SkCanvas clip and SkCanvas matrix. + + @param canvas SkCanvas to guard + @param doSave call SkCanvas::save() + @return utility to restore SkCanvas state on destructor + */ + SkAutoCanvasRestore(SkCanvas* canvas, bool doSave) : fCanvas(canvas), fSaveCount(0) { + if (fCanvas) { + fSaveCount = canvas->getSaveCount(); + if (doSave) { + canvas->save(); + } + } + } + + /** Restores SkCanvas to saved state. Destructor is called when container goes out of + scope. + */ + ~SkAutoCanvasRestore() { + if (fCanvas) { + fCanvas->restoreToCount(fSaveCount); + } + } + + /** Restores SkCanvas to saved state immediately. Subsequent calls and + ~SkAutoCanvasRestore() have no effect. + */ + void restore() { + if (fCanvas) { + fCanvas->restoreToCount(fSaveCount); + fCanvas = nullptr; + } + } + +private: + SkCanvas* fCanvas; + int fSaveCount; + + SkAutoCanvasRestore(SkAutoCanvasRestore&&) = delete; + SkAutoCanvasRestore(const SkAutoCanvasRestore&) = delete; + SkAutoCanvasRestore& operator=(SkAutoCanvasRestore&&) = delete; + SkAutoCanvasRestore& operator=(const SkAutoCanvasRestore&) = delete; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkCanvasVirtualEnforcer.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkCanvasVirtualEnforcer.h new file mode 100644 index 0000000000..5086b4337d --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkCanvasVirtualEnforcer.h @@ -0,0 +1,61 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkCanvasVirtualEnforcer_DEFINED +#define SkCanvasVirtualEnforcer_DEFINED + +#include "include/core/SkCanvas.h" + +// If you would ordinarily want to inherit from Base (eg SkCanvas, SkNWayCanvas), instead +// inherit from SkCanvasVirtualEnforcer, which will make the build fail if you forget +// to override one of SkCanvas' key virtual hooks. +template +class SkCanvasVirtualEnforcer : public Base { +public: + using Base::Base; + +protected: + void onDrawPaint(const SkPaint& paint) override = 0; + void onDrawBehind(const SkPaint&) override {} // make zero after android updates + void onDrawRect(const SkRect& rect, const SkPaint& paint) override = 0; + void onDrawRRect(const SkRRect& rrect, const SkPaint& paint) override = 0; + void onDrawDRRect(const SkRRect& outer, const SkRRect& inner, + const SkPaint& paint) override = 0; + void onDrawOval(const SkRect& rect, const SkPaint& paint) override = 0; + void onDrawArc(const SkRect& rect, SkScalar startAngle, SkScalar sweepAngle, bool useCenter, + const SkPaint& paint) override = 0; + void onDrawPath(const SkPath& path, const SkPaint& paint) override = 0; + void onDrawRegion(const SkRegion& region, const SkPaint& paint) override = 0; + + void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, + const SkPaint& paint) override = 0; + + void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], + const SkPoint texCoords[4], SkBlendMode mode, + const SkPaint& paint) override = 0; + void onDrawPoints(SkCanvas::PointMode mode, size_t count, const SkPoint pts[], + const SkPaint& paint) override = 0; + +#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK + // This is under active development for Chrome and not used in Android. Hold off on adding + // implementations in Android's SkCanvas subclasses until this stabilizes. + void onDrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4], + SkCanvas::QuadAAFlags aaFlags, const SkColor4f& color, SkBlendMode mode) override {} +#else + void onDrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4], + SkCanvas::QuadAAFlags aaFlags, const SkColor4f& color, SkBlendMode mode) override = 0; +#endif + + void onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) override = 0; + void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override = 0; + + void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override = 0; + void onDrawPicture(const SkPicture* picture, const SkMatrix* matrix, + const SkPaint* paint) override = 0; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkCapabilities.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkCapabilities.h new file mode 100644 index 0000000000..3053c57559 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkCapabilities.h @@ -0,0 +1,39 @@ +/* + * 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 SkCapabilities_DEFINED +#define SkCapabilities_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkTypes.h" +#include "include/sksl/SkSLVersion.h" + +namespace SkSL { struct ShaderCaps; } + +#if defined(SK_GRAPHITE) +namespace skgpu::graphite { class Caps; } +#endif + +class SK_API SkCapabilities : public SkRefCnt { +public: + static sk_sp RasterBackend(); + + SkSL::Version skslVersion() const { return fSkSLVersion; } + +protected: +#if defined(SK_GRAPHITE) + friend class skgpu::graphite::Caps; // for ctor +#endif + + SkCapabilities() = default; + + void initSkCaps(const SkSL::ShaderCaps*); + + SkSL::Version fSkSLVersion = SkSL::Version::k100; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkClipOp.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkClipOp.h new file mode 100644 index 0000000000..3da6c61131 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkClipOp.h @@ -0,0 +1,19 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkClipOp_DEFINED +#define SkClipOp_DEFINED + +#include "include/core/SkTypes.h" + +enum class SkClipOp { + kDifference = 0, + kIntersect = 1, + kMax_EnumValue = kIntersect +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkColor.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkColor.h new file mode 100644 index 0000000000..33352c7a83 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkColor.h @@ -0,0 +1,447 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkColor_DEFINED +#define SkColor_DEFINED + +#include "include/core/SkAlphaType.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" +#include "include/private/base/SkCPUTypes.h" + +#include +#include + +/** \file SkColor.h + + Types, consts, functions, and macros for colors. +*/ + +/** 8-bit type for an alpha value. 255 is 100% opaque, zero is 100% transparent. +*/ +typedef uint8_t SkAlpha; + +/** 32-bit ARGB color value, unpremultiplied. Color components are always in + a known order. This is different from SkPMColor, which has its bytes in a configuration + dependent order, to match the format of kBGRA_8888_SkColorType bitmaps. SkColor + is the type used to specify colors in SkPaint and in gradients. + + Color that is premultiplied has the same component values as color + that is unpremultiplied if alpha is 255, fully opaque, although may have the + component values in a different order. +*/ +typedef uint32_t SkColor; + +/** Returns color value from 8-bit component values. Asserts if SK_DEBUG is defined + if a, r, g, or b exceed 255. Since color is unpremultiplied, a may be smaller + than the largest of r, g, and b. + + @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 color and alpha, unpremultiplied +*/ +static constexpr inline SkColor SkColorSetARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) { + return SkASSERT(a <= 255 && r <= 255 && g <= 255 && b <= 255), + (a << 24) | (r << 16) | (g << 8) | (b << 0); +} + +/** Returns color value from 8-bit component values, with alpha set + fully opaque to 255. +*/ +#define SkColorSetRGB(r, g, b) SkColorSetARGB(0xFF, r, g, b) + +/** Returns alpha byte from color value. +*/ +#define SkColorGetA(color) (((color) >> 24) & 0xFF) + +/** Returns red component of color, from zero to 255. +*/ +#define SkColorGetR(color) (((color) >> 16) & 0xFF) + +/** Returns green component of color, from zero to 255. +*/ +#define SkColorGetG(color) (((color) >> 8) & 0xFF) + +/** Returns blue component of color, from zero to 255. +*/ +#define SkColorGetB(color) (((color) >> 0) & 0xFF) + +/** Returns unpremultiplied color with red, blue, and green set from c; and alpha set + from a. Alpha component of c is ignored and is replaced by a in result. + + @param c packed RGB, eight bits per component + @param a alpha: transparent at zero, fully opaque at 255 + @return color with transparency +*/ +[[nodiscard]] static constexpr inline SkColor SkColorSetA(SkColor c, U8CPU a) { + return (c & 0x00FFFFFF) | (a << 24); +} + +/** Represents fully transparent SkAlpha value. SkAlpha ranges from zero, + fully transparent; to 255, fully opaque. +*/ +constexpr SkAlpha SK_AlphaTRANSPARENT = 0x00; + +/** Represents fully opaque SkAlpha value. SkAlpha ranges from zero, + fully transparent; to 255, fully opaque. +*/ +constexpr SkAlpha SK_AlphaOPAQUE = 0xFF; + +/** Represents fully transparent SkColor. May be used to initialize a destination + containing a mask or a non-rectangular image. +*/ +constexpr SkColor SK_ColorTRANSPARENT = SkColorSetARGB(0x00, 0x00, 0x00, 0x00); + +/** Represents fully opaque black. +*/ +constexpr SkColor SK_ColorBLACK = SkColorSetARGB(0xFF, 0x00, 0x00, 0x00); + +/** Represents fully opaque dark gray. + Note that SVG dark gray is equivalent to 0xFFA9A9A9. +*/ +constexpr SkColor SK_ColorDKGRAY = SkColorSetARGB(0xFF, 0x44, 0x44, 0x44); + +/** Represents fully opaque gray. + Note that HTML gray is equivalent to 0xFF808080. +*/ +constexpr SkColor SK_ColorGRAY = SkColorSetARGB(0xFF, 0x88, 0x88, 0x88); + +/** Represents fully opaque light gray. HTML silver is equivalent to 0xFFC0C0C0. + Note that SVG light gray is equivalent to 0xFFD3D3D3. +*/ +constexpr SkColor SK_ColorLTGRAY = SkColorSetARGB(0xFF, 0xCC, 0xCC, 0xCC); + +/** Represents fully opaque white. +*/ +constexpr SkColor SK_ColorWHITE = SkColorSetARGB(0xFF, 0xFF, 0xFF, 0xFF); + +/** Represents fully opaque red. +*/ +constexpr SkColor SK_ColorRED = SkColorSetARGB(0xFF, 0xFF, 0x00, 0x00); + +/** Represents fully opaque green. HTML lime is equivalent. + Note that HTML green is equivalent to 0xFF008000. +*/ +constexpr SkColor SK_ColorGREEN = SkColorSetARGB(0xFF, 0x00, 0xFF, 0x00); + +/** Represents fully opaque blue. +*/ +constexpr SkColor SK_ColorBLUE = SkColorSetARGB(0xFF, 0x00, 0x00, 0xFF); + +/** Represents fully opaque yellow. +*/ +constexpr SkColor SK_ColorYELLOW = SkColorSetARGB(0xFF, 0xFF, 0xFF, 0x00); + +/** Represents fully opaque cyan. HTML aqua is equivalent. +*/ +constexpr SkColor SK_ColorCYAN = SkColorSetARGB(0xFF, 0x00, 0xFF, 0xFF); + +/** Represents fully opaque magenta. HTML fuchsia is equivalent. +*/ +constexpr SkColor SK_ColorMAGENTA = SkColorSetARGB(0xFF, 0xFF, 0x00, 0xFF); + +/** Converts RGB to its HSV components. + hsv[0] contains hsv hue, a value from zero to less than 360. + hsv[1] contains hsv saturation, a value from zero to one. + hsv[2] contains hsv value, a value from zero to one. + + @param red red component value from zero to 255 + @param green green component value from zero to 255 + @param blue blue component value from zero to 255 + @param hsv three element array which holds the resulting HSV components +*/ +SK_API void SkRGBToHSV(U8CPU red, U8CPU green, U8CPU blue, SkScalar hsv[3]); + +/** Converts ARGB to its HSV components. Alpha in ARGB is ignored. + hsv[0] contains hsv hue, and is assigned a value from zero to less than 360. + hsv[1] contains hsv saturation, a value from zero to one. + hsv[2] contains hsv value, a value from zero to one. + + @param color ARGB color to convert + @param hsv three element array which holds the resulting HSV components +*/ +static inline void SkColorToHSV(SkColor color, SkScalar hsv[3]) { + SkRGBToHSV(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color), hsv); +} + +/** Converts HSV components to an ARGB color. Alpha is passed through unchanged. + hsv[0] represents hsv hue, an angle from zero to less than 360. + hsv[1] represents hsv saturation, and varies from zero to one. + hsv[2] represents hsv value, and varies from zero to one. + + Out of range hsv values are pinned. + + @param alpha alpha component of the returned ARGB color + @param hsv three element array which holds the input HSV components + @return ARGB equivalent to HSV +*/ +SK_API SkColor SkHSVToColor(U8CPU alpha, const SkScalar hsv[3]); + +/** Converts HSV components to an ARGB color. Alpha is set to 255. + hsv[0] represents hsv hue, an angle from zero to less than 360. + hsv[1] represents hsv saturation, and varies from zero to one. + hsv[2] represents hsv value, and varies from zero to one. + + Out of range hsv values are pinned. + + @param hsv three element array which holds the input HSV components + @return RGB equivalent to HSV +*/ +static inline SkColor SkHSVToColor(const SkScalar hsv[3]) { + return SkHSVToColor(0xFF, hsv); +} + +/** 32-bit ARGB color value, premultiplied. The byte order for this value is + configuration dependent, matching the format of kBGRA_8888_SkColorType bitmaps. + This is different from SkColor, which is unpremultiplied, and is always in the + same byte order. +*/ +typedef uint32_t SkPMColor; + +/** Returns a SkPMColor value from unpremultiplied 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 SkPreMultiplyARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b); + +/** Returns pmcolor closest to color c. Multiplies c RGB components by the c alpha, + and arranges the bytes to match the format of kN32_SkColorType. + + @param c unpremultiplied ARGB color + @return premultiplied color +*/ +SK_API SkPMColor SkPreMultiplyColor(SkColor c); + +/** \enum SkColorChannel + Describes different color channels one can manipulate +*/ +enum class SkColorChannel { + kR, // the red channel + kG, // the green channel + kB, // the blue channel + kA, // the alpha channel + + kLastEnum = kA, +}; + +/** Used to represent the channels available in a color type or texture format as a mask. */ +enum SkColorChannelFlag : uint32_t { + kRed_SkColorChannelFlag = 1 << static_cast(SkColorChannel::kR), + kGreen_SkColorChannelFlag = 1 << static_cast(SkColorChannel::kG), + kBlue_SkColorChannelFlag = 1 << static_cast(SkColorChannel::kB), + kAlpha_SkColorChannelFlag = 1 << static_cast(SkColorChannel::kA), + kGray_SkColorChannelFlag = 0x10, + // Convenience values + kGrayAlpha_SkColorChannelFlags = kGray_SkColorChannelFlag | kAlpha_SkColorChannelFlag, + kRG_SkColorChannelFlags = kRed_SkColorChannelFlag | kGreen_SkColorChannelFlag, + kRGB_SkColorChannelFlags = kRG_SkColorChannelFlags | kBlue_SkColorChannelFlag, + kRGBA_SkColorChannelFlags = kRGB_SkColorChannelFlags | kAlpha_SkColorChannelFlag, +}; +static_assert(0 == (kGray_SkColorChannelFlag & kRGBA_SkColorChannelFlags), "bitfield conflict"); + +/** \struct SkRGBA4f + RGBA color value, holding four floating point components. Color components are always in + a known order. kAT determines if the SkRGBA4f's R, G, and B components are premultiplied + by alpha or not. + + Skia's public API always uses unpremultiplied colors, which can be stored as + SkRGBA4f. For convenience, this type can also be referred to + as SkColor4f. +*/ +template +struct SkRGBA4f { + float fR; //!< red component + float fG; //!< green component + float fB; //!< blue component + float fA; //!< alpha component + + /** Compares SkRGBA4f with other, and returns true if all components are equal. + + @param other SkRGBA4f to compare + @return true if SkRGBA4f equals other + */ + bool operator==(const SkRGBA4f& other) const { + return fA == other.fA && fR == other.fR && fG == other.fG && fB == other.fB; + } + + /** Compares SkRGBA4f with other, and returns true if not all components are equal. + + @param other SkRGBA4f to compare + @return true if SkRGBA4f is not equal to other + */ + bool operator!=(const SkRGBA4f& other) const { + return !(*this == other); + } + + /** Returns SkRGBA4f multiplied by scale. + + @param scale value to multiply by + @return SkRGBA4f as (fR * scale, fG * scale, fB * scale, fA * scale) + */ + SkRGBA4f operator*(float scale) const { + return { fR * scale, fG * scale, fB * scale, fA * scale }; + } + + /** Returns SkRGBA4f multiplied component-wise by scale. + + @param scale SkRGBA4f to multiply by + @return SkRGBA4f as (fR * scale.fR, fG * scale.fG, fB * scale.fB, fA * scale.fA) + */ + SkRGBA4f operator*(const SkRGBA4f& scale) const { + return { fR * scale.fR, fG * scale.fG, fB * scale.fB, fA * scale.fA }; + } + + /** Returns a pointer to components of SkRGBA4f, for array access. + + @return pointer to array [fR, fG, fB, fA] + */ + const float* vec() const { return &fR; } + + /** Returns a pointer to components of SkRGBA4f, for array access. + + @return pointer to array [fR, fG, fB, fA] + */ + float* vec() { return &fR; } + + /** As a std::array */ + std::array array() const { return {fR, fG, fB, fA}; } + + /** Returns one component. Asserts if index is out of range and SK_DEBUG is defined. + + @param index one of: 0 (fR), 1 (fG), 2 (fB), 3 (fA) + @return value corresponding to index + */ + float operator[](int index) const { + SkASSERT(index >= 0 && index < 4); + return this->vec()[index]; + } + + /** Returns one component. Asserts if index is out of range and SK_DEBUG is defined. + + @param index one of: 0 (fR), 1 (fG), 2 (fB), 3 (fA) + @return value corresponding to index + */ + float& operator[](int index) { + SkASSERT(index >= 0 && index < 4); + return this->vec()[index]; + } + + /** Returns true if SkRGBA4f is an opaque color. Asserts if fA is out of range and + SK_DEBUG is defined. + + @return true if SkRGBA4f is opaque + */ + bool isOpaque() const { + SkASSERT(fA <= 1.0f && fA >= 0.0f); + return fA == 1.0f; + } + + /** Returns true if all channels are in [0, 1]. */ + bool fitsInBytes() const { + SkASSERT(fA >= 0.0f && fA <= 1.0f); + return fR >= 0.0f && fR <= 1.0f && + fG >= 0.0f && fG <= 1.0f && + fB >= 0.0f && fB <= 1.0f; + } + + /** Returns closest SkRGBA4f to SkColor. Only allowed if SkRGBA4f is unpremultiplied. + + @param color Color with Alpha, red, blue, and green components + @return SkColor as SkRGBA4f + + example: https://fiddle.skia.org/c/@RGBA4f_FromColor + */ + static SkRGBA4f FromColor(SkColor color); // impl. depends on kAT + + /** Returns closest SkColor to SkRGBA4f. Only allowed if SkRGBA4f is unpremultiplied. + + @return color as SkColor + + example: https://fiddle.skia.org/c/@RGBA4f_toSkColor + */ + SkColor toSkColor() const; // impl. depends on kAT + + /** Returns closest SkRGBA4f to SkPMColor. Only allowed if SkRGBA4f is premultiplied. + + @return SkPMColor as SkRGBA4f + */ + static SkRGBA4f FromPMColor(SkPMColor); // impl. depends on kAT + + /** Returns SkRGBA4f premultiplied by alpha. Asserts at compile time if SkRGBA4f is + already premultiplied. + + @return premultiplied color + */ + SkRGBA4f premul() const { + static_assert(kAT == kUnpremul_SkAlphaType, ""); + return { fR * fA, fG * fA, fB * fA, fA }; + } + + /** Returns SkRGBA4f unpremultiplied by alpha. Asserts at compile time if SkRGBA4f is + already unpremultiplied. + + @return unpremultiplied color + */ + SkRGBA4f unpremul() const { + static_assert(kAT == kPremul_SkAlphaType, ""); + + if (fA == 0.0f) { + return { 0, 0, 0, 0 }; + } else { + float invAlpha = 1 / fA; + return { fR * invAlpha, fG * invAlpha, fB * invAlpha, fA }; + } + } + + // This produces bytes in RGBA order (eg GrColor). Impl. is the same, regardless of kAT + uint32_t toBytes_RGBA() const; + static SkRGBA4f FromBytes_RGBA(uint32_t color); + + /** + Returns a copy of the SkRGBA4f but with alpha component set to 1.0f. + + @return opaque color + */ + SkRGBA4f makeOpaque() const { + return { fR, fG, fB, 1.0f }; + } +}; + +/** \struct SkColor4f + RGBA color value, holding four floating point components. Color components are always in + a known order, and are unpremultiplied. + + This is a specialization of SkRGBA4f. For details, @see SkRGBA4f. +*/ +using SkColor4f = SkRGBA4f; + +template <> SK_API SkColor4f SkColor4f::FromColor(SkColor); +template <> SK_API SkColor SkColor4f::toSkColor() const; +template <> SK_API uint32_t SkColor4f::toBytes_RGBA() const; +template <> SK_API SkColor4f SkColor4f::FromBytes_RGBA(uint32_t color); + +namespace SkColors { +constexpr SkColor4f kTransparent = {0, 0, 0, 0}; +constexpr SkColor4f kBlack = {0, 0, 0, 1}; +constexpr SkColor4f kDkGray = {0.25f, 0.25f, 0.25f, 1}; +constexpr SkColor4f kGray = {0.50f, 0.50f, 0.50f, 1}; +constexpr SkColor4f kLtGray = {0.75f, 0.75f, 0.75f, 1}; +constexpr SkColor4f kWhite = {1, 1, 1, 1}; +constexpr SkColor4f kRed = {1, 0, 0, 1}; +constexpr SkColor4f kGreen = {0, 1, 0, 1}; +constexpr SkColor4f kBlue = {0, 0, 1, 1}; +constexpr SkColor4f kYellow = {1, 1, 0, 1}; +constexpr SkColor4f kCyan = {0, 1, 1, 1}; +constexpr SkColor4f kMagenta = {1, 0, 1, 1}; +} // namespace SkColors +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkColorFilter.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkColorFilter.h new file mode 100644 index 0000000000..0898f0acad --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkColorFilter.h @@ -0,0 +1,156 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkColorFilter_DEFINED +#define SkColorFilter_DEFINED + +#include "include/core/SkColor.h" +#include "include/core/SkFlattenable.h" +#include "include/core/SkRefCnt.h" +#include "include/private/base/SkAPI.h" + +#include +#include +#include + +class SkColorMatrix; +class SkColorSpace; +class SkColorTable; + +enum class SkBlendMode; +struct SkDeserialProcs; + +/** +* ColorFilters are optional objects in the drawing pipeline. When present in +* a paint, they are called with the "src" colors, and return new colors, which +* are then passed onto the next stage (either ImageFilter or Xfermode). +* +* All subclasses are required to be reentrant-safe : it must be legal to share +* the same instance between several threads. +*/ +class SK_API SkColorFilter : public SkFlattenable { +public: + /** If the filter can be represented by a source color plus Mode, this + * returns true, and sets (if not NULL) the color and mode appropriately. + * If not, this returns false and ignores the parameters. + */ + bool asAColorMode(SkColor* color, SkBlendMode* mode) const; + + /** If the filter can be represented by a 5x4 matrix, this + * returns true, and sets the matrix appropriately. + * If not, this returns false and ignores the parameter. + */ + bool asAColorMatrix(float matrix[20]) const; + + // Returns true if the filter is guaranteed to never change the alpha of a color it filters. + bool isAlphaUnchanged() const; + + /** + * Applies this filter to the input color. This function does no color management. + * + * DEPRECATED: Please use filterColor4f instead. That function supports higher precision, + * wide-gamut color, and is explicit about the color space of the input and output. + */ + SkColor filterColor(SkColor) const; + + /** + * Converts the src color (in src colorspace), into the dst colorspace, + * then applies this filter to it, returning the filtered color in the dst colorspace. + */ + SkColor4f filterColor4f(const SkColor4f& srcColor, SkColorSpace* srcCS, + SkColorSpace* dstCS) const; + + /** Construct a colorfilter whose effect is to first apply the inner filter and then apply + * this filter, applied to the output of the inner filter. + * + * result = this(inner(...)) + */ + sk_sp makeComposed(sk_sp inner) const; + + /** Return a colorfilter that will compute this filter in a specific color space. By default all + * filters operate in the destination (surface) color space. This allows filters like Blend and + * Matrix, or runtime color filters to perform their math in a known space. + */ + sk_sp makeWithWorkingColorSpace(sk_sp) const; + + static sk_sp Deserialize(const void* data, size_t size, + const SkDeserialProcs* procs = nullptr); + +private: + SkColorFilter() = default; + friend class SkColorFilterBase; + + using INHERITED = SkFlattenable; +}; + +class SK_API SkColorFilters { +public: + static sk_sp Compose(const sk_sp& outer, + sk_sp inner) { + return outer ? outer->makeComposed(std::move(inner)) + : std::move(inner); + } + + // Blends between the constant color (src) and input color (dst) based on the SkBlendMode. + // If the color space is null, the constant color is assumed to be defined in sRGB. + static sk_sp Blend(const SkColor4f& c, sk_sp, SkBlendMode mode); + static sk_sp Blend(SkColor c, SkBlendMode mode); + + static sk_sp Matrix(const SkColorMatrix&); + static sk_sp Matrix(const float rowMajor[20]); + + // A version of Matrix which operates in HSLA space instead of RGBA. + // I.e. HSLA-to-RGBA(Matrix(RGBA-to-HSLA(input))). + static sk_sp HSLAMatrix(const SkColorMatrix&); + static sk_sp HSLAMatrix(const float rowMajor[20]); + + static sk_sp LinearToSRGBGamma(); + static sk_sp SRGBToLinearGamma(); + static sk_sp Lerp(float t, sk_sp dst, sk_sp src); + + /** + * Create a table colorfilter, copying the table into the filter, and + * applying it to all 4 components. + * a' = table[a]; + * r' = table[r]; + * g' = table[g]; + * b' = table[b]; + * Components are operated on in unpremultiplied space. If the incomming + * colors are premultiplied, they are temporarily unpremultiplied, then + * the table is applied, and then the result is remultiplied. + */ + static sk_sp Table(const uint8_t table[256]); + + /** + * Create a table colorfilter, with a different table for each + * component [A, R, G, B]. If a given table is NULL, then it is + * treated as identity, with the component left unchanged. If a table + * is not null, then its contents are copied into the filter. + */ + static sk_sp TableARGB(const uint8_t tableA[256], + const uint8_t tableR[256], + const uint8_t tableG[256], + const uint8_t tableB[256]); + + /** + * Create a table colorfilter that holds a ref to the shared color table. + */ + static sk_sp Table(sk_sp table); + + /** + * Create a colorfilter that multiplies the RGB channels by one color, and + * then adds a second color, pinning the result for each component to + * [0..255]. The alpha components of the mul and add arguments + * are ignored. + */ + static sk_sp Lighting(SkColor mul, SkColor add); + +private: + SkColorFilters() = delete; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkColorPriv.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkColorPriv.h new file mode 100644 index 0000000000..f89de9db72 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkColorPriv.h @@ -0,0 +1,167 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkColorPriv_DEFINED +#define SkColorPriv_DEFINED + +#include "include/core/SkColor.h" +#include "include/private/base/SkMath.h" +#include "include/private/base/SkTPin.h" +#include "include/private/base/SkTo.h" + +#include + +/** Turn 0..255 into 0..256 by adding 1 at the half-way point. Used to turn a + byte into a scale value, so that we can say scale * value >> 8 instead of + alpha * value / 255. + + In debugging, asserts that alpha is 0..255 +*/ +static inline unsigned SkAlpha255To256(U8CPU alpha) { + SkASSERT(SkToU8(alpha) == alpha); + // this one assues that blending on top of an opaque dst keeps it that way + // even though it is less accurate than a+(a>>7) for non-opaque dsts + return alpha + 1; +} + +/** Multiplify value by 0..256, and shift the result down 8 + (i.e. return (value * alpha256) >> 8) + */ +#define SkAlphaMul(value, alpha256) (((value) * (alpha256)) >> 8) + +static inline U8CPU SkUnitScalarClampToByte(SkScalar x) { + return static_cast(SkTPin(x, 0.0f, 1.0f) * 255 + 0.5); +} + +#define SK_A32_BITS 8 +#define SK_R32_BITS 8 +#define SK_G32_BITS 8 +#define SK_B32_BITS 8 + +#define SK_A32_MASK ((1 << SK_A32_BITS) - 1) +#define SK_R32_MASK ((1 << SK_R32_BITS) - 1) +#define SK_G32_MASK ((1 << SK_G32_BITS) - 1) +#define SK_B32_MASK ((1 << SK_B32_BITS) - 1) + +/* + * Skia's 32bit backend only supports 1 swizzle order at a time (compile-time). + * This is specified by SK_R32_SHIFT=0 or SK_R32_SHIFT=16. + * + * For easier compatibility with Skia's GPU backend, we further restrict these + * to either (in memory-byte-order) RGBA or BGRA. Note that this "order" does + * not directly correspond to the same shift-order, since we have to take endianess + * into account. + * + * Here we enforce this constraint. + */ + +#define SK_RGBA_R32_SHIFT 0 +#define SK_RGBA_G32_SHIFT 8 +#define SK_RGBA_B32_SHIFT 16 +#define SK_RGBA_A32_SHIFT 24 + +#define SK_BGRA_B32_SHIFT 0 +#define SK_BGRA_G32_SHIFT 8 +#define SK_BGRA_R32_SHIFT 16 +#define SK_BGRA_A32_SHIFT 24 + +#if defined(SK_PMCOLOR_IS_RGBA) || defined(SK_PMCOLOR_IS_BGRA) + #error "Configure PMCOLOR by setting SK_R32_SHIFT." +#endif + +// Deduce which SK_PMCOLOR_IS_ to define from the _SHIFT defines + +#if (SK_A32_SHIFT == SK_RGBA_A32_SHIFT && \ + SK_R32_SHIFT == SK_RGBA_R32_SHIFT && \ + SK_G32_SHIFT == SK_RGBA_G32_SHIFT && \ + SK_B32_SHIFT == SK_RGBA_B32_SHIFT) + #define SK_PMCOLOR_IS_RGBA +#elif (SK_A32_SHIFT == SK_BGRA_A32_SHIFT && \ + SK_R32_SHIFT == SK_BGRA_R32_SHIFT && \ + SK_G32_SHIFT == SK_BGRA_G32_SHIFT && \ + SK_B32_SHIFT == SK_BGRA_B32_SHIFT) + #define SK_PMCOLOR_IS_BGRA +#else + #error "need 32bit packing to be either RGBA or BGRA" +#endif + +#define SkGetPackedA32(packed) ((uint32_t)((packed) << (24 - SK_A32_SHIFT)) >> 24) +#define SkGetPackedR32(packed) ((uint32_t)((packed) << (24 - SK_R32_SHIFT)) >> 24) +#define SkGetPackedG32(packed) ((uint32_t)((packed) << (24 - SK_G32_SHIFT)) >> 24) +#define SkGetPackedB32(packed) ((uint32_t)((packed) << (24 - SK_B32_SHIFT)) >> 24) + +#define SkA32Assert(a) SkASSERT((unsigned)(a) <= SK_A32_MASK) +#define SkR32Assert(r) SkASSERT((unsigned)(r) <= SK_R32_MASK) +#define SkG32Assert(g) SkASSERT((unsigned)(g) <= SK_G32_MASK) +#define SkB32Assert(b) SkASSERT((unsigned)(b) <= SK_B32_MASK) + +/** + * Pack the components into a SkPMColor, checking (in the debug version) that + * the components are 0..255, and are already premultiplied (i.e. alpha >= color) + */ +static inline SkPMColor SkPackARGB32(U8CPU a, U8CPU r, U8CPU g, U8CPU b) { + SkA32Assert(a); + SkASSERT(r <= a); + SkASSERT(g <= a); + SkASSERT(b <= a); + + return (a << SK_A32_SHIFT) | (r << SK_R32_SHIFT) | + (g << SK_G32_SHIFT) | (b << SK_B32_SHIFT); +} + +/** + * Same as SkPackARGB32, but this version guarantees to not check that the + * values are premultiplied in the debug version. + */ +static inline SkPMColor SkPackARGB32NoCheck(U8CPU a, U8CPU r, U8CPU g, U8CPU b) { + return (a << SK_A32_SHIFT) | (r << SK_R32_SHIFT) | + (g << SK_G32_SHIFT) | (b << SK_B32_SHIFT); +} + +static inline +SkPMColor SkPremultiplyARGBInline(U8CPU a, U8CPU r, U8CPU g, U8CPU b) { + SkA32Assert(a); + SkR32Assert(r); + SkG32Assert(g); + SkB32Assert(b); + + if (a != 255) { + r = SkMulDiv255Round(r, a); + g = SkMulDiv255Round(g, a); + b = SkMulDiv255Round(b, a); + } + return SkPackARGB32(a, r, g, b); +} + +// When Android is compiled optimizing for size, SkAlphaMulQ doesn't get +// inlined; forcing inlining significantly improves performance. +static SK_ALWAYS_INLINE uint32_t SkAlphaMulQ(uint32_t c, unsigned scale) { + uint32_t mask = 0xFF00FF; + + uint32_t rb = ((c & mask) * scale) >> 8; + uint32_t ag = ((c >> 8) & mask) * scale; + return (rb & mask) | (ag & ~mask); +} + +static inline SkPMColor SkPMSrcOver(SkPMColor src, SkPMColor dst) { + uint32_t scale = SkAlpha255To256(255 - SkGetPackedA32(src)); + + uint32_t mask = 0xFF00FF; + uint32_t rb = (((dst & mask) * scale) >> 8) & mask; + uint32_t ag = (((dst >> 8) & mask) * scale) & ~mask; + + rb += (src & mask); + ag += (src & ~mask); + + // Color channels (but not alpha) can overflow, so we have to saturate to 0xFF in each lane. + return std::min(rb & 0x000001FF, 0x000000FFU) | + std::min(ag & 0x0001FF00, 0x0000FF00U) | + std::min(rb & 0x01FF0000, 0x00FF0000U) | + (ag & 0xFF000000); +} + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkColorSpace.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkColorSpace.h new file mode 100644 index 0000000000..57c29e222a --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkColorSpace.h @@ -0,0 +1,242 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkColorSpace_DEFINED +#define SkColorSpace_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkTypes.h" +#include "include/private/base/SkFixed.h" +#include "include/private/base/SkOnce.h" +#include "modules/skcms/skcms.h" + +#include +#include + +class SkData; + +/** + * Describes a color gamut with primaries and a white point. + */ +struct SK_API SkColorSpacePrimaries { + float fRX; + float fRY; + float fGX; + float fGY; + float fBX; + float fBY; + float fWX; + float fWY; + + /** + * Convert primaries and a white point to a toXYZD50 matrix, the preferred color gamut + * representation of SkColorSpace. + */ + bool toXYZD50(skcms_Matrix3x3* toXYZD50) const; +}; + +namespace SkNamedTransferFn { + +// Like SkNamedGamut::kSRGB, keeping this bitwise exactly the same as skcms makes things fastest. +static constexpr skcms_TransferFunction kSRGB = + { 2.4f, (float)(1/1.055), (float)(0.055/1.055), (float)(1/12.92), 0.04045f, 0.0f, 0.0f }; + +static constexpr skcms_TransferFunction k2Dot2 = + { 2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; + +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}; + +static constexpr skcms_TransferFunction kPQ = + {-2.0f, -107/128.0f, 1.0f, 32/2523.0f, 2413/128.0f, -2392/128.0f, 8192/1305.0f }; + +static constexpr skcms_TransferFunction kHLG = + {-3.0f, 2.0f, 2.0f, 1/0.17883277f, 0.28466892f, 0.55991073f, 0.0f }; + +} // namespace SkNamedTransferFn + +namespace SkNamedGamut { + +static constexpr skcms_Matrix3x3 kSRGB = {{ + // ICC fixed-point (16.16) representation, taken from skcms. Please keep them exactly in sync. + // 0.436065674f, 0.385147095f, 0.143066406f, + // 0.222488403f, 0.716873169f, 0.060607910f, + // 0.013916016f, 0.097076416f, 0.714096069f, + { SkFixedToFloat(0x6FA2), SkFixedToFloat(0x6299), SkFixedToFloat(0x24A0) }, + { SkFixedToFloat(0x38F5), SkFixedToFloat(0xB785), SkFixedToFloat(0x0F84) }, + { SkFixedToFloat(0x0390), SkFixedToFloat(0x18DA), SkFixedToFloat(0xB6CF) }, +}}; + +static constexpr skcms_Matrix3x3 kAdobeRGB = {{ + // ICC fixed-point (16.16) repesentation of: + // 0.60974, 0.20528, 0.14919, + // 0.31111, 0.62567, 0.06322, + // 0.01947, 0.06087, 0.74457, + { SkFixedToFloat(0x9c18), SkFixedToFloat(0x348d), SkFixedToFloat(0x2631) }, + { SkFixedToFloat(0x4fa5), SkFixedToFloat(0xa02c), SkFixedToFloat(0x102f) }, + { SkFixedToFloat(0x04fc), SkFixedToFloat(0x0f95), SkFixedToFloat(0xbe9c) }, +}}; + +static constexpr skcms_Matrix3x3 kDisplayP3 = {{ + { 0.515102f, 0.291965f, 0.157153f }, + { 0.241182f, 0.692236f, 0.0665819f }, + { -0.00104941f, 0.0418818f, 0.784378f }, +}}; + +static constexpr skcms_Matrix3x3 kRec2020 = {{ + { 0.673459f, 0.165661f, 0.125100f }, + { 0.279033f, 0.675338f, 0.0456288f }, + { -0.00193139f, 0.0299794f, 0.797162f }, +}}; + +static constexpr skcms_Matrix3x3 kXYZ = {{ + { 1.0f, 0.0f, 0.0f }, + { 0.0f, 1.0f, 0.0f }, + { 0.0f, 0.0f, 1.0f }, +}}; + +} // namespace SkNamedGamut + +class SK_API SkColorSpace : public SkNVRefCnt { +public: + /** + * Create the sRGB color space. + */ + static sk_sp MakeSRGB(); + + /** + * Colorspace with the sRGB primaries, but a linear (1.0) gamma. + */ + static sk_sp MakeSRGBLinear(); + + /** + * Create an SkColorSpace from a transfer function and a row-major 3x3 transformation to XYZ. + */ + static sk_sp MakeRGB(const skcms_TransferFunction& transferFn, + const skcms_Matrix3x3& toXYZ); + + /** + * Create an SkColorSpace from a parsed (skcms) ICC profile. + */ + static sk_sp Make(const skcms_ICCProfile&); + + /** + * Convert this color space to an skcms ICC profile struct. + */ + void toProfile(skcms_ICCProfile*) const; + + /** + * Returns true if the color space gamma is near enough to be approximated as sRGB. + */ + bool gammaCloseToSRGB() const; + + /** + * Returns true if the color space gamma is linear. + */ + bool gammaIsLinear() const; + + /** + * Sets |fn| to the transfer function from this color space. Returns true if the transfer + * function can be represented as coefficients to the standard ICC 7-parameter equation. + * Returns false otherwise (eg, PQ, HLG). + */ + bool isNumericalTransferFn(skcms_TransferFunction* fn) const; + + /** + * Returns true and sets |toXYZD50|. + */ + bool toXYZD50(skcms_Matrix3x3* toXYZD50) const; + + /** + * Returns a hash of the gamut transformation to XYZ D50. Allows for fast equality checking + * of gamuts, at the (very small) risk of collision. + */ + uint32_t toXYZD50Hash() const { return fToXYZD50Hash; } + + /** + * Returns a color space with the same gamut as this one, but with a linear gamma. + */ + sk_sp makeLinearGamma() const; + + /** + * Returns a color space with the same gamut as this one, but with the sRGB transfer + * function. + */ + sk_sp makeSRGBGamma() const; + + /** + * Returns a color space with the same transfer function as this one, but with the primary + * colors rotated. In other words, this produces a new color space that maps RGB to GBR + * (when applied to a source), and maps RGB to BRG (when applied to a destination). + * + * This is used for testing, to construct color spaces that have severe and testable behavior. + */ + sk_sp makeColorSpin() const; + + /** + * Returns true if the color space is sRGB. + * Returns false otherwise. + * + * This allows a little bit of tolerance, given that we might see small numerical error + * in some cases: converting ICC fixed point to float, converting white point to D50, + * rounding decisions on transfer function and matrix. + * + * This does not consider a 2.2f exponential transfer function to be sRGB. While these + * functions are similar (and it is sometimes useful to consider them together), this + * function checks for logical equality. + */ + bool isSRGB() const; + + /** + * Returns a serialized representation of this color space. + */ + sk_sp serialize() const; + + /** + * If |memory| is nullptr, returns the size required to serialize. + * Otherwise, serializes into |memory| and returns the size. + */ + size_t writeToMemory(void* memory) const; + + static sk_sp Deserialize(const void* data, size_t length); + + /** + * If both are null, we return true. If one is null and the other is not, we return false. + * If both are non-null, we do a deeper compare. + */ + static bool Equals(const SkColorSpace*, const SkColorSpace*); + + void transferFn(float gabcdef[7]) const; // DEPRECATED: Remove when webview usage is gone + void transferFn(skcms_TransferFunction* fn) const; + void invTransferFn(skcms_TransferFunction* fn) const; + void gamutTransformTo(const SkColorSpace* dst, skcms_Matrix3x3* src_to_dst) const; + + uint32_t transferFnHash() const { return fTransferFnHash; } + uint64_t hash() const { return (uint64_t)fTransferFnHash << 32 | fToXYZD50Hash; } + +private: + friend class SkColorSpaceSingletonFactory; + + SkColorSpace(const skcms_TransferFunction& transferFn, const skcms_Matrix3x3& toXYZ); + + void computeLazyDstFields() const; + + uint32_t fTransferFnHash; + uint32_t fToXYZD50Hash; + + skcms_TransferFunction fTransferFn; + skcms_Matrix3x3 fToXYZD50; + + mutable skcms_TransferFunction fInvTransferFn; + mutable skcms_Matrix3x3 fFromXYZD50; + mutable SkOnce fLazyDstFieldsOnce; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkColorTable.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkColorTable.h new file mode 100644 index 0000000000..dc16048a59 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkColorTable.h @@ -0,0 +1,62 @@ +/* + * Copyright 2023 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkColorTable_DEFINED +#define SkColorTable_DEFINED + +#include "include/core/SkBitmap.h" +#include "include/core/SkRefCnt.h" +#include "include/private/base/SkAPI.h" + +#include + +class SkReadBuffer; +class SkWriteBuffer; + +/** + * SkColorTable holds the lookup tables for each channel (ARGB) used to define the filter behavior + * of `SkColorFilters::Table`, and provides a way to share the table data between client code and + * the returned SkColorFilter. Once created, an SkColorTable is immutable. +*/ +class SK_API SkColorTable : public SkRefCnt { +public: + // Creates a new SkColorTable with 'table' used for all four channels. The table is copied into + // the SkColorTable. + static sk_sp Make(const uint8_t table[256]) { + return Make(table, table, table, table); + } + + // Creates a new SkColorTable with the per-channel lookup tables. Each non-null table is copied + // into the SkColorTable. Null parameters are interpreted as the identity table. + static sk_sp Make(const uint8_t tableA[256], + const uint8_t tableR[256], + const uint8_t tableG[256], + const uint8_t tableB[256]); + + // Per-channel constant value lookup (0-255). + const uint8_t* alphaTable() const { return fTable.getAddr8(0, 0); } + const uint8_t* redTable() const { return fTable.getAddr8(0, 1); } + const uint8_t* greenTable() const { return fTable.getAddr8(0, 2); } + const uint8_t* blueTable() const { return fTable.getAddr8(0, 3); } + + void flatten(SkWriteBuffer& buffer) const; + + static sk_sp Deserialize(SkReadBuffer& buffer); + +private: + friend class SkTableColorFilter; // for bitmap() + + SkColorTable(const SkBitmap& table) : fTable(table) {} + + // The returned SkBitmap is immutable; attempting to modify its pixel data will trigger asserts + // in debug builds and cause undefined behavior in release builds. + const SkBitmap& bitmap() const { return fTable; } + + SkBitmap fTable; // A 256x4 A8 image +}; + +#endif // SkColorTable_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkColorType.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkColorType.h new file mode 100644 index 0000000000..2b2837a040 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkColorType.h @@ -0,0 +1,70 @@ +/* + * 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 SkColorType_DEFINED +#define SkColorType_DEFINED + +#include "include/core/SkTypes.h" + +/** \enum SkColorType + Describes how pixel bits encode color. A pixel may be an alpha mask, a grayscale, RGB, or ARGB. + + 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. +*/ +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 + kRGBA_F32_SkColorType, //!< pixel using C float for red, green, blue, alpha; in 128-bit word + + // 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 + + kSRGBA_8888_SkColorType, + kR8_unorm_SkColorType, + + kLastEnum_SkColorType = kR8_unorm_SkColorType, //!< last valid value + +#if SK_PMCOLOR_BYTE_ORDER(B,G,R,A) + kN32_SkColorType = kBGRA_8888_SkColorType,//!< native 32-bit BGRA encoding + +#elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A) + kN32_SkColorType = kRGBA_8888_SkColorType,//!< native 32-bit RGBA encoding + +#else + #error "SK_*32_SHIFT values must correspond to BGRA or RGBA byte order" +#endif +}; +static constexpr int kSkColorTypeCnt = static_cast(kLastEnum_SkColorType) + 1; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkContourMeasure.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkContourMeasure.h new file mode 100644 index 0000000000..29e33d84f3 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkContourMeasure.h @@ -0,0 +1,139 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkContourMeasure_DEFINED +#define SkContourMeasure_DEFINED + +#include "include/core/SkPoint.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkScalar.h" +#include "include/private/base/SkAPI.h" +#include "include/private/base/SkTDArray.h" + +#include + +class SkMatrix; +class SkPath; + +class SK_API SkContourMeasure : public SkRefCnt { +public: + /** Return the length of the contour. + */ + SkScalar length() const { return fLength; } + + /** Pins distance to 0 <= distance <= length(), and then computes the corresponding + * position and tangent. + */ + [[nodiscard]] bool getPosTan(SkScalar distance, SkPoint* position, SkVector* tangent) const; + + enum MatrixFlags { + kGetPosition_MatrixFlag = 0x01, + kGetTangent_MatrixFlag = 0x02, + kGetPosAndTan_MatrixFlag = kGetPosition_MatrixFlag | kGetTangent_MatrixFlag + }; + + /** Pins distance to 0 <= distance <= getLength(), and then computes + the corresponding matrix (by calling getPosTan). + Returns false if there is no path, or a zero-length path was specified, in which case + matrix is unchanged. + */ + [[nodiscard]] bool getMatrix(SkScalar distance, SkMatrix* matrix, + MatrixFlags flags = kGetPosAndTan_MatrixFlag) const; + + /** Given a start and stop distance, return in dst the intervening segment(s). + If the segment is zero-length, return false, else return true. + startD and stopD are pinned to legal values (0..getLength()). If startD > stopD + then return false (and leave dst untouched). + Begin the segment with a moveTo if startWithMoveTo is true + */ + [[nodiscard]] bool getSegment(SkScalar startD, SkScalar stopD, SkPath* dst, + bool startWithMoveTo) const; + + /** Return true if the contour is closed() + */ + bool isClosed() const { return fIsClosed; } + +private: + struct Segment { + SkScalar fDistance; // total distance up to this point + unsigned fPtIndex; // index into the fPts array + unsigned fTValue : 30; + unsigned fType : 2; // actually the enum SkSegType + // See SkPathMeasurePriv.h + + SkScalar getScalarT() const; + + static const Segment* Next(const Segment* seg) { + unsigned ptIndex = seg->fPtIndex; + do { + ++seg; + } while (seg->fPtIndex == ptIndex); + return seg; + } + + }; + + const SkTDArray fSegments; + const SkTDArray fPts; // Points used to define the segments + + const SkScalar fLength; + const bool fIsClosed; + + SkContourMeasure(SkTDArray&& segs, SkTDArray&& pts, + SkScalar length, bool isClosed); + ~SkContourMeasure() override {} + + const Segment* distanceToSegment(SkScalar distance, SkScalar* t) const; + + friend class SkContourMeasureIter; + friend class SkPathMeasurePriv; +}; + +class SK_API SkContourMeasureIter { +public: + SkContourMeasureIter(); + /** + * Initialize the Iter with a path. + * The parts of the path that are needed are copied, so the client is free to modify/delete + * the path after this call. + * + * resScale controls the precision of the measure. values > 1 increase the + * precision (and possibly slow down the computation). + */ + SkContourMeasureIter(const SkPath& path, bool forceClosed, SkScalar resScale = 1); + ~SkContourMeasureIter(); + + SkContourMeasureIter(SkContourMeasureIter&&); + SkContourMeasureIter& operator=(SkContourMeasureIter&&); + + /** + * Reset the Iter with a path. + * The parts of the path that are needed are copied, so the client is free to modify/delete + * the path after this call. + */ + void reset(const SkPath& path, bool forceClosed, SkScalar resScale = 1); + + /** + * Iterates through contours in path, returning a contour-measure object for each contour + * in the path. Returns null when it is done. + * + * This only returns non-zero length contours, where a contour is the segments between + * a kMove_Verb and either ... + * - the next kMove_Verb + * - kClose_Verb (1 or more) + * - kDone_Verb + * If it encounters a zero-length contour, it is skipped. + */ + sk_sp next(); + +private: + class Impl; + + std::unique_ptr fImpl; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkCoverageMode.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkCoverageMode.h new file mode 100644 index 0000000000..aaae60c419 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkCoverageMode.h @@ -0,0 +1,28 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkCoverageMode_DEFINED +#define SkCoverageMode_DEFINED + +/** + * Describes geometric operations (ala SkRegion::Op) that can be applied to coverage bytes. + * These can be thought of as variants of porter-duff (SkBlendMode) modes, but only applied + * to the alpha channel. + * + * See SkMaskFilter for ways to use these when combining two different masks. + */ +enum class SkCoverageMode { + kUnion, // A ∪ B A+B-A*B + kIntersect, // A ∩ B A*B + kDifference, // A - B A*(1-B) + kReverseDifference, // B - A B*(1-A) + kXor, // A ⊕ B A+B-2*A*B + + kLast = kXor, +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkCubicMap.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkCubicMap.h new file mode 100644 index 0000000000..863c9333f6 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkCubicMap.h @@ -0,0 +1,47 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkCubicMap_DEFINED +#define SkCubicMap_DEFINED + +#include "include/core/SkPoint.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" + +/** + * Fast evaluation of a cubic ease-in / ease-out curve. This is defined as a parametric cubic + * curve inside the unit square. + * + * pt[0] is implicitly { 0, 0 } + * pt[3] is implicitly { 1, 1 } + * pts[1,2].X are inside the unit [0..1] + */ +class SK_API SkCubicMap { +public: + SkCubicMap(SkPoint p1, SkPoint p2); + + static bool IsLinear(SkPoint p1, SkPoint p2) { + return SkScalarNearlyEqual(p1.fX, p1.fY) && SkScalarNearlyEqual(p2.fX, p2.fY); + } + + float computeYFromX(float x) const; + + SkPoint computeFromT(float t) const; + +private: + enum Type { + kLine_Type, // x == y + kCubeRoot_Type, // At^3 == x + kSolver_Type, // general monotonic cubic solver + }; + + SkPoint fCoeff[3]; + Type fType; +}; + +#endif + diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkData.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkData.h new file mode 100644 index 0000000000..2b50cebc81 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkData.h @@ -0,0 +1,191 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkData_DEFINED +#define SkData_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/private/base/SkAPI.h" +#include "include/private/base/SkAssert.h" + +#include +#include + +class SkStream; + +/** + * SkData holds an immutable data buffer. Not only is the data immutable, + * but the actual ptr that is returned (by data() or bytes()) is guaranteed + * to always be the same for the life of this instance. + */ +class SK_API SkData final : public SkNVRefCnt { +public: + /** + * Returns the number of bytes stored. + */ + size_t size() const { return fSize; } + + bool isEmpty() const { return 0 == fSize; } + + /** + * Returns the ptr to the data. + */ + const void* data() const { return fPtr; } + + /** + * Like data(), returns a read-only ptr into the data, but in this case + * it is cast to uint8_t*, to make it easy to add an offset to it. + */ + const uint8_t* bytes() const { + return reinterpret_cast(fPtr); + } + + /** + * USE WITH CAUTION. + * This call will assert that the refcnt is 1, as a precaution against modifying the + * contents when another client/thread has access to the data. + */ + void* writable_data() { + if (fSize) { + // only assert we're unique if we're not empty + SkASSERT(this->unique()); + } + return const_cast(fPtr); + } + + /** + * Helper to copy a range of the data into a caller-provided buffer. + * Returns the actual number of bytes copied, after clamping offset and + * length to the size of the data. If buffer is NULL, it is ignored, and + * only the computed number of bytes is returned. + */ + size_t copyRange(size_t offset, size_t length, void* buffer) const; + + /** + * Returns true if these two objects have the same length and contents, + * effectively returning 0 == memcmp(...) + */ + bool equals(const SkData* other) const; + + /** + * Function that, if provided, will be called when the SkData goes out + * of scope, allowing for custom allocation/freeing of the data's contents. + */ + typedef void (*ReleaseProc)(const void* ptr, void* context); + + /** + * Create a new dataref by copying the specified data + */ + static sk_sp MakeWithCopy(const void* data, size_t length); + + + /** + * Create a new data with uninitialized contents. The caller should call writable_data() + * to write into the buffer, but this must be done before another ref() is made. + */ + static sk_sp MakeUninitialized(size_t length); + + /** + * Create a new data with zero-initialized contents. The caller should call writable_data() + * to write into the buffer, but this must be done before another ref() is made. + */ + static sk_sp MakeZeroInitialized(size_t length); + + /** + * Create a new dataref by copying the specified c-string + * (a null-terminated array of bytes). The returned SkData will have size() + * equal to strlen(cstr) + 1. If cstr is NULL, it will be treated the same + * as "". + */ + static sk_sp MakeWithCString(const char cstr[]); + + /** + * Create a new dataref, taking the ptr as is, and using the + * releaseproc to free it. The proc may be NULL. + */ + static sk_sp MakeWithProc(const void* ptr, size_t length, ReleaseProc proc, void* ctx); + + /** + * Call this when the data parameter is already const and will outlive the lifetime of the + * SkData. Suitable for with const globals. + */ + static sk_sp MakeWithoutCopy(const void* data, size_t length) { + return MakeWithProc(data, length, NoopReleaseProc, nullptr); + } + + /** + * Create a new dataref from a pointer allocated by malloc. The Data object + * takes ownership of that allocation, and will handling calling sk_free. + */ + static sk_sp MakeFromMalloc(const void* data, size_t length); + + /** + * Create a new dataref the file with the specified path. + * If the file cannot be opened, this returns NULL. + */ + static sk_sp MakeFromFileName(const char path[]); + + /** + * Create a new dataref from a stdio FILE. + * This does not take ownership of the FILE, nor close it. + * The caller is free to close the FILE at its convenience. + * The FILE must be open for reading only. + * Returns NULL on failure. + */ + static sk_sp MakeFromFILE(FILE* f); + + /** + * Create a new dataref from a file descriptor. + * This does not take ownership of the file descriptor, nor close it. + * The caller is free to close the file descriptor at its convenience. + * The file descriptor must be open for reading only. + * Returns NULL on failure. + */ + static sk_sp MakeFromFD(int fd); + + /** + * Attempt to read size bytes into a SkData. If the read succeeds, return the data, + * else return NULL. Either way the stream's cursor may have been changed as a result + * of calling read(). + */ + static sk_sp MakeFromStream(SkStream*, size_t size); + + /** + * Create a new dataref using a subset of the data in the specified + * src dataref. + */ + static sk_sp MakeSubset(const SkData* src, size_t offset, size_t length); + + /** + * Returns a new empty dataref (or a reference to a shared empty dataref). + * New or shared, the caller must see that unref() is eventually called. + */ + static sk_sp MakeEmpty(); + +private: + friend class SkNVRefCnt; + ReleaseProc fReleaseProc; + void* fReleaseProcContext; + const void* fPtr; + size_t fSize; + + SkData(const void* ptr, size_t size, ReleaseProc, void* context); + explicit SkData(size_t size); // inplace new/delete + ~SkData(); + + // Ensure the unsized delete is called. + void operator delete(void* p); + + // shared internal factory + static sk_sp PrivateNewWithCopy(const void* srcOrNull, size_t length); + + static void NoopReleaseProc(const void*, void*); // {} + + using INHERITED = SkRefCnt; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkDataTable.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkDataTable.h new file mode 100644 index 0000000000..3aa48d5f33 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkDataTable.h @@ -0,0 +1,122 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkDataTable_DEFINED +#define SkDataTable_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/private/base/SkAPI.h" +#include "include/private/base/SkAssert.h" + +#include +#include + +/** + * Like SkData, SkDataTable holds an immutable data buffer. The data buffer is + * organized into a table of entries, each with a length, so the entries are + * not required to all be the same size. + */ +class SK_API SkDataTable : public SkRefCnt { +public: + /** + * Returns true if the table is empty (i.e. has no entries). + */ + bool isEmpty() const { return 0 == fCount; } + + /** + * Return the number of entries in the table. 0 for an empty table + */ + int count() const { return fCount; } + + /** + * Return the size of the index'th entry in the table. The caller must + * ensure that index is valid for this table. + */ + size_t atSize(int index) const; + + /** + * Return a pointer to the data of the index'th entry in the table. + * The caller must ensure that index is valid for this table. + * + * @param size If non-null, this returns the byte size of this entry. This + * will be the same value that atSize(index) would return. + */ + const void* at(int index, size_t* size = nullptr) const; + + template + const T* atT(int index, size_t* size = nullptr) const { + return reinterpret_cast(this->at(index, size)); + } + + /** + * Returns the index'th entry as a c-string, and assumes that the trailing + * null byte had been copied into the table as well. + */ + const char* atStr(int index) const { + size_t size; + const char* str = this->atT(index, &size); + SkASSERT(strlen(str) + 1 == size); + return str; + } + + typedef void (*FreeProc)(void* context); + + static sk_sp MakeEmpty(); + + /** + * Return a new DataTable that contains a copy of the data stored in each + * "array". + * + * @param ptrs array of points to each element to be copied into the table. + * @param sizes array of byte-lengths for each entry in the corresponding + * ptrs[] array. + * @param count the number of array elements in ptrs[] and sizes[] to copy. + */ + static sk_sp MakeCopyArrays(const void * const * ptrs, + const size_t sizes[], int count); + + /** + * Return a new table that contains a copy of the data in array. + * + * @param array contiguous array of data for all elements to be copied. + * @param elemSize byte-length for a given element. + * @param count the number of entries to be copied out of array. The number + * of bytes that will be copied is count * elemSize. + */ + static sk_sp MakeCopyArray(const void* array, size_t elemSize, int count); + + static sk_sp MakeArrayProc(const void* array, size_t elemSize, int count, + FreeProc proc, void* context); + +private: + struct Dir { + const void* fPtr; + uintptr_t fSize; + }; + + int fCount; + size_t fElemSize; + union { + const Dir* fDir; + const char* fElems; + } fU; + + FreeProc fFreeProc; + void* fFreeProcContext; + + SkDataTable(); + SkDataTable(const void* array, size_t elemSize, int count, + FreeProc, void* context); + SkDataTable(const Dir*, int count, FreeProc, void* context); + ~SkDataTable() override; + + friend class SkDataTableBuilder; // access to Dir + + using INHERITED = SkRefCnt; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkDocument.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkDocument.h new file mode 100644 index 0000000000..c5fe5e850d --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkDocument.h @@ -0,0 +1,93 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkDocument_DEFINED +#define SkDocument_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkScalar.h" +#include "include/private/base/SkAPI.h" + +class SkCanvas; +class SkWStream; +struct SkRect; + +/** SK_ScalarDefaultDPI is 72 dots per inch. */ +static constexpr SkScalar SK_ScalarDefaultRasterDPI = 72.0f; + +/** + * High-level API for creating a document-based canvas. To use.. + * + * 1. Create a document, specifying a stream to store the output. + * 2. For each "page" of content: + * a. canvas = doc->beginPage(...) + * b. draw_my_content(canvas); + * c. doc->endPage(); + * 3. Close the document with doc->close(). + */ +class SK_API SkDocument : public SkRefCnt { +public: + + /** + * Begin a new page for the document, returning the canvas that will draw + * into the page. The document owns this canvas, and it will go out of + * scope when endPage() or close() is called, or the document is deleted. + * This will call endPage() if there is a currently active page. + */ + SkCanvas* beginPage(SkScalar width, SkScalar height, const SkRect* content = nullptr); + + /** + * Call endPage() when the content for the current page has been drawn + * (into the canvas returned by beginPage()). After this call the canvas + * returned by beginPage() will be out-of-scope. + */ + void endPage(); + + /** + * Call close() when all pages have been drawn. This will close the file + * or stream holding the document's contents. After close() the document + * can no longer add new pages. Deleting the document will automatically + * call close() if need be. + */ + void close(); + + /** + * Call abort() to stop producing the document immediately. + * The stream output must be ignored, and should not be trusted. + */ + void abort(); + +protected: + SkDocument(SkWStream*); + + // note: subclasses must call close() in their destructor, as the base class + // cannot do this for them. + ~SkDocument() override; + + virtual SkCanvas* onBeginPage(SkScalar width, SkScalar height) = 0; + virtual void onEndPage() = 0; + virtual void onClose(SkWStream*) = 0; + virtual void onAbort() = 0; + + // Allows subclasses to write to the stream as pages are written. + SkWStream* getStream() { return fStream; } + + enum State { + kBetweenPages_State, + kInPage_State, + kClosed_State + }; + State getState() const { return fState; } + +private: + SkWStream* fStream; + State fState; + + using INHERITED = SkRefCnt; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkDrawable.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkDrawable.h new file mode 100644 index 0000000000..764a825449 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkDrawable.h @@ -0,0 +1,178 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkDrawable_DEFINED +#define SkDrawable_DEFINED + +#include "include/core/SkFlattenable.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkScalar.h" +#include "include/private/base/SkAPI.h" + +#include +#include +#include + +class GrBackendDrawableInfo; +class SkCanvas; +class SkMatrix; +class SkPicture; +enum class GrBackendApi : unsigned int; +struct SkDeserialProcs; +struct SkIRect; +struct SkImageInfo; +struct SkRect; + +/** + * Base-class for objects that draw into SkCanvas. + * + * The object has a generation ID, which is guaranteed to be unique across all drawables. To + * allow for clients of the drawable that may want to cache the results, the drawable must + * change its generation ID whenever its internal state changes such that it will draw differently. + */ +class SK_API SkDrawable : public SkFlattenable { +public: + /** + * Draws into the specified content. The drawing sequence will be balanced upon return + * (i.e. the saveLevel() on the canvas will match what it was when draw() was called, + * and the current matrix and clip settings will not be changed. + */ + void draw(SkCanvas*, const SkMatrix* = nullptr); + void draw(SkCanvas*, SkScalar x, SkScalar y); + + /** + * When using the GPU backend it is possible for a drawable to execute using the underlying 3D + * API rather than the SkCanvas API. It does so by creating a GpuDrawHandler. The GPU backend + * is deferred so the handler will be given access to the 3D API at the correct point in the + * drawing stream as the GPU backend flushes. Since the drawable may mutate, each time it is + * drawn to a GPU-backed canvas a new handler is snapped, representing the drawable's state at + * the time of the snap. + * + * When the GPU backend flushes to the 3D API it will call the draw method on the + * GpuDrawHandler. At this time the drawable may add commands to the stream of GPU commands for + * the unerlying 3D API. The draw function takes a GrBackendDrawableInfo which contains + * information about the current state of 3D API which the caller must respect. See + * GrBackendDrawableInfo for more specific details on what information is sent and the + * requirements for different 3D APIs. + * + * Additionaly there may be a slight delay from when the drawable adds its commands to when + * those commands are actually submitted to the GPU. Thus the drawable or GpuDrawHandler is + * required to keep any resources that are used by its added commands alive and valid until + * those commands are submitted to the GPU. The GpuDrawHandler will be kept alive and then + * deleted once the commands are submitted to the GPU. The dtor of the GpuDrawHandler is the + * signal to the drawable that the commands have all been submitted. Different 3D APIs may have + * additional requirements for certain resources which require waiting for the GPU to finish + * all work on those resources before reusing or deleting them. In this case, the drawable can + * use the dtor call of the GpuDrawHandler to add a fence to the GPU to track when the GPU work + * has completed. + * + * Currently this is only supported for the GPU Vulkan backend. + */ + + class GpuDrawHandler { + public: + virtual ~GpuDrawHandler() {} + + virtual void draw(const GrBackendDrawableInfo&) {} + }; + + /** + * Snaps off a GpuDrawHandler to represent the state of the SkDrawable at the time the snap is + * called. This is used for executing GPU backend specific draws intermixed with normal Skia GPU + * draws. The GPU API, which will be used for the draw, as well as the full matrix, device clip + * bounds and imageInfo of the target buffer are passed in as inputs. + */ + std::unique_ptr snapGpuDrawHandler(GrBackendApi backendApi, + const SkMatrix& matrix, + const SkIRect& clipBounds, + const SkImageInfo& bufferInfo) { + return this->onSnapGpuDrawHandler(backendApi, matrix, clipBounds, bufferInfo); + } + + /** + * Returns an SkPicture with the contents of this SkDrawable. + */ + sk_sp makePictureSnapshot(); + + /** + * Return a unique value for this instance. If two calls to this return the same value, + * it is presumed that calling the draw() method will render the same thing as well. + * + * Subclasses that change their state should call notifyDrawingChanged() to ensure that + * a new value will be returned the next time it is called. + */ + uint32_t getGenerationID(); + + /** + * Return the (conservative) bounds of what the drawable will draw. If the drawable can + * change what it draws (e.g. animation or in response to some external change), then this + * must return a bounds that is always valid for all possible states. + */ + SkRect getBounds(); + + /** + * Return approximately how many bytes would be freed if this drawable is destroyed. + * The base implementation returns 0 to indicate that this is unknown. + */ + size_t approximateBytesUsed(); + + /** + * Calling this invalidates the previous generation ID, and causes a new one to be computed + * the next time getGenerationID() is called. Typically this is called by the object itself, + * in response to its internal state changing. + */ + void notifyDrawingChanged(); + + static SkFlattenable::Type GetFlattenableType() { + return kSkDrawable_Type; + } + + SkFlattenable::Type getFlattenableType() const override { + return kSkDrawable_Type; + } + + static sk_sp Deserialize(const void* data, size_t size, + const SkDeserialProcs* procs = nullptr) { + return sk_sp(static_cast( + SkFlattenable::Deserialize( + kSkDrawable_Type, data, size, procs).release())); + } + + Factory getFactory() const override { return nullptr; } + const char* getTypeName() const override { return nullptr; } + +protected: + SkDrawable(); + + virtual SkRect onGetBounds() = 0; + virtual size_t onApproximateBytesUsed(); + virtual void onDraw(SkCanvas*) = 0; + + virtual std::unique_ptr onSnapGpuDrawHandler(GrBackendApi, const SkMatrix&, + const SkIRect& /*clipBounds*/, + const SkImageInfo&) { + return nullptr; + } + + // TODO: Delete this once Android gets updated to take the clipBounds version above. + virtual std::unique_ptr onSnapGpuDrawHandler(GrBackendApi, const SkMatrix&) { + return nullptr; + } + + /** + * Default implementation calls onDraw() with a canvas that records into a picture. Subclasses + * may override if they have a more efficient way to return a picture for the current state + * of their drawable. Note: this picture must draw the same as what would be drawn from + * onDraw(). + */ + virtual sk_sp onMakePictureSnapshot(); + +private: + int32_t fGenerationID; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkExecutor.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkExecutor.h new file mode 100644 index 0000000000..88e2ca6e52 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkExecutor.h @@ -0,0 +1,41 @@ +/* + * 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 SkExecutor_DEFINED +#define SkExecutor_DEFINED + +#include +#include +#include "include/core/SkTypes.h" + +class SK_API SkExecutor { +public: + virtual ~SkExecutor(); + + // Create a thread pool SkExecutor with a fixed thread count, by default the number of cores. + static std::unique_ptr MakeFIFOThreadPool(int threads = 0, + bool allowBorrowing = true); + static std::unique_ptr MakeLIFOThreadPool(int threads = 0, + bool allowBorrowing = true); + + // There is always a default SkExecutor available by calling SkExecutor::GetDefault(). + static SkExecutor& GetDefault(); + static void SetDefault(SkExecutor*); // Does not take ownership. Not thread safe. + + // Add work to execute. + virtual void add(std::function) = 0; + + // If it makes sense for this executor, use this thread to execute work for a little while. + virtual void borrow() {} + +protected: + SkExecutor() = default; + SkExecutor(const SkExecutor&) = delete; + SkExecutor& operator=(const SkExecutor&) = delete; +}; + +#endif//SkExecutor_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFlattenable.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFlattenable.h new file mode 100644 index 0000000000..892a5933a2 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFlattenable.h @@ -0,0 +1,115 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkFlattenable_DEFINED +#define SkFlattenable_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkTypes.h" + +#include + +class SkData; +class SkReadBuffer; +class SkWriteBuffer; +struct SkDeserialProcs; +struct SkSerialProcs; + +/** \class SkFlattenable + + SkFlattenable is the base class for objects that need to be flattened + into a data stream for either transport or as part of the key to the + font cache. + */ +class SK_API SkFlattenable : public SkRefCnt { +public: + enum Type { + kSkColorFilter_Type, + kSkBlender_Type, + kSkDrawable_Type, + kSkDrawLooper_Type, // no longer supported by Skia + kSkImageFilter_Type, + kSkMaskFilter_Type, + kSkPathEffect_Type, + kSkShader_Type, + }; + + typedef sk_sp (*Factory)(SkReadBuffer&); + + SkFlattenable() {} + + /** Implement this to return a factory function pointer that can be called + to recreate your class given a buffer (previously written to by your + override of flatten(). + */ + virtual Factory getFactory() const = 0; + + /** + * Returns the name of the object's class. + */ + virtual const char* getTypeName() const = 0; + + static Factory NameToFactory(const char name[]); + static const char* FactoryToName(Factory); + + static void Register(const char name[], Factory); + + /** + * Override this if your subclass needs to record data that it will need to recreate itself + * from its CreateProc (returned by getFactory()). + * + * DEPRECATED public : will move to protected ... use serialize() instead + */ + virtual void flatten(SkWriteBuffer&) const {} + + virtual Type getFlattenableType() const = 0; + + // + // public ways to serialize / deserialize + // + sk_sp serialize(const SkSerialProcs* = nullptr) const; + size_t serialize(void* memory, size_t memory_size, + const SkSerialProcs* = nullptr) const; + static sk_sp Deserialize(Type, const void* data, size_t length, + const SkDeserialProcs* procs = nullptr); + +protected: + class PrivateInitializer { + public: + static void InitEffects(); + static void InitImageFilters(); + }; + +private: + static void RegisterFlattenablesIfNeeded(); + static void Finalize(); + + friend class SkGraphics; + + using INHERITED = SkRefCnt; +}; + +#if defined(SK_DISABLE_EFFECT_DESERIALIZATION) + #define SK_REGISTER_FLATTENABLE(type) do{}while(false) + + #define SK_FLATTENABLE_HOOKS(type) \ + static sk_sp CreateProc(SkReadBuffer&); \ + friend class SkFlattenable::PrivateInitializer; \ + Factory getFactory() const override { return nullptr; } \ + const char* getTypeName() const override { return #type; } +#else + #define SK_REGISTER_FLATTENABLE(type) \ + SkFlattenable::Register(#type, type::CreateProc) + + #define SK_FLATTENABLE_HOOKS(type) \ + static sk_sp CreateProc(SkReadBuffer&); \ + friend class SkFlattenable::PrivateInitializer; \ + Factory getFactory() const override { return type::CreateProc; } \ + const char* getTypeName() const override { return #type; } +#endif + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFont.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFont.h new file mode 100644 index 0000000000..e0c3533fd6 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFont.h @@ -0,0 +1,539 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkFont_DEFINED +#define SkFont_DEFINED + +#include "include/core/SkRect.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypeface.h" +#include "include/core/SkTypes.h" +#include "include/private/base/SkTo.h" +#include "include/private/base/SkTypeTraits.h" + +#include +#include +#include +#include + +class SkMatrix; +class SkPaint; +class SkPath; +enum class SkFontHinting; +enum class SkTextEncoding; +struct SkFontMetrics; +struct SkPoint; + +/** \class SkFont + SkFont controls options applied when drawing and measuring text. +*/ +class SK_API SkFont { +public: + /** Whether edge pixels draw opaque or with partial transparency. + */ + enum class Edging { + kAlias, //!< no transparent pixels on glyph edges + kAntiAlias, //!< may have transparent pixels on glyph edges + kSubpixelAntiAlias, //!< glyph positioned in pixel using transparency + }; + + /** Constructs SkFont with default values. + + @return default initialized SkFont + */ + SkFont(); + + /** Constructs SkFont with default values with SkTypeface and size in points. + + @param typeface font and style used to draw and measure text + @param size typographic height of text + @return initialized SkFont + */ + SkFont(sk_sp typeface, SkScalar size); + + /** Constructs SkFont with default values with SkTypeface. + + @param typeface font and style used to draw and measure text + @return initialized SkFont + */ + explicit SkFont(sk_sp typeface); + + + /** Constructs SkFont with default values with SkTypeface and size in points, + horizontal scale, and horizontal skew. Horizontal scale emulates condensed + and expanded fonts. Horizontal skew emulates oblique fonts. + + @param typeface font and style used to draw and measure text + @param size typographic height of text + @param scaleX text horizontal scale + @param skewX additional shear on x-axis relative to y-axis + @return initialized SkFont + */ + SkFont(sk_sp typeface, SkScalar size, SkScalar scaleX, SkScalar skewX); + + + /** Compares SkFont and font, and returns true if they are equivalent. + May return false if SkTypeface has identical contents but different pointers. + + @param font font to compare + @return true if SkFont pair are equivalent + */ + bool operator==(const SkFont& font) const; + + /** Compares SkFont and font, and returns true if they are not equivalent. + May return true if SkTypeface has identical contents but different pointers. + + @param font font to compare + @return true if SkFont pair are not equivalent + */ + bool operator!=(const SkFont& font) const { return !(*this == font); } + + /** If true, instructs the font manager to always hint glyphs. + Returned value is only meaningful if platform uses FreeType as the font manager. + + @return true if all glyphs are hinted + */ + bool isForceAutoHinting() const { return SkToBool(fFlags & kForceAutoHinting_PrivFlag); } + + /** Returns true if font engine may return glyphs from font bitmaps instead of from outlines. + + @return true if glyphs may be font bitmaps + */ + bool isEmbeddedBitmaps() const { return SkToBool(fFlags & kEmbeddedBitmaps_PrivFlag); } + + /** Returns true if glyphs may be drawn at sub-pixel offsets. + + @return true if glyphs may be drawn at sub-pixel offsets. + */ + bool isSubpixel() const { return SkToBool(fFlags & kSubpixel_PrivFlag); } + + /** Returns true if font and glyph metrics are requested to be linearly scalable. + + @return true if font and glyph metrics are requested to be linearly scalable. + */ + bool isLinearMetrics() const { return SkToBool(fFlags & kLinearMetrics_PrivFlag); } + + /** Returns true if bold is approximated by increasing the stroke width when creating glyph + bitmaps from outlines. + + @return bold is approximated through stroke width + */ + bool isEmbolden() const { return SkToBool(fFlags & kEmbolden_PrivFlag); } + + /** Returns true if baselines will be snapped to pixel positions when the current transformation + matrix is axis aligned. + + @return baselines may be snapped to pixels + */ + bool isBaselineSnap() const { return SkToBool(fFlags & kBaselineSnap_PrivFlag); } + + /** Sets whether to always hint glyphs. + If forceAutoHinting is set, instructs the font manager to always hint glyphs. + + Only affects platforms that use FreeType as the font manager. + + @param forceAutoHinting setting to always hint glyphs + */ + void setForceAutoHinting(bool forceAutoHinting); + + /** Requests, but does not require, to use bitmaps in fonts instead of outlines. + + @param embeddedBitmaps setting to use bitmaps in fonts + */ + void setEmbeddedBitmaps(bool embeddedBitmaps); + + /** Requests, but does not require, that glyphs respect sub-pixel positioning. + + @param subpixel setting for sub-pixel positioning + */ + void setSubpixel(bool subpixel); + + /** Requests, but does not require, linearly scalable font and glyph metrics. + + For outline fonts 'true' means font and glyph metrics should ignore hinting and rounding. + Note that some bitmap formats may not be able to scale linearly and will ignore this flag. + + @param linearMetrics setting for linearly scalable font and glyph metrics. + */ + void setLinearMetrics(bool linearMetrics); + + /** Increases stroke width when creating glyph bitmaps to approximate a bold typeface. + + @param embolden setting for bold approximation + */ + void setEmbolden(bool embolden); + + /** Requests that baselines be snapped to pixels when the current transformation matrix is axis + aligned. + + @param baselineSnap setting for baseline snapping to pixels + */ + void setBaselineSnap(bool baselineSnap); + + /** Whether edge pixels draw opaque or with partial transparency. + */ + Edging getEdging() const { return (Edging)fEdging; } + + /** Requests, but does not require, that edge pixels draw opaque or with + partial transparency. + */ + void setEdging(Edging edging); + + /** Sets level of glyph outline adjustment. + Does not check for valid values of hintingLevel. + */ + void setHinting(SkFontHinting hintingLevel); + + /** Returns level of glyph outline adjustment. + */ + SkFontHinting getHinting() const { return (SkFontHinting)fHinting; } + + /** Returns a font with the same attributes of this font, but with the specified size. + Returns nullptr if size is less than zero, infinite, or NaN. + + @param size typographic height of text + @return initialized SkFont + */ + SkFont makeWithSize(SkScalar size) const; + + /** Does not alter SkTypeface SkRefCnt. + + @return non-null SkTypeface + */ + SkTypeface* getTypeface() const { + SkASSERT(fTypeface); + return fTypeface.get(); + } + + /** Returns text size in points. + + @return typographic height of text + */ + SkScalar getSize() const { return fSize; } + + /** Returns text scale on x-axis. + Default value is 1. + + @return text horizontal scale + */ + SkScalar getScaleX() const { return fScaleX; } + + /** Returns text skew on x-axis. + Default value is zero. + + @return additional shear on x-axis relative to y-axis + */ + SkScalar getSkewX() const { return fSkewX; } + + /** Increases SkTypeface SkRefCnt by one. + + @return A non-null SkTypeface. + */ + sk_sp refTypeface() const { + SkASSERT(fTypeface); + return fTypeface; + } + + /** Sets SkTypeface to typeface, decreasing SkRefCnt of the previous SkTypeface. + Pass nullptr to clear SkTypeface and use an empty typeface (which draws nothing). + Increments tf SkRefCnt by one. + + @param tf font and style used to draw text + */ + void setTypeface(sk_sp tf); + + /** Sets text size in points. + Has no effect if textSize is not greater than or equal to zero. + + @param textSize typographic height of text + */ + void setSize(SkScalar textSize); + + /** Sets text scale on x-axis. + Default value is 1. + + @param scaleX text horizontal scale + */ + void setScaleX(SkScalar scaleX); + + /** Sets text skew on x-axis. + Default value is zero. + + @param skewX additional shear on x-axis relative to y-axis + */ + void setSkewX(SkScalar skewX); + + /** Converts text into glyph indices. + Returns the number of glyph indices represented by text. + SkTextEncoding specifies how text represents characters or glyphs. + glyphs may be nullptr, to compute the glyph count. + + Does not check text for valid character codes or valid glyph indices. + + If byteLength equals zero, returns zero. + If byteLength includes a partial character, the partial character is ignored. + + If encoding is SkTextEncoding::kUTF8 and text contains an invalid UTF-8 sequence, + zero is returned. + + When encoding is SkTextEncoding::kUTF8, SkTextEncoding::kUTF16, or + SkTextEncoding::kUTF32; then each Unicode codepoint is mapped to a + single glyph. This function uses the default character-to-glyph + mapping from the SkTypeface and maps characters not found in the + SkTypeface to zero. + + If maxGlyphCount is not sufficient to store all the glyphs, no glyphs are copied. + The total glyph count is returned for subsequent buffer reallocation. + + @param text character storage encoded with SkTextEncoding + @param byteLength length of character storage in bytes + @param glyphs storage for glyph indices; may be nullptr + @param maxGlyphCount storage capacity + @return number of glyphs represented by text of length byteLength + */ + int textToGlyphs(const void* text, size_t byteLength, SkTextEncoding encoding, + SkGlyphID glyphs[], int maxGlyphCount) const; + + /** Returns glyph index for Unicode character. + + If the character is not supported by the SkTypeface, returns 0. + + @param uni Unicode character + @return glyph index + */ + SkGlyphID unicharToGlyph(SkUnichar uni) const; + + void unicharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const; + + /** Returns number of glyphs represented by text. + + If encoding is SkTextEncoding::kUTF8, SkTextEncoding::kUTF16, or + SkTextEncoding::kUTF32; then each Unicode codepoint is mapped to a + single glyph. + + @param text character storage encoded with SkTextEncoding + @param byteLength length of character storage in bytes + @return number of glyphs represented by text of length byteLength + */ + int countText(const void* text, size_t byteLength, SkTextEncoding encoding) const { + return this->textToGlyphs(text, byteLength, encoding, nullptr, 0); + } + + /** Returns the advance width of text. + The advance is the normal distance to move before drawing additional text. + Returns the bounding box of text if bounds is not nullptr. + + @param text character storage encoded with SkTextEncoding + @param byteLength length of character storage in bytes + @param bounds returns bounding box relative to (0, 0) if not nullptr + @return the sum of the default advance widths + */ + SkScalar measureText(const void* text, size_t byteLength, SkTextEncoding encoding, + SkRect* bounds = nullptr) const { + return this->measureText(text, byteLength, encoding, bounds, nullptr); + } + + /** Returns the advance width of text. + The advance is the normal distance to move before drawing additional text. + Returns the bounding box of text if bounds is not nullptr. The paint + stroke settings, mask filter, or path effect may modify the bounds. + + @param text character storage encoded with SkTextEncoding + @param byteLength length of character storage in bytes + @param bounds returns bounding box relative to (0, 0) if not nullptr + @param paint optional; may be nullptr + @return the sum of the default advance widths + */ + SkScalar measureText(const void* text, size_t byteLength, SkTextEncoding encoding, + SkRect* bounds, const SkPaint* paint) const; + + /** DEPRECATED + Retrieves the advance and bounds for each glyph in glyphs. + Both widths and bounds may be nullptr. + If widths is not nullptr, widths must be an array of count entries. + if bounds is not nullptr, bounds must be an array of count entries. + + @param glyphs array of glyph indices to be measured + @param count number of glyphs + @param widths returns text advances for each glyph; may be nullptr + @param bounds returns bounds for each glyph relative to (0, 0); may be nullptr + */ + void getWidths(const SkGlyphID glyphs[], int count, SkScalar widths[], SkRect bounds[]) const { + this->getWidthsBounds(glyphs, count, widths, bounds, nullptr); + } + + // DEPRECATED + void getWidths(const SkGlyphID glyphs[], int count, SkScalar widths[], std::nullptr_t) const { + this->getWidths(glyphs, count, widths); + } + + /** Retrieves the advance and bounds for each glyph in glyphs. + Both widths and bounds may be nullptr. + If widths is not nullptr, widths must be an array of count entries. + if bounds is not nullptr, bounds must be an array of count entries. + + @param glyphs array of glyph indices to be measured + @param count number of glyphs + @param widths returns text advances for each glyph + */ + void getWidths(const SkGlyphID glyphs[], int count, SkScalar widths[]) const { + this->getWidthsBounds(glyphs, count, widths, nullptr, nullptr); + } + + /** Retrieves the advance and bounds for each glyph in glyphs. + Both widths and bounds may be nullptr. + If widths is not nullptr, widths must be an array of count entries. + if bounds is not nullptr, bounds must be an array of count entries. + + @param glyphs array of glyph indices to be measured + @param count number of glyphs + @param widths returns text advances for each glyph; may be nullptr + @param bounds returns bounds for each glyph relative to (0, 0); may be nullptr + @param paint optional, specifies stroking, SkPathEffect and SkMaskFilter + */ + void getWidthsBounds(const SkGlyphID glyphs[], int count, SkScalar widths[], SkRect bounds[], + const SkPaint* paint) const; + + + /** Retrieves the bounds for each glyph in glyphs. + bounds must be an array of count entries. + If paint is not nullptr, its stroking, SkPathEffect, and SkMaskFilter fields are respected. + + @param glyphs array of glyph indices to be measured + @param count number of glyphs + @param bounds returns bounds for each glyph relative to (0, 0); may be nullptr + @param paint optional, specifies stroking, SkPathEffect, and SkMaskFilter + */ + void getBounds(const SkGlyphID glyphs[], int count, SkRect bounds[], + const SkPaint* paint) const { + this->getWidthsBounds(glyphs, count, nullptr, bounds, paint); + } + + /** Retrieves the positions for each glyph, beginning at the specified origin. The caller + must allocated at least count number of elements in the pos[] array. + + @param glyphs array of glyph indices to be positioned + @param count number of glyphs + @param pos returns glyphs positions + @param origin location of the first glyph. Defaults to {0, 0}. + */ + void getPos(const SkGlyphID glyphs[], int count, SkPoint pos[], SkPoint origin = {0, 0}) const; + + /** Retrieves the x-positions for each glyph, beginning at the specified origin. The caller + must allocated at least count number of elements in the xpos[] array. + + @param glyphs array of glyph indices to be positioned + @param count number of glyphs + @param xpos returns glyphs x-positions + @param origin x-position of the first glyph. Defaults to 0. + */ + void getXPos(const SkGlyphID glyphs[], int count, SkScalar xpos[], SkScalar origin = 0) const; + + /** Returns intervals [start, end] describing lines parallel to the advance that intersect + * with the glyphs. + * + * @param glyphs the glyphs to intersect + * @param count the number of glyphs and positions + * @param pos the position of each glyph + * @param top the top of the line intersecting + * @param bottom the bottom of the line intersecting + @return array of pairs of x values [start, end]. May be empty. + */ + std::vector getIntercepts(const SkGlyphID glyphs[], int count, const SkPoint pos[], + SkScalar top, SkScalar bottom, + const SkPaint* = nullptr) const; + + /** Modifies path to be the outline of the glyph. + If the glyph has an outline, modifies path to be the glyph's outline and returns true. + The glyph outline may be empty. Degenerate contours in the glyph outline will be skipped. + If glyph is described by a bitmap, returns false and ignores path parameter. + + @param glyphID index of glyph + @param path pointer to existing SkPath + @return true if glyphID is described by path + */ + bool getPath(SkGlyphID glyphID, SkPath* path) const; + + /** Returns path corresponding to glyph array. + + @param glyphIDs array of glyph indices + @param count number of glyphs + @param glyphPathProc function returning one glyph description as path + @param ctx function context + */ + void getPaths(const SkGlyphID glyphIDs[], int count, + void (*glyphPathProc)(const SkPath* pathOrNull, const SkMatrix& mx, void* ctx), + void* ctx) const; + + /** Returns SkFontMetrics associated with SkTypeface. + The return value is the recommended spacing between lines: the sum of metrics + descent, ascent, and leading. + If metrics is not nullptr, SkFontMetrics is copied to metrics. + Results are scaled by text size but does not take into account + dimensions required by text scale, text skew, fake bold, + style stroke, and SkPathEffect. + + @param metrics storage for SkFontMetrics; may be nullptr + @return recommended spacing between lines + */ + SkScalar getMetrics(SkFontMetrics* metrics) const; + + /** Returns the recommended spacing between lines: the sum of metrics + descent, ascent, and leading. + Result is scaled by text size but does not take into account + dimensions required by stroking and SkPathEffect. + Returns the same result as getMetrics(). + + @return recommended spacing between lines + */ + SkScalar getSpacing() const { return this->getMetrics(nullptr); } + + /** Dumps fields of the font to SkDebugf. May change its output over time, so clients should + * not rely on this for anything specific. Used to aid in debugging. + */ + void dump() const; + + using sk_is_trivially_relocatable = std::true_type; + +private: + enum PrivFlags { + kForceAutoHinting_PrivFlag = 1 << 0, + kEmbeddedBitmaps_PrivFlag = 1 << 1, + kSubpixel_PrivFlag = 1 << 2, + kLinearMetrics_PrivFlag = 1 << 3, + kEmbolden_PrivFlag = 1 << 4, + kBaselineSnap_PrivFlag = 1 << 5, + }; + + static constexpr unsigned kAllFlags = kForceAutoHinting_PrivFlag + | kEmbeddedBitmaps_PrivFlag + | kSubpixel_PrivFlag + | kLinearMetrics_PrivFlag + | kEmbolden_PrivFlag + | kBaselineSnap_PrivFlag; + + sk_sp fTypeface; + SkScalar fSize; + SkScalar fScaleX; + SkScalar fSkewX; + uint8_t fFlags; + uint8_t fEdging; + uint8_t fHinting; + + static_assert(::sk_is_trivially_relocatable::value); + + SkScalar setupForAsPaths(SkPaint*); + bool hasSomeAntiAliasing() const; + + friend class SkFontPriv; + friend class SkGlyphRunListPainterCPU; + friend class SkStrikeSpec; + friend class SkRemoteGlyphCacheTest; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFontArguments.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFontArguments.h new file mode 100644 index 0000000000..5ab8d2e182 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFontArguments.h @@ -0,0 +1,94 @@ +/* + * 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 SkFontArguments_DEFINED +#define SkFontArguments_DEFINED + +#include "include/core/SkColor.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" + +/** Represents a set of actual arguments for a font. */ +struct SkFontArguments { + struct VariationPosition { + struct Coordinate { + SkFourByteTag axis; + float value; + }; + const Coordinate* coordinates; + int coordinateCount; + }; + + /** Specify a palette to use and overrides for palette entries. + * + * `overrides` is a list of pairs of palette entry index and color. + * The overriden palette entries will use the associated color. + * Override pairs with palette entry indices out of range will not be applied. + * Later override entries override earlier ones. + */ + struct Palette { + struct Override { + uint16_t index; + SkColor color; + }; + int index; + const Override* overrides; + int overrideCount; + }; + + SkFontArguments() + : fCollectionIndex(0) + , fVariationDesignPosition{nullptr, 0} + , fPalette{0, nullptr, 0} {} + + /** Specify the index of the desired font. + * + * Font formats like ttc, dfont, cff, cid, pfr, t42, t1, and fon may actually be indexed + * collections of fonts. + */ + SkFontArguments& setCollectionIndex(int collectionIndex) { + fCollectionIndex = collectionIndex; + return *this; + } + + /** Specify a position in the variation design space. + * + * Any axis not specified will use the default value. + * Any specified axis not actually present in the font will be ignored. + * + * @param position not copied. The value must remain valid for life of SkFontArguments. + */ + SkFontArguments& setVariationDesignPosition(VariationPosition position) { + fVariationDesignPosition.coordinates = position.coordinates; + fVariationDesignPosition.coordinateCount = position.coordinateCount; + return *this; + } + + int getCollectionIndex() const { + return fCollectionIndex; + } + + VariationPosition getVariationDesignPosition() const { + return fVariationDesignPosition; + } + + SkFontArguments& setPalette(Palette palette) { + fPalette.index = palette.index; + fPalette.overrides = palette.overrides; + fPalette.overrideCount = palette.overrideCount; + return *this; + } + + Palette getPalette() const { return fPalette; } + +private: + int fCollectionIndex; + VariationPosition fVariationDesignPosition; + Palette fPalette; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFontMetrics.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFontMetrics.h new file mode 100644 index 0000000000..0686246ad1 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFontMetrics.h @@ -0,0 +1,139 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkFontMetrics_DEFINED +#define SkFontMetrics_DEFINED + +#include "include/core/SkScalar.h" +#include "include/private/base/SkTo.h" + +/** \class SkFontMetrics + The metrics of an SkFont. + The metric values are consistent with the Skia y-down coordinate system. + */ +struct SK_API SkFontMetrics { + bool operator==(const SkFontMetrics& that) const { + return + this->fFlags == that.fFlags && + this->fTop == that.fTop && + this->fAscent == that.fAscent && + this->fDescent == that.fDescent && + this->fBottom == that.fBottom && + this->fLeading == that.fLeading && + this->fAvgCharWidth == that.fAvgCharWidth && + this->fMaxCharWidth == that.fMaxCharWidth && + this->fXMin == that.fXMin && + this->fXMax == that.fXMax && + this->fXHeight == that.fXHeight && + this->fCapHeight == that.fCapHeight && + this->fUnderlineThickness == that.fUnderlineThickness && + this->fUnderlinePosition == that.fUnderlinePosition && + this->fStrikeoutThickness == that.fStrikeoutThickness && + this->fStrikeoutPosition == that.fStrikeoutPosition; + } + + /** \enum FontMetricsFlags + FontMetricsFlags indicate when certain metrics are valid; + the underline or strikeout metrics may be valid and zero. + Fonts with embedded bitmaps may not have valid underline or strikeout metrics. + */ + enum FontMetricsFlags { + kUnderlineThicknessIsValid_Flag = 1 << 0, //!< set if fUnderlineThickness is valid + kUnderlinePositionIsValid_Flag = 1 << 1, //!< set if fUnderlinePosition is valid + kStrikeoutThicknessIsValid_Flag = 1 << 2, //!< set if fStrikeoutThickness is valid + kStrikeoutPositionIsValid_Flag = 1 << 3, //!< set if fStrikeoutPosition is valid + kBoundsInvalid_Flag = 1 << 4, //!< set if fTop, fBottom, fXMin, fXMax invalid + }; + + uint32_t fFlags; //!< FontMetricsFlags indicating which metrics are valid + SkScalar fTop; //!< greatest extent above origin of any glyph bounding box, typically negative; deprecated with variable fonts + SkScalar fAscent; //!< distance to reserve above baseline, typically negative + SkScalar fDescent; //!< distance to reserve below baseline, typically positive + SkScalar fBottom; //!< greatest extent below origin of any glyph bounding box, typically positive; deprecated with variable fonts + SkScalar fLeading; //!< distance to add between lines, typically positive or zero + SkScalar fAvgCharWidth; //!< average character width, zero if unknown + SkScalar fMaxCharWidth; //!< maximum character width, zero if unknown + SkScalar fXMin; //!< greatest extent to left of origin of any glyph bounding box, typically negative; deprecated with variable fonts + SkScalar fXMax; //!< greatest extent to right of origin of any glyph bounding box, typically positive; deprecated with variable fonts + SkScalar fXHeight; //!< height of lower-case 'x', zero if unknown, typically negative + SkScalar fCapHeight; //!< height of an upper-case letter, zero if unknown, typically negative + SkScalar fUnderlineThickness; //!< underline thickness + SkScalar fUnderlinePosition; //!< distance from baseline to top of stroke, typically positive + SkScalar fStrikeoutThickness; //!< strikeout thickness + SkScalar fStrikeoutPosition; //!< distance from baseline to bottom of stroke, typically negative + + /** Returns true if SkFontMetrics has a valid underline thickness, and sets + thickness to that value. If the underline thickness is not valid, + return false, and ignore thickness. + + @param thickness storage for underline width + @return true if font specifies underline width + */ + bool hasUnderlineThickness(SkScalar* thickness) const { + if (SkToBool(fFlags & kUnderlineThicknessIsValid_Flag)) { + *thickness = fUnderlineThickness; + return true; + } + return false; + } + + /** Returns true if SkFontMetrics has a valid underline position, and sets + position to that value. If the underline position is not valid, + return false, and ignore position. + + @param position storage for underline position + @return true if font specifies underline position + */ + bool hasUnderlinePosition(SkScalar* position) const { + if (SkToBool(fFlags & kUnderlinePositionIsValid_Flag)) { + *position = fUnderlinePosition; + return true; + } + return false; + } + + /** Returns true if SkFontMetrics has a valid strikeout thickness, and sets + thickness to that value. If the underline thickness is not valid, + return false, and ignore thickness. + + @param thickness storage for strikeout width + @return true if font specifies strikeout width + */ + bool hasStrikeoutThickness(SkScalar* thickness) const { + if (SkToBool(fFlags & kStrikeoutThicknessIsValid_Flag)) { + *thickness = fStrikeoutThickness; + return true; + } + return false; + } + + /** Returns true if SkFontMetrics has a valid strikeout position, and sets + position to that value. If the underline position is not valid, + return false, and ignore position. + + @param position storage for strikeout position + @return true if font specifies strikeout position + */ + bool hasStrikeoutPosition(SkScalar* position) const { + if (SkToBool(fFlags & kStrikeoutPositionIsValid_Flag)) { + *position = fStrikeoutPosition; + return true; + } + return false; + } + + /** Returns true if SkFontMetrics has a valid fTop, fBottom, fXMin, and fXMax. + If the bounds are not valid, return false. + + @return true if font specifies maximum glyph bounds + */ + bool hasBounds() const { + return !SkToBool(fFlags & kBoundsInvalid_Flag); + } +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFontMgr.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFontMgr.h new file mode 100644 index 0000000000..48f49f6845 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFontMgr.h @@ -0,0 +1,143 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkFontMgr_DEFINED +#define SkFontMgr_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkTypes.h" + +#include + +class SkData; +class SkFontStyle; +class SkStreamAsset; +class SkString; +class SkTypeface; +struct SkFontArguments; + +class SK_API SkFontStyleSet : public SkRefCnt { +public: + virtual int count() = 0; + virtual void getStyle(int index, SkFontStyle*, SkString* style) = 0; + virtual sk_sp createTypeface(int index) = 0; + virtual sk_sp matchStyle(const SkFontStyle& pattern) = 0; + + static sk_sp CreateEmpty(); + +protected: + sk_sp matchStyleCSS3(const SkFontStyle& pattern); +}; + +class SK_API SkFontMgr : public SkRefCnt { +public: + int countFamilies() const; + void getFamilyName(int index, SkString* familyName) const; + sk_sp createStyleSet(int index) const; + + /** + * The caller must call unref() on the returned object. + * Never returns NULL; will return an empty set if the name is not found. + * + * Passing nullptr as the parameter will return the default system family. + * Note that most systems don't have a default system family, so passing nullptr will often + * result in the empty set. + * + * It is possible that this will return a style set not accessible from + * createStyleSet(int) due to hidden or auto-activated fonts. + */ + sk_sp matchFamily(const char familyName[]) const; + + /** + * Find the closest matching typeface to the specified familyName and style + * and return a ref to it. The caller must call unref() on the returned + * object. Will return nullptr if no 'good' match is found. + * + * Passing |nullptr| as the parameter for |familyName| will return the + * default system font. + * + * It is possible that this will return a style set not accessible from + * createStyleSet(int) or matchFamily(const char[]) due to hidden or + * auto-activated fonts. + */ + sk_sp matchFamilyStyle(const char familyName[], const SkFontStyle&) const; + + /** + * Use the system fallback to find a typeface for the given character. + * Note that bcp47 is a combination of ISO 639, 15924, and 3166-1 codes, + * so it is fine to just pass a ISO 639 here. + * + * Will return NULL if no family can be found for the character + * in the system fallback. + * + * Passing |nullptr| as the parameter for |familyName| will return the + * default system font. + * + * bcp47[0] is the least significant fallback, bcp47[bcp47Count-1] is the + * most significant. If no specified bcp47 codes match, any font with the + * requested character will be matched. + */ + sk_sp matchFamilyStyleCharacter(const char familyName[], const SkFontStyle&, + const char* bcp47[], int bcp47Count, + SkUnichar character) const; + + /** + * Create a typeface for the specified data and TTC index (pass 0 for none) + * or NULL if the data is not recognized. The caller must call unref() on + * the returned object if it is not null. + */ + sk_sp makeFromData(sk_sp, int ttcIndex = 0) const; + + /** + * Create a typeface for the specified stream and TTC index + * (pass 0 for none) or NULL if the stream is not recognized. The caller + * must call unref() on the returned object if it is not null. + */ + sk_sp makeFromStream(std::unique_ptr, int ttcIndex = 0) const; + + /* Experimental, API subject to change. */ + sk_sp makeFromStream(std::unique_ptr, const SkFontArguments&) const; + + /** + * Create a typeface for the specified fileName and TTC index + * (pass 0 for none) or NULL if the file is not found, or its contents are + * not recognized. The caller must call unref() on the returned object + * if it is not null. + */ + sk_sp makeFromFile(const char path[], int ttcIndex = 0) const; + + sk_sp legacyMakeTypeface(const char familyName[], SkFontStyle style) const; + + /* Returns an empty font manager without any typeface dependencies */ + static sk_sp RefEmpty(); + +protected: + virtual int onCountFamilies() const = 0; + virtual void onGetFamilyName(int index, SkString* familyName) const = 0; + virtual sk_sp onCreateStyleSet(int index)const = 0; + + /** May return NULL if the name is not found. */ + virtual sk_sp onMatchFamily(const char familyName[]) const = 0; + + virtual sk_sp onMatchFamilyStyle(const char familyName[], + const SkFontStyle&) const = 0; + virtual sk_sp onMatchFamilyStyleCharacter(const char familyName[], + const SkFontStyle&, + const char* bcp47[], int bcp47Count, + SkUnichar character) const = 0; + + virtual sk_sp onMakeFromData(sk_sp, int ttcIndex) const = 0; + virtual sk_sp onMakeFromStreamIndex(std::unique_ptr, + int ttcIndex) const = 0; + virtual sk_sp onMakeFromStreamArgs(std::unique_ptr, + const SkFontArguments&) const = 0; + virtual sk_sp onMakeFromFile(const char path[], int ttcIndex) const = 0; + + virtual sk_sp onLegacyMakeTypeface(const char familyName[], SkFontStyle) const = 0; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFontParameters.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFontParameters.h new file mode 100644 index 0000000000..ae4f1d68b6 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFontParameters.h @@ -0,0 +1,42 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkFontParameters_DEFINED +#define SkFontParameters_DEFINED + +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" + +struct SkFontParameters { + struct Variation { + // Parameters in a variation font axis. + struct Axis { + constexpr Axis() : tag(0), min(0), def(0), max(0), flags(0) {} + constexpr Axis(SkFourByteTag tag, float min, float def, float max, bool hidden) : + tag(tag), min(min), def(def), max(max), flags(hidden ? HIDDEN : 0) {} + + // Four character identifier of the font axis (weight, width, slant, italic...). + SkFourByteTag tag; + // Minimum value supported by this axis. + float min; + // Default value set by this axis. + float def; + // Maximum value supported by this axis. The maximum can equal the minimum. + float max; + // Return whether this axis is recommended to be remain hidden in user interfaces. + bool isHidden() const { return flags & HIDDEN; } + // Set this axis to be remain hidden in user interfaces. + void setHidden(bool hidden) { flags = hidden ? (flags | HIDDEN) : (flags & ~HIDDEN); } + private: + static constexpr uint16_t HIDDEN = 0x0001; + // Attributes for a font axis. + uint16_t flags; + }; + }; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFontStyle.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFontStyle.h new file mode 100644 index 0000000000..be46b53bb2 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFontStyle.h @@ -0,0 +1,84 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkFontStyle_DEFINED +#define SkFontStyle_DEFINED + +#include "include/core/SkTypes.h" +#include "include/private/base/SkTPin.h" + +#include + +class SK_API SkFontStyle { +public: + enum Weight { + kInvisible_Weight = 0, + kThin_Weight = 100, + kExtraLight_Weight = 200, + kLight_Weight = 300, + kNormal_Weight = 400, + kMedium_Weight = 500, + kSemiBold_Weight = 600, + kBold_Weight = 700, + kExtraBold_Weight = 800, + kBlack_Weight = 900, + kExtraBlack_Weight = 1000, + }; + + enum Width { + kUltraCondensed_Width = 1, + kExtraCondensed_Width = 2, + kCondensed_Width = 3, + kSemiCondensed_Width = 4, + kNormal_Width = 5, + kSemiExpanded_Width = 6, + kExpanded_Width = 7, + kExtraExpanded_Width = 8, + kUltraExpanded_Width = 9, + }; + + enum Slant { + kUpright_Slant, + kItalic_Slant, + kOblique_Slant, + }; + + constexpr SkFontStyle(int weight, int width, Slant slant) : fValue( + (SkTPin(weight, kInvisible_Weight, kExtraBlack_Weight)) + + (SkTPin(width, kUltraCondensed_Width, kUltraExpanded_Width) << 16) + + (SkTPin(slant, kUpright_Slant, kOblique_Slant) << 24) + ) { } + + constexpr SkFontStyle() : SkFontStyle{kNormal_Weight, kNormal_Width, kUpright_Slant} { } + + bool operator==(const SkFontStyle& rhs) const { + return fValue == rhs.fValue; + } + + int weight() const { return fValue & 0xFFFF; } + int width() const { return (fValue >> 16) & 0xFF; } + Slant slant() const { return (Slant)((fValue >> 24) & 0xFF); } + + static constexpr SkFontStyle Normal() { + return SkFontStyle(kNormal_Weight, kNormal_Width, kUpright_Slant); + } + static constexpr SkFontStyle Bold() { + return SkFontStyle(kBold_Weight, kNormal_Width, kUpright_Slant); + } + static constexpr SkFontStyle Italic() { + return SkFontStyle(kNormal_Weight, kNormal_Width, kItalic_Slant ); + } + static constexpr SkFontStyle BoldItalic() { + return SkFontStyle(kBold_Weight, kNormal_Width, kItalic_Slant ); + } + +private: + friend class SkTypefaceProxyPrototype; // To serialize fValue + int32_t fValue; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFontTypes.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFontTypes.h new file mode 100644 index 0000000000..76f5dde67f --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkFontTypes.h @@ -0,0 +1,25 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkFontTypes_DEFINED +#define SkFontTypes_DEFINED + +enum class SkTextEncoding { + kUTF8, //!< uses bytes to represent UTF-8 or ASCII + kUTF16, //!< uses two byte words to represent most of Unicode + kUTF32, //!< uses four byte words to represent all of Unicode + kGlyphID, //!< uses two byte words to represent glyph indices +}; + +enum class SkFontHinting { + kNone, //!< glyph outlines unchanged + kSlight, //!< minimal modification to improve constrast + kNormal, //!< glyph outlines modified to improve constrast + kFull, //!< modifies glyph outlines for maximum constrast +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkGraphics.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkGraphics.h new file mode 100644 index 0000000000..58fd16b734 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkGraphics.h @@ -0,0 +1,169 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkGraphics_DEFINED +#define SkGraphics_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/private/base/SkAPI.h" + +#include +#include +#include + +class SkData; +class SkImageGenerator; +class SkOpenTypeSVGDecoder; +class SkTraceMemoryDump; + +class SK_API SkGraphics { +public: + /** + * Call this at process initialization time if your environment does not + * permit static global initializers that execute code. + * Init() is thread-safe and idempotent. + */ + static void Init(); + + /** + * Return the max number of bytes that should be used by the font cache. + * If the cache needs to allocate more, it will purge previous entries. + * This max can be changed by calling SetFontCacheLimit(). + */ + static size_t GetFontCacheLimit(); + + /** + * Specify the max number of bytes that should be used by the font cache. + * If the cache needs to allocate more, it will purge previous entries. + * + * This function returns the previous setting, as if GetFontCacheLimit() + * had be called before the new limit was set. + */ + static size_t SetFontCacheLimit(size_t bytes); + + /** + * Return the number of bytes currently used by the font cache. + */ + static size_t GetFontCacheUsed(); + + /** + * Return the number of entries in the font cache. + * A cache "entry" is associated with each typeface + pointSize + matrix. + */ + static int GetFontCacheCountUsed(); + + /** + * Return the current limit to the number of entries in the font cache. + * A cache "entry" is associated with each typeface + pointSize + matrix. + */ + static int GetFontCacheCountLimit(); + + /** + * Set the limit to the number of entries in the font cache, and return + * the previous value. If this new value is lower than the previous, + * it will automatically try to purge entries to meet the new limit. + */ + static int SetFontCacheCountLimit(int count); + + /** + * Return the current limit to the number of entries in the typeface cache. + * A cache "entry" is associated with each typeface. + */ + static int GetTypefaceCacheCountLimit(); + + /** + * Set the limit to the number of entries in the typeface cache, and return + * the previous value. Changes to this only take effect the next time + * each cache object is modified. + */ + static int SetTypefaceCacheCountLimit(int count); + + /** + * For debugging purposes, this will attempt to purge the font cache. It + * does not change the limit, but will cause subsequent font measures and + * draws to be recreated, since they will no longer be in the cache. + */ + static void PurgeFontCache(); + + /** + * If the strike cache is above the cache limit, attempt to purge strikes + * with pinners. This should be called after clients release locks on + * pinned strikes. + */ + static void PurgePinnedFontCache(); + + /** + * This function returns the memory used for temporary images and other resources. + */ + static size_t GetResourceCacheTotalBytesUsed(); + + /** + * These functions get/set the memory usage limit for the resource cache, used for temporary + * bitmaps and other resources. Entries are purged from the cache when the memory useage + * exceeds this limit. + */ + static size_t GetResourceCacheTotalByteLimit(); + static size_t SetResourceCacheTotalByteLimit(size_t newLimit); + + /** + * For debugging purposes, this will attempt to purge the resource cache. It + * does not change the limit. + */ + static void PurgeResourceCache(); + + /** + * When the cachable entry is very lage (e.g. a large scaled bitmap), adding it to the cache + * can cause most/all of the existing entries to be purged. To avoid the, the client can set + * a limit for a single allocation. If a cacheable entry would have been cached, but its size + * exceeds this limit, then we do not attempt to cache it at all. + * + * Zero is the default value, meaning we always attempt to cache entries. + */ + static size_t GetResourceCacheSingleAllocationByteLimit(); + static size_t SetResourceCacheSingleAllocationByteLimit(size_t newLimit); + + /** + * Dumps memory usage of caches using the SkTraceMemoryDump interface. See SkTraceMemoryDump + * for usage of this method. + */ + static void DumpMemoryStatistics(SkTraceMemoryDump* dump); + + /** + * Free as much globally cached memory as possible. This will purge all private caches in Skia, + * including font and image caches. + * + * If there are caches associated with GPU context, those will not be affected by this call. + */ + static void PurgeAllCaches(); + + typedef std::unique_ptr + (*ImageGeneratorFromEncodedDataFactory)(sk_sp); + + /** + * To instantiate images from encoded data, first looks at this runtime function-ptr. If it + * exists, it is called to create an SkImageGenerator from SkData. If there is no function-ptr + * or there is, but it returns NULL, then skia will call its internal default implementation. + * + * Returns the previous factory (which could be NULL). + */ + static ImageGeneratorFromEncodedDataFactory + SetImageGeneratorFromEncodedDataFactory(ImageGeneratorFromEncodedDataFactory); + + /** + * To draw OpenType SVG data, Skia will look at this runtime function pointer. If this function + * pointer is set, the SkTypeface implementations which support OpenType SVG will call this + * function to create an SkOpenTypeSVGDecoder to decode the OpenType SVG and draw it as needed. + * If this function is not set, the SkTypeface implementations will generally not support + * OpenType SVG and attempt to use other glyph representations if available. + */ + using OpenTypeSVGDecoderFactory = + std::unique_ptr (*)(const uint8_t* svg, size_t length); + static OpenTypeSVGDecoderFactory SetOpenTypeSVGDecoderFactory(OpenTypeSVGDecoderFactory); + static OpenTypeSVGDecoderFactory GetOpenTypeSVGDecoderFactory(); +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkImage.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkImage.h new file mode 100644 index 0000000000..600f167971 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkImage.h @@ -0,0 +1,948 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkImage_DEFINED +#define SkImage_DEFINED + +#include "include/core/SkAlphaType.h" +#include "include/core/SkImageInfo.h" +#include "include/core/SkRect.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkSize.h" +#include "include/private/base/SkAPI.h" + +#include +#include +#include +#include + +class GrDirectContext; +class GrRecordingContext; +class SkBitmap; +class SkColorSpace; +class SkData; +class SkImage; +class SkImageFilter; +class SkImageGenerator; +class SkMatrix; +class SkMipmap; +class SkPaint; +class SkPicture; +class SkPixmap; +class SkShader; +class SkSurfaceProps; +enum SkColorType : int; +enum class SkTextureCompressionType; +enum class SkTileMode; + +struct SkIPoint; +struct SkSamplingOptions; + +namespace skgpu::graphite { class Recorder; } + +namespace SkImages { + +/** Caller data passed to RasterReleaseProc; may be nullptr. */ +using ReleaseContext = void*; +/** Function called when SkImage no longer shares pixels. ReleaseContext is + provided by caller when SkImage is created, and may be nullptr. +*/ +using RasterReleaseProc = void(const void* pixels, ReleaseContext); + +/** Creates a CPU-backed SkImage from bitmap, sharing or copying bitmap pixels. If the bitmap + is marked immutable, and its pixel memory is shareable, it may be shared + instead of copied. + + SkImage is returned if bitmap is valid. Valid SkBitmap parameters include: + dimensions are greater than zero; + each dimension fits in 29 bits; + SkColorType and SkAlphaType are valid, and SkColorType is not kUnknown_SkColorType; + row bytes are large enough to hold one row of pixels; + pixel address is not nullptr. + + @param bitmap SkImageInfo, row bytes, and pixels + @return created SkImage, or nullptr +*/ +SK_API sk_sp RasterFromBitmap(const SkBitmap& bitmap); + +/** Creates a CPU-backed SkImage from compressed data. + + This method will decompress the compressed data and create an image wrapping + it. Any mipmap levels present in the compressed data are discarded. + + @param data compressed data to store in SkImage + @param width width of full SkImage + @param height height of full SkImage + @param type type of compression used + @return created SkImage, or nullptr +*/ +SK_API sk_sp RasterFromCompressedTextureData(sk_sp data, + int width, + int height, + SkTextureCompressionType type); + +/** + * Return a SkImage using the encoded data, but attempts to defer decoding until the + * image is actually used/drawn. This deferral allows the system to cache the result, either on the + * CPU or on the GPU, depending on where the image is drawn. If memory is low, the cache may + * be purged, causing the next draw of the image to have to re-decode. + * + * If alphaType is nullopt, the image's alpha type will be chosen automatically based on the + * image format. Transparent images will default to kPremul_SkAlphaType. If alphaType contains + * kPremul_SkAlphaType or kUnpremul_SkAlphaType, that alpha type will be used. Forcing opaque + * (passing kOpaque_SkAlphaType) is not allowed, and will return nullptr. + * + * If the encoded format is not supported, nullptr is returned. + * + * If possible, clients should use SkCodecs::DeferredImage instead. + * + * @param encoded the encoded data + * @return created SkImage, or nullptr + + example: https://fiddle.skia.org/c/@Image_DeferredFromEncodedData +*/ +SK_API sk_sp DeferredFromEncodedData(sk_sp encoded, + std::optional alphaType = std::nullopt); + +/** Creates SkImage from data returned by imageGenerator. The image data will not be created + (on either the CPU or GPU) until the image is actually drawn. + Generated data is owned by SkImage and may not be shared or accessed. + + SkImage is returned if generator data is valid. Valid data parameters vary by type of data + and platform. + + imageGenerator may wrap SkPicture data, codec data, or custom data. + + @param imageGenerator stock or custom routines to retrieve SkImage + @return created SkImage, or nullptr +*/ +SK_API sk_sp DeferredFromGenerator(std::unique_ptr imageGenerator); + +enum class BitDepth { + kU8, //!< uses 8-bit unsigned int per color component + kF16, //!< uses 16-bit float per color component +}; + +/** Creates SkImage from picture. Returned SkImage width and height are set by dimensions. + SkImage draws picture with matrix and paint, set to bitDepth and colorSpace. + + The Picture data is not turned into an image (CPU or GPU) until it is drawn. + + If matrix is nullptr, draws with identity SkMatrix. If paint is nullptr, draws + with default SkPaint. colorSpace may be nullptr. + + @param picture stream of drawing commands + @param dimensions width and height + @param matrix SkMatrix to rotate, scale, translate, and so on; may be nullptr + @param paint SkPaint to apply transparency, filtering, and so on; may be nullptr + @param bitDepth 8-bit integer or 16-bit float: per component + @param colorSpace range of colors; may be nullptr + @param props props to use when rasterizing the picture + @return created SkImage, or nullptr +*/ +SK_API sk_sp DeferredFromPicture(sk_sp picture, + const SkISize& dimensions, + const SkMatrix* matrix, + const SkPaint* paint, + BitDepth bitDepth, + sk_sp colorSpace, + SkSurfaceProps props); +SK_API sk_sp DeferredFromPicture(sk_sp picture, + const SkISize& dimensions, + const SkMatrix* matrix, + const SkPaint* paint, + BitDepth bitDepth, + sk_sp colorSpace); + +/** Creates a CPU-backed SkImage from pixmap, copying the pixel data. + As a result, pixmap pixels may be modified or deleted without affecting SkImage. + + SkImage is returned if SkPixmap is valid. Valid SkPixmap parameters include: + dimensions are greater than zero; + each dimension fits in 29 bits; + SkColorType and SkAlphaType are valid, and SkColorType is not kUnknown_SkColorType; + row bytes are large enough to hold one row of pixels; + pixel address is not nullptr. + + @param pixmap SkImageInfo, pixel address, and row bytes + @return copy of SkPixmap pixels, or nullptr + + example: https://fiddle.skia.org/c/@Image_RasterFromPixmapCopy +*/ +SK_API sk_sp RasterFromPixmapCopy(const SkPixmap& pixmap); + +/** Creates CPU-backed SkImage from pixmap, sharing SkPixmap pixels. Pixels must remain valid and + unchanged until rasterReleaseProc is called. rasterReleaseProc is passed + releaseContext when SkImage is deleted or no longer refers to pixmap pixels. + + Pass nullptr for rasterReleaseProc to share SkPixmap without requiring a callback + when SkImage is released. Pass nullptr for releaseContext if rasterReleaseProc + does not require state. + + SkImage is returned if pixmap is valid. Valid SkPixmap parameters include: + dimensions are greater than zero; + each dimension fits in 29 bits; + SkColorType and SkAlphaType are valid, and SkColorType is not kUnknown_SkColorType; + row bytes are large enough to hold one row of pixels; + pixel address is not nullptr. + + @param pixmap SkImageInfo, pixel address, and row bytes + @param rasterReleaseProc function called when pixels can be released; or nullptr + @param releaseContext state passed to rasterReleaseProc; or nullptr + @return SkImage sharing pixmap +*/ +SK_API sk_sp RasterFromPixmap(const SkPixmap& pixmap, + RasterReleaseProc rasterReleaseProc, + ReleaseContext releaseContext); + +/** Creates CPU-backed SkImage from pixel data described by info. + The pixels data will *not* be copied. + + SkImage is returned if SkImageInfo is valid. Valid SkImageInfo parameters include: + dimensions are greater than zero; + each dimension fits in 29 bits; + SkColorType and SkAlphaType are valid, and SkColorType is not kUnknown_SkColorType; + rowBytes are large enough to hold one row of pixels; + pixels is not nullptr, and contains enough data for SkImage. + + @param info contains width, height, SkAlphaType, SkColorType, SkColorSpace + @param pixels address or pixel storage + @param rowBytes size of pixel row or larger + @return SkImage sharing pixels, or nullptr +*/ +SK_API sk_sp RasterFromData(const SkImageInfo& info, + sk_sp pixels, + size_t rowBytes); + +/** Creates a filtered SkImage on the CPU. filter processes the src image, potentially changing + the color, position, and size. subset is the bounds of src that are processed + by filter. clipBounds is the expected bounds of the filtered SkImage. outSubset + is required storage for the actual bounds of the filtered SkImage. offset is + required storage for translation of returned SkImage. + + Returns nullptr a filtered result could not be created. If nullptr is returned, outSubset + and offset are undefined. + + Useful for animation of SkImageFilter that varies size from frame to frame. + outSubset describes the valid bounds of returned image. offset translates the returned SkImage + to keep subsequent animation frames aligned with respect to each other. + + @param src the image to be filtered + @param filter the image filter to be applied + @param subset bounds of SkImage processed by filter + @param clipBounds expected bounds of filtered SkImage + @param outSubset storage for returned SkImage bounds + @param offset storage for returned SkImage translation + @return filtered SkImage, or nullptr +*/ +SK_API sk_sp MakeWithFilter(sk_sp src, + const SkImageFilter* filter, + const SkIRect& subset, + const SkIRect& clipBounds, + SkIRect* outSubset, + SkIPoint* offset); + +} // namespace SkImages + +/** \class SkImage + SkImage describes a two dimensional array of pixels to draw. The pixels may be + decoded in a raster bitmap, encoded in a SkPicture or compressed data stream, + or located in GPU memory as a GPU texture. + + SkImage cannot be modified after it is created. SkImage may allocate additional + storage as needed; for instance, an encoded SkImage may decode when drawn. + + SkImage width and height are greater than zero. Creating an SkImage with zero width + or height returns SkImage equal to nullptr. + + SkImage may be created from SkBitmap, SkPixmap, SkSurface, SkPicture, encoded streams, + GPU texture, YUV_ColorSpace data, or hardware buffer. Encoded streams supported + include BMP, GIF, HEIF, ICO, JPEG, PNG, WBMP, WebP. Supported encoding details + vary with platform. + + See SkImages namespace for the static factory methods to make SkImages. + + Clients should *not* subclass SkImage as there is a lot of internal machinery that is + not publicly accessible. +*/ +class SK_API SkImage : public SkRefCnt { +public: + /** Returns a SkImageInfo describing the width, height, color type, alpha type, and color space + of the SkImage. + + @return image info of SkImage. + */ + const SkImageInfo& imageInfo() const { return fInfo; } + + /** Returns pixel count in each row. + + @return pixel width in SkImage + */ + int width() const { return fInfo.width(); } + + /** Returns pixel row count. + + @return pixel height in SkImage + */ + int height() const { return fInfo.height(); } + + /** Returns SkISize { width(), height() }. + + @return integral size of width() and height() + */ + SkISize dimensions() const { return SkISize::Make(fInfo.width(), fInfo.height()); } + + /** Returns SkIRect { 0, 0, width(), height() }. + + @return integral rectangle from origin to width() and height() + */ + SkIRect bounds() const { return SkIRect::MakeWH(fInfo.width(), fInfo.height()); } + + /** Returns value unique to image. SkImage contents cannot change after SkImage is + created. Any operation to create a new SkImage will receive generate a new + unique number. + + @return unique identifier + */ + uint32_t uniqueID() const { return fUniqueID; } + + /** Returns SkAlphaType. + + SkAlphaType returned was a parameter to an SkImage constructor, + or was parsed from encoded data. + + @return SkAlphaType in SkImage + + example: https://fiddle.skia.org/c/@Image_alphaType + */ + SkAlphaType alphaType() const; + + /** Returns SkColorType if known; otherwise, returns kUnknown_SkColorType. + + @return SkColorType of SkImage + + example: https://fiddle.skia.org/c/@Image_colorType + */ + SkColorType colorType() const; + + /** Returns SkColorSpace, the range of colors, associated with SkImage. The + reference count of SkColorSpace is unchanged. The returned SkColorSpace is + immutable. + + SkColorSpace returned was passed to an SkImage constructor, + or was parsed from encoded data. SkColorSpace returned may be ignored when SkImage + is drawn, depending on the capabilities of the SkSurface receiving the drawing. + + @return SkColorSpace in SkImage, or nullptr + + example: https://fiddle.skia.org/c/@Image_colorSpace + */ + SkColorSpace* colorSpace() const; + + /** Returns a smart pointer to SkColorSpace, the range of colors, associated with + SkImage. The smart pointer tracks the number of objects sharing this + SkColorSpace reference so the memory is released when the owners destruct. + + The returned SkColorSpace is immutable. + + SkColorSpace returned was passed to an SkImage constructor, + or was parsed from encoded data. SkColorSpace returned may be ignored when SkImage + is drawn, depending on the capabilities of the SkSurface receiving the drawing. + + @return SkColorSpace in SkImage, or nullptr, wrapped in a smart pointer + + example: https://fiddle.skia.org/c/@Image_refColorSpace + */ + sk_sp refColorSpace() const; + + /** Returns true if SkImage pixels represent transparency only. If true, each pixel + is packed in 8 bits as defined by kAlpha_8_SkColorType. + + @return true if pixels represent a transparency mask + + example: https://fiddle.skia.org/c/@Image_isAlphaOnly + */ + bool isAlphaOnly() const; + + /** Returns true if pixels ignore their alpha value and are treated as fully opaque. + + @return true if SkAlphaType is kOpaque_SkAlphaType + */ + bool isOpaque() const { return SkAlphaTypeIsOpaque(this->alphaType()); } + + /** + * Make a shader with the specified tiling and mipmap sampling. + */ + sk_sp makeShader(SkTileMode tmx, SkTileMode tmy, const SkSamplingOptions&, + const SkMatrix* localMatrix = nullptr) const; + sk_sp makeShader(SkTileMode tmx, SkTileMode tmy, const SkSamplingOptions& sampling, + const SkMatrix& lm) const; + /** Defaults to clamp in both X and Y. */ + sk_sp makeShader(const SkSamplingOptions& sampling, const SkMatrix& lm) const; + sk_sp makeShader(const SkSamplingOptions& sampling, + const SkMatrix* lm = nullptr) const; + + /** + * makeRawShader functions like makeShader, but for images that contain non-color data. + * This includes images encoding things like normals, material properties (eg, roughness), + * heightmaps, or any other purely mathematical data that happens to be stored in an image. + * These types of images are useful with some programmable shaders (see: SkRuntimeEffect). + * + * Raw image shaders work like regular image shaders (including filtering and tiling), with + * a few major differences: + * - No color space transformation is ever applied (the color space of the image is ignored). + * - Images with an alpha type of kUnpremul are *not* automatically premultiplied. + * - Bicubic filtering is not supported. If SkSamplingOptions::useCubic is true, these + * factories will return nullptr. + */ + sk_sp makeRawShader(SkTileMode tmx, SkTileMode tmy, const SkSamplingOptions&, + const SkMatrix* localMatrix = nullptr) const; + sk_sp makeRawShader(SkTileMode tmx, SkTileMode tmy, const SkSamplingOptions& sampling, + const SkMatrix& lm) const; + /** Defaults to clamp in both X and Y. */ + sk_sp makeRawShader(const SkSamplingOptions& sampling, const SkMatrix& lm) const; + sk_sp makeRawShader(const SkSamplingOptions& sampling, + const SkMatrix* lm = nullptr) const; + + /** Copies SkImage pixel address, row bytes, and SkImageInfo to pixmap, if address + is available, and returns true. If pixel address is not available, return + false and leave pixmap unchanged. + + @param pixmap storage for pixel state if pixels are readable; otherwise, ignored + @return true if SkImage has direct access to pixels + + example: https://fiddle.skia.org/c/@Image_peekPixels + */ + bool peekPixels(SkPixmap* pixmap) const; + + /** Returns true if the contents of SkImage was created on or uploaded to GPU memory, + and is available as a GPU texture. + + @return true if SkImage is a GPU texture + + example: https://fiddle.skia.org/c/@Image_isTextureBacked + */ + virtual bool isTextureBacked() const = 0; + + /** Returns an approximation of the amount of texture memory used by the image. Returns + zero if the image is not texture backed or if the texture has an external format. + */ + virtual size_t textureSize() const = 0; + + /** Returns true if SkImage can be drawn on either raster surface or GPU surface. + If context is nullptr, tests if SkImage draws on raster surface; + otherwise, tests if SkImage draws on GPU surface associated with context. + + SkImage backed by GPU texture may become invalid if associated context is + invalid. lazy image may be invalid and may not draw to raster surface or + GPU surface or both. + + @param context GPU context + @return true if SkImage can be drawn + + example: https://fiddle.skia.org/c/@Image_isValid + */ + virtual bool isValid(GrRecordingContext* context) const = 0; + + /** \enum SkImage::CachingHint + CachingHint selects whether Skia may internally cache SkBitmap generated by + decoding SkImage, or by copying SkImage from GPU to CPU. The default behavior + allows caching SkBitmap. + + Choose kDisallow_CachingHint if SkImage pixels are to be used only once, or + if SkImage pixels reside in a cache outside of Skia, or to reduce memory pressure. + + Choosing kAllow_CachingHint does not ensure that pixels will be cached. + SkImage pixels may not be cached if memory requirements are too large or + pixels are not accessible. + */ + enum CachingHint { + kAllow_CachingHint, //!< allows internally caching decoded and copied pixels + kDisallow_CachingHint, //!< disallows internally caching decoded and copied pixels + }; + + /** Copies SkRect of pixels from SkImage to dstPixels. Copy starts at offset (srcX, srcY), + and does not exceed SkImage (width(), height()). + + dstInfo specifies width, height, SkColorType, SkAlphaType, and SkColorSpace of + destination. dstRowBytes specifies the gap from one destination row to the next. + Returns true if pixels are copied. Returns false if: + - dstInfo.addr() equals nullptr + - dstRowBytes is less than dstInfo.minRowBytes() + - SkPixelRef is nullptr + + Pixels are copied only if pixel conversion is possible. If SkImage SkColorType is + kGray_8_SkColorType, or kAlpha_8_SkColorType; dstInfo.colorType() must match. + If SkImage SkColorType is kGray_8_SkColorType, dstInfo.colorSpace() must match. + If SkImage SkAlphaType is kOpaque_SkAlphaType, dstInfo.alphaType() must + match. If SkImage SkColorSpace is nullptr, dstInfo.colorSpace() must match. Returns + false if pixel conversion is not possible. + + srcX and srcY may be negative to copy only top or left of source. Returns + false if width() or height() is zero or negative. + Returns false if abs(srcX) >= Image width(), or if abs(srcY) >= Image height(). + + If cachingHint is kAllow_CachingHint, pixels may be retained locally. + If cachingHint is kDisallow_CachingHint, pixels are not added to the local cache. + + @param context the GrDirectContext in play, if it exists + @param dstInfo destination width, height, SkColorType, SkAlphaType, SkColorSpace + @param dstPixels destination pixel storage + @param dstRowBytes destination row length + @param srcX column index whose absolute value is less than width() + @param srcY row index whose absolute value is less than height() + @param cachingHint whether the pixels should be cached locally + @return true if pixels are copied to dstPixels + */ + bool readPixels(GrDirectContext* context, + const SkImageInfo& dstInfo, + void* dstPixels, + size_t dstRowBytes, + int srcX, int srcY, + CachingHint cachingHint = kAllow_CachingHint) const; + + /** Copies a SkRect of pixels from SkImage to dst. Copy starts at (srcX, srcY), and + does not exceed SkImage (width(), height()). + + dst specifies width, height, SkColorType, SkAlphaType, SkColorSpace, pixel storage, + and row bytes of destination. dst.rowBytes() specifics the gap from one destination + row to the next. Returns true if pixels are copied. Returns false if: + - dst pixel storage equals nullptr + - dst.rowBytes is less than SkImageInfo::minRowBytes + - SkPixelRef is nullptr + + Pixels are copied only if pixel conversion is possible. If SkImage SkColorType is + kGray_8_SkColorType, or kAlpha_8_SkColorType; dst.colorType() must match. + If SkImage SkColorType is kGray_8_SkColorType, dst.colorSpace() must match. + If SkImage SkAlphaType is kOpaque_SkAlphaType, dst.alphaType() must + match. If SkImage SkColorSpace is nullptr, dst.colorSpace() must match. Returns + false if pixel conversion is not possible. + + srcX and srcY may be negative to copy only top or left of source. Returns + false if width() or height() is zero or negative. + Returns false if abs(srcX) >= Image width(), or if abs(srcY) >= Image height(). + + If cachingHint is kAllow_CachingHint, pixels may be retained locally. + If cachingHint is kDisallow_CachingHint, pixels are not added to the local cache. + + @param context the GrDirectContext in play, if it exists + @param dst destination SkPixmap: SkImageInfo, pixels, row bytes + @param srcX column index whose absolute value is less than width() + @param srcY row index whose absolute value is less than height() + @param cachingHint whether the pixels should be cached locallyZ + @return true if pixels are copied to dst + */ + bool readPixels(GrDirectContext* context, + const SkPixmap& dst, + int srcX, + int srcY, + CachingHint cachingHint = kAllow_CachingHint) const; + +#if defined(GRAPHITE_TEST_UTILS) + bool readPixelsGraphite(skgpu::graphite::Recorder*, + const SkPixmap& dst, + int srcX, + int srcY) const; +#endif + +#ifndef SK_IMAGE_READ_PIXELS_DISABLE_LEGACY_API + /** Deprecated. Use the variants that accept a GrDirectContext. */ + bool readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes, + int srcX, int srcY, CachingHint cachingHint = kAllow_CachingHint) const; + bool readPixels(const SkPixmap& dst, int srcX, int srcY, + CachingHint cachingHint = kAllow_CachingHint) const; +#endif + + /** The result from asyncRescaleAndReadPixels() or asyncRescaleAndReadPixelsYUV420(). */ + class AsyncReadResult { + public: + AsyncReadResult(const AsyncReadResult&) = delete; + AsyncReadResult(AsyncReadResult&&) = delete; + AsyncReadResult& operator=(const AsyncReadResult&) = delete; + AsyncReadResult& operator=(AsyncReadResult&&) = delete; + + virtual ~AsyncReadResult() = default; + virtual int count() const = 0; + virtual const void* data(int i) const = 0; + virtual size_t rowBytes(int i) const = 0; + + protected: + AsyncReadResult() = default; + }; + + /** Client-provided context that is passed to client-provided ReadPixelsContext. */ + using ReadPixelsContext = void*; + + /** Client-provided callback to asyncRescaleAndReadPixels() or + asyncRescaleAndReadPixelsYUV420() that is called when read result is ready or on failure. + */ + using ReadPixelsCallback = void(ReadPixelsContext, std::unique_ptr); + + enum class RescaleGamma : bool { kSrc, kLinear }; + + enum class RescaleMode { + kNearest, + kLinear, + kRepeatedLinear, + kRepeatedCubic, + }; + + /** Makes image pixel data available to caller, possibly asynchronously. It can also rescale + the image pixels. + + Currently asynchronous reads are only supported on the GPU backend and only when the + underlying 3D API supports transfer buffers and CPU/GPU synchronization primitives. In all + other cases this operates synchronously. + + Data is read from the source sub-rectangle, is optionally converted to a linear gamma, is + rescaled to the size indicated by 'info', is then converted to the color space, color type, + and alpha type of 'info'. A 'srcRect' that is not contained by the bounds of the image + causes failure. + + When the pixel data is ready the caller's ReadPixelsCallback is called with a + AsyncReadResult containing pixel data in the requested color type, alpha type, and color + space. The AsyncReadResult will have count() == 1. Upon failure the callback is called with + nullptr for AsyncReadResult. For a GPU image this flushes work but a submit must occur to + guarantee a finite time before the callback is called. + + The data is valid for the lifetime of AsyncReadResult with the exception that if the SkImage + is GPU-backed the data is immediately invalidated if the context is abandoned or + destroyed. + + @param info info of the requested pixels + @param srcRect subrectangle of image to read + @param rescaleGamma controls whether rescaling is done in the image's gamma or whether + the source data is transformed to a linear gamma before rescaling. + @param rescaleMode controls the technique (and cost) of the rescaling + @param callback function to call with result of the read + @param context passed to callback + */ + void asyncRescaleAndReadPixels(const SkImageInfo& info, + const SkIRect& srcRect, + RescaleGamma rescaleGamma, + RescaleMode rescaleMode, + ReadPixelsCallback callback, + ReadPixelsContext context) const; + + /** + Similar to asyncRescaleAndReadPixels but performs an additional conversion to YUV. The + RGB->YUV conversion is controlled by 'yuvColorSpace'. The YUV data is returned as three + planes ordered y, u, v. The u and v planes are half the width and height of the resized + rectangle. The y, u, and v values are single bytes. Currently this fails if 'dstSize' + width and height are not even. A 'srcRect' that is not contained by the bounds of the + image causes failure. + + When the pixel data is ready the caller's ReadPixelsCallback is called with a + AsyncReadResult containing the planar data. The AsyncReadResult will have count() == 3. + Upon failure the callback is called with nullptr for AsyncReadResult. For a GPU image this + flushes work but a submit must occur to guarantee a finite time before the callback is + called. + + The data is valid for the lifetime of AsyncReadResult with the exception that if the SkImage + is GPU-backed the data is immediately invalidated if the context is abandoned or + destroyed. + + @param yuvColorSpace The transformation from RGB to YUV. Applied to the resized image + after it is converted to dstColorSpace. + @param dstColorSpace The color space to convert the resized image to, after rescaling. + @param srcRect The portion of the image to rescale and convert to YUV planes. + @param dstSize The size to rescale srcRect to + @param rescaleGamma controls whether rescaling is done in the image's gamma or whether + the source data is transformed to a linear gamma before rescaling. + @param rescaleMode controls the technique (and cost) of the rescaling + @param callback function to call with the planar read result + @param context passed to callback + */ + void asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace, + sk_sp dstColorSpace, + const SkIRect& srcRect, + const SkISize& dstSize, + RescaleGamma rescaleGamma, + RescaleMode rescaleMode, + ReadPixelsCallback callback, + ReadPixelsContext context) const; + + /** + * Identical to asyncRescaleAndReadPixelsYUV420 but a fourth plane is returned in the + * AsyncReadResult passed to 'callback'. The fourth plane contains the alpha chanel at the + * same full resolution as the Y plane. + */ + void asyncRescaleAndReadPixelsYUVA420(SkYUVColorSpace yuvColorSpace, + sk_sp dstColorSpace, + const SkIRect& srcRect, + const SkISize& dstSize, + RescaleGamma rescaleGamma, + RescaleMode rescaleMode, + ReadPixelsCallback callback, + ReadPixelsContext context) const; + + /** Copies SkImage to dst, scaling pixels to fit dst.width() and dst.height(), and + converting pixels to match dst.colorType() and dst.alphaType(). Returns true if + pixels are copied. Returns false if dst.addr() is nullptr, or dst.rowBytes() is + less than dst SkImageInfo::minRowBytes. + + Pixels are copied only if pixel conversion is possible. If SkImage SkColorType is + kGray_8_SkColorType, or kAlpha_8_SkColorType; dst.colorType() must match. + If SkImage SkColorType is kGray_8_SkColorType, dst.colorSpace() must match. + If SkImage SkAlphaType is kOpaque_SkAlphaType, dst.alphaType() must + match. If SkImage SkColorSpace is nullptr, dst.colorSpace() must match. Returns + false if pixel conversion is not possible. + + If cachingHint is kAllow_CachingHint, pixels may be retained locally. + If cachingHint is kDisallow_CachingHint, pixels are not added to the local cache. + + @param dst destination SkPixmap: SkImageInfo, pixels, row bytes + @return true if pixels are scaled to fit dst + */ + bool scalePixels(const SkPixmap& dst, const SkSamplingOptions&, + CachingHint cachingHint = kAllow_CachingHint) const; + + /** Returns encoded SkImage pixels as SkData, if SkImage was created from supported + encoded stream format. Platform support for formats vary and may require building + with one or more of: SK_ENCODE_JPEG, SK_ENCODE_PNG, SK_ENCODE_WEBP. + + Returns nullptr if SkImage contents are not encoded. + + @return encoded SkImage, or nullptr + + example: https://fiddle.skia.org/c/@Image_refEncodedData + */ + sk_sp refEncodedData() const; + + /** Returns subset of this image. + + Returns nullptr if any of the following are true: + - Subset is empty + - Subset is not contained inside the image's bounds + - Pixels in the source image could not be read or copied + - This image is texture-backed and the provided context is null or does not match + the source image's context. + + If the source image was texture-backed, the resulting image will be texture-backed also. + Otherwise, the returned image will be raster-backed. + + @param direct the GrDirectContext of the source image (nullptr is ok if the source image + is not texture-backed). + @param subset bounds of returned SkImage + @return the subsetted image, or nullptr + + example: https://fiddle.skia.org/c/@Image_makeSubset + */ + virtual sk_sp makeSubset(GrDirectContext* direct, const SkIRect& subset) const = 0; + + struct RequiredProperties { + bool fMipmapped; + + bool operator==(const RequiredProperties& other) const { + return fMipmapped == other.fMipmapped; + } + + bool operator!=(const RequiredProperties& other) const { return !(*this == other); } + + bool operator<(const RequiredProperties& other) const { + return fMipmapped < other.fMipmapped; + } + }; + + /** Returns subset of this image. + + Returns nullptr if any of the following are true: + - Subset is empty + - Subset is not contained inside the image's bounds + - Pixels in the image could not be read or copied + - This image is texture-backed and the provided context is null or does not match + the source image's context. + + If the source image was texture-backed, the resulting image will be texture-backed also. + Otherwise, the returned image will be raster-backed. + + @param recorder the recorder of the source image (nullptr is ok if the + source image was texture-backed). + @param subset bounds of returned SkImage + @param RequiredProperties properties the returned SkImage must possess (e.g. mipmaps) + @return the subsetted image, or nullptr + */ + virtual sk_sp makeSubset(skgpu::graphite::Recorder*, + const SkIRect& subset, + RequiredProperties) const = 0; + + /** + * Returns true if the image has mipmap levels. + */ + bool hasMipmaps() const; + + /** + * Returns true if the image holds protected content. + */ + bool isProtected() const; + + /** + * Returns an image with the same "base" pixels as the this image, but with mipmap levels + * automatically generated and attached. + */ + sk_sp withDefaultMipmaps() const; + + /** Returns raster image or lazy image. Copies SkImage backed by GPU texture into + CPU memory if needed. Returns original SkImage if decoded in raster bitmap, + or if encoded in a stream. + + Returns nullptr if backed by GPU texture and copy fails. + + @return raster image, lazy image, or nullptr + + example: https://fiddle.skia.org/c/@Image_makeNonTextureImage + */ + sk_sp makeNonTextureImage(GrDirectContext* = nullptr) const; + + /** Returns raster image. Copies SkImage backed by GPU texture into CPU memory, + or decodes SkImage from lazy image. Returns original SkImage if decoded in + raster bitmap. + + Returns nullptr if copy, decode, or pixel read fails. + + If cachingHint is kAllow_CachingHint, pixels may be retained locally. + If cachingHint is kDisallow_CachingHint, pixels are not added to the local cache. + + @return raster image, or nullptr + + example: https://fiddle.skia.org/c/@Image_makeRasterImage + */ + sk_sp makeRasterImage(GrDirectContext*, + CachingHint cachingHint = kDisallow_CachingHint) const; + +#if !defined(SK_IMAGE_READ_PIXELS_DISABLE_LEGACY_API) + sk_sp makeRasterImage(CachingHint cachingHint = kDisallow_CachingHint) const { + return this->makeRasterImage(nullptr, cachingHint); + } +#endif + + /** Deprecated. + */ + enum LegacyBitmapMode { + kRO_LegacyBitmapMode, //!< returned bitmap is read-only and immutable + }; + + /** Deprecated. + Creates raster SkBitmap with same pixels as SkImage. If legacyBitmapMode is + kRO_LegacyBitmapMode, returned bitmap is read-only and immutable. + Returns true if SkBitmap is stored in bitmap. Returns false and resets bitmap if + SkBitmap write did not succeed. + + @param bitmap storage for legacy SkBitmap + @param legacyBitmapMode bitmap is read-only and immutable + @return true if SkBitmap was created + */ + bool asLegacyBitmap(SkBitmap* bitmap, + LegacyBitmapMode legacyBitmapMode = kRO_LegacyBitmapMode) const; + + /** Returns true if SkImage is backed by an image-generator or other service that creates + and caches its pixels or texture on-demand. + + @return true if SkImage is created as needed + + example: https://fiddle.skia.org/c/@Image_isLazyGenerated_a + example: https://fiddle.skia.org/c/@Image_isLazyGenerated_b + */ + virtual bool isLazyGenerated() const = 0; + + /** Creates SkImage in target SkColorSpace. + Returns nullptr if SkImage could not be created. + + Returns original SkImage if it is in target SkColorSpace. + Otherwise, converts pixels from SkImage SkColorSpace to target SkColorSpace. + If SkImage colorSpace() returns nullptr, SkImage SkColorSpace is assumed to be sRGB. + + If this image is texture-backed, the context parameter is required and must match the + context of the source image. + + @param direct The GrDirectContext in play, if it exists + @param target SkColorSpace describing color range of returned SkImage + @return created SkImage in target SkColorSpace + + example: https://fiddle.skia.org/c/@Image_makeColorSpace + */ + virtual sk_sp makeColorSpace(GrDirectContext* direct, + sk_sp target) const = 0; + + /** Creates SkImage in target SkColorSpace. + Returns nullptr if SkImage could not be created. + + Returns original SkImage if it is in target SkColorSpace. + Otherwise, converts pixels from SkImage SkColorSpace to target SkColorSpace. + If SkImage colorSpace() returns nullptr, SkImage SkColorSpace is assumed to be sRGB. + + If this image is graphite-backed, the recorder parameter is required. + + @param targetColorSpace SkColorSpace describing color range of returned SkImage + @param recorder The Recorder in which to create the new image + @param RequiredProperties properties the returned SkImage must possess (e.g. mipmaps) + @return created SkImage in target SkColorSpace + */ + virtual sk_sp makeColorSpace(skgpu::graphite::Recorder*, + sk_sp targetColorSpace, + RequiredProperties) const = 0; + + /** Experimental. + Creates SkImage in target SkColorType and SkColorSpace. + Returns nullptr if SkImage could not be created. + + Returns original SkImage if it is in target SkColorType and SkColorSpace. + + If this image is texture-backed, the context parameter is required and must match the + context of the source image. + + @param direct The GrDirectContext in play, if it exists + @param targetColorType SkColorType of returned SkImage + @param targetColorSpace SkColorSpace of returned SkImage + @return created SkImage in target SkColorType and SkColorSpace + */ + virtual sk_sp makeColorTypeAndColorSpace(GrDirectContext* direct, + SkColorType targetColorType, + sk_sp targetCS) const = 0; + + /** Experimental. + Creates SkImage in target SkColorType and SkColorSpace. + Returns nullptr if SkImage could not be created. + + Returns original SkImage if it is in target SkColorType and SkColorSpace. + + If this image is graphite-backed, the recorder parameter is required. + + @param targetColorType SkColorType of returned SkImage + @param targetColorSpace SkColorSpace of returned SkImage + @param recorder The Recorder in which to create the new image + @param RequiredProperties properties the returned SkImage must possess (e.g. mipmaps) + @return created SkImage in target SkColorType and SkColorSpace + */ + virtual sk_sp makeColorTypeAndColorSpace(skgpu::graphite::Recorder*, + SkColorType targetColorType, + sk_sp targetColorSpace, + RequiredProperties) const = 0; + + /** Creates a new SkImage identical to this one, but with a different SkColorSpace. + This does not convert the underlying pixel data, so the resulting image will draw + differently. + */ + sk_sp reinterpretColorSpace(sk_sp newColorSpace) const; + +private: + SkImage(const SkImageInfo& info, uint32_t uniqueID); + + friend class SkBitmap; + friend class SkImage_Base; // for private ctor + friend class SkImage_Raster; // for withMipmaps + friend class SkMipmapBuilder; + + SkImageInfo fInfo; + const uint32_t fUniqueID; + + sk_sp withMipmaps(sk_sp) const; + + using INHERITED = SkRefCnt; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkImageFilter.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkImageFilter.h new file mode 100644 index 0000000000..7352cf71f7 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkImageFilter.h @@ -0,0 +1,119 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkImageFilter_DEFINED +#define SkImageFilter_DEFINED + +#include "include/core/SkFlattenable.h" +#include "include/core/SkRect.h" +#include "include/core/SkRefCnt.h" +#include "include/private/base/SkAPI.h" + +#include + +class SkColorFilter; +class SkMatrix; +struct SkDeserialProcs; + +/** + * Base class for image filters. If one is installed in the paint, then all drawing occurs as + * usual, but it is as if the drawing happened into an offscreen (before the xfermode is applied). + * This offscreen bitmap will then be handed to the imagefilter, who in turn creates a new bitmap + * which is what will finally be drawn to the device (using the original xfermode). + * + * The local space of image filters matches the local space of the drawn geometry. For instance if + * there is rotation on the canvas, the blur will be computed along those rotated axes and not in + * the device space. In order to achieve this result, the actual drawing of the geometry may happen + * in an unrotated coordinate system so that the filtered image can be computed more easily, and + * then it will be post transformed to match what would have been produced if the geometry were + * drawn with the total canvas matrix to begin with. + */ +class SK_API SkImageFilter : public SkFlattenable { +public: + enum MapDirection { + kForward_MapDirection, + kReverse_MapDirection, + }; + /** + * Map a device-space rect recursively forward or backward through the filter DAG. + * kForward_MapDirection is used to determine which pixels of the destination canvas a source + * image rect would touch after filtering. kReverse_MapDirection is used to determine which rect + * of the source image would be required to fill the given rect (typically, clip bounds). Used + * for clipping and temp-buffer allocations, so the result need not be exact, but should never + * be smaller than the real answer. The default implementation recursively unions all input + * bounds, or returns the source rect if no inputs. + * + * In kReverse mode, 'inputRect' is the device-space bounds of the input pixels. In kForward + * mode it should always be null. If 'inputRect' is null in kReverse mode the resulting answer + * may be incorrect. + */ + SkIRect filterBounds(const SkIRect& src, const SkMatrix& ctm, + MapDirection, const SkIRect* inputRect = nullptr) const; + + /** + * Returns whether this image filter is a color filter and puts the color filter into the + * "filterPtr" parameter if it can. Does nothing otherwise. + * If this returns false, then the filterPtr is unchanged. + * If this returns true, then if filterPtr is not null, it must be set to a ref'd colorfitler + * (i.e. it may not be set to NULL). + */ + bool isColorFilterNode(SkColorFilter** filterPtr) const; + + // DEPRECATED : use isColorFilterNode() instead + bool asColorFilter(SkColorFilter** filterPtr) const { + return this->isColorFilterNode(filterPtr); + } + + /** + * Returns true (and optionally returns a ref'd filter) if this imagefilter can be completely + * replaced by the returned colorfilter. i.e. the two effects will affect drawing in the same + * way. + */ + bool asAColorFilter(SkColorFilter** filterPtr) const; + + /** + * Returns the number of inputs this filter will accept (some inputs can be NULL). + */ + int countInputs() const; + + /** + * Returns the input filter at a given index, or NULL if no input is connected. The indices + * used are filter-specific. + */ + const SkImageFilter* getInput(int i) const; + + // Default impl returns union of all input bounds. + virtual SkRect computeFastBounds(const SkRect& bounds) const; + + // Can this filter DAG compute the resulting bounds of an object-space rectangle? + bool canComputeFastBounds() const; + + /** + * If this filter can be represented by another filter + a localMatrix, return that filter, + * else return null. + */ + sk_sp makeWithLocalMatrix(const SkMatrix& matrix) const; + + static sk_sp Deserialize(const void* data, size_t size, + const SkDeserialProcs* procs = nullptr) { + return sk_sp(static_cast( + SkFlattenable::Deserialize(kSkImageFilter_Type, data, size, procs).release())); + } + +protected: + + sk_sp refMe() const { + return sk_ref_sp(const_cast(this)); + } + +private: + friend class SkImageFilter_Base; + + using INHERITED = SkFlattenable; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkImageGenerator.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkImageGenerator.h new file mode 100644 index 0000000000..4210cdb601 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkImageGenerator.h @@ -0,0 +1,148 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkImageGenerator_DEFINED +#define SkImageGenerator_DEFINED + +#include "include/core/SkData.h" +#include "include/core/SkImageInfo.h" +#include "include/core/SkPixmap.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkYUVAPixmaps.h" +#include "include/private/base/SkAPI.h" + +#if defined(SK_GRAPHITE) +#include "include/core/SkImage.h" +#include "include/gpu/graphite/Recorder.h" +#endif + +#include +#include + +class GrRecordingContext; + +class SK_API SkImageGenerator { +public: + /** + * The PixelRef which takes ownership of this SkImageGenerator + * will call the image generator's destructor. + */ + virtual ~SkImageGenerator() { } + + uint32_t uniqueID() const { return fUniqueID; } + + /** + * Return a ref to the encoded (i.e. compressed) representation + * of this data. + * + * If non-NULL is returned, the caller is responsible for calling + * unref() on the data when it is finished. + */ + sk_sp refEncodedData() { + return this->onRefEncodedData(); + } + + /** + * Return the ImageInfo associated with this generator. + */ + const SkImageInfo& getInfo() const { return fInfo; } + + /** + * Can this generator be used to produce images that will be drawable to the specified context + * (or to CPU, if context is nullptr)? + */ + bool isValid(GrRecordingContext* context) const { + return this->onIsValid(context); + } + + /** + * Will this generator produce protected content + */ + bool isProtected() const { + return this->onIsProtected(); + } + + /** + * Decode into the given pixels, a block of memory of size at + * least (info.fHeight - 1) * rowBytes + (info.fWidth * + * bytesPerPixel) + * + * Repeated calls to this function should give the same results, + * allowing the PixelRef to be immutable. + * + * @param info A description of the format + * expected by the caller. This can simply be identical + * to the info returned by getInfo(). + * + * This contract also allows the caller to specify + * different output-configs, which the implementation can + * decide to support or not. + * + * A size that does not match getInfo() implies a request + * to scale. If the generator cannot perform this scale, + * it will return false. + * + * @return true on success. + */ + bool getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes); + + bool getPixels(const SkPixmap& pm) { + return this->getPixels(pm.info(), pm.writable_addr(), pm.rowBytes()); + } + + /** + * If decoding to YUV is supported, this returns true. Otherwise, this + * returns false and the caller will ignore output parameter yuvaPixmapInfo. + * + * @param supportedDataTypes Indicates the data type/planar config combinations that are + * supported by the caller. If the generator supports decoding to + * YUV(A), but not as a type in supportedDataTypes, this method + * returns false. + * @param yuvaPixmapInfo Output parameter that specifies the planar configuration, subsampling, + * orientation, chroma siting, plane color types, and row bytes. + */ + bool queryYUVAInfo(const SkYUVAPixmapInfo::SupportedDataTypes& supportedDataTypes, + SkYUVAPixmapInfo* yuvaPixmapInfo) const; + + /** + * Returns true on success and false on failure. + * This always attempts to perform a full decode. To get the planar + * configuration without decoding use queryYUVAInfo(). + * + * @param yuvaPixmaps Contains preallocated pixmaps configured according to a successful call + * to queryYUVAInfo(). + */ + bool getYUVAPlanes(const SkYUVAPixmaps& yuvaPixmaps); + + virtual bool isTextureGenerator() const { return false; } + +protected: + static constexpr int kNeedNewImageUniqueID = 0; + + SkImageGenerator(const SkImageInfo& info, uint32_t uniqueId = kNeedNewImageUniqueID); + + virtual sk_sp onRefEncodedData() { return nullptr; } + struct Options {}; + virtual bool onGetPixels(const SkImageInfo&, void*, size_t, const Options&) { return false; } + virtual bool onIsValid(GrRecordingContext*) const { return true; } + virtual bool onIsProtected() const { return false; } + virtual bool onQueryYUVAInfo(const SkYUVAPixmapInfo::SupportedDataTypes&, + SkYUVAPixmapInfo*) const { return false; } + virtual bool onGetYUVAPlanes(const SkYUVAPixmaps&) { return false; } + + const SkImageInfo fInfo; + +private: + const uint32_t fUniqueID; + + SkImageGenerator(SkImageGenerator&&) = delete; + SkImageGenerator(const SkImageGenerator&) = delete; + SkImageGenerator& operator=(SkImageGenerator&&) = delete; + SkImageGenerator& operator=(const SkImageGenerator&) = delete; +}; + +#endif // SkImageGenerator_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkImageInfo.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkImageInfo.h new file mode 100644 index 0000000000..4d08e429fc --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkImageInfo.h @@ -0,0 +1,628 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkImageInfo_DEFINED +#define SkImageInfo_DEFINED + +#include "include/core/SkAlphaType.h" +#include "include/core/SkColorType.h" +#include "include/core/SkRect.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkSize.h" +#include "include/private/base/SkAPI.h" +#include "include/private/base/SkDebug.h" +#include "include/private/base/SkMath.h" +#include "include/private/base/SkTFitsIn.h" + +#include +#include +#include + +class SkColorSpace; + +/** Returns the number of bytes required to store a pixel, including unused padding. + Returns zero if ct is kUnknown_SkColorType or invalid. + + @return bytes per pixel +*/ +SK_API int SkColorTypeBytesPerPixel(SkColorType ct); + +/** Returns true if SkColorType always decodes alpha to 1.0, making the pixel + fully opaque. If true, SkColorType does not reserve bits to encode alpha. + + @return true if alpha is always set to 1.0 +*/ +SK_API bool SkColorTypeIsAlwaysOpaque(SkColorType ct); + +/** Returns true if canonical can be set to a valid SkAlphaType for colorType. If + there is more than one valid canonical SkAlphaType, set to alphaType, if valid. + If true is returned and canonical is not nullptr, store valid SkAlphaType. + + Returns false only if alphaType is kUnknown_SkAlphaType, color type is not + kUnknown_SkColorType, and SkColorType is not always opaque. If false is returned, + canonical is ignored. + + @param canonical storage for SkAlphaType + @return true if valid SkAlphaType can be associated with colorType +*/ +SK_API bool SkColorTypeValidateAlphaType(SkColorType colorType, SkAlphaType alphaType, + SkAlphaType* canonical = nullptr); + +/** \enum SkImageInfo::SkYUVColorSpace + Describes color range of YUV pixels. The color mapping from YUV to RGB varies + depending on the source. YUV pixels may be generated by JPEG images, standard + video streams, or high definition video streams. Each has its own mapping from + YUV to RGB. + + JPEG YUV values encode the full range of 0 to 255 for all three components. + Video YUV values often range from 16 to 235 for Y and from 16 to 240 for U and V (limited). + Details of encoding and conversion to RGB are described in YCbCr color space. + + The identity colorspace exists to provide a utility mapping from Y to R, U to G and V to B. + It can be used to visualize the YUV planes or to explicitly post process the YUV channels. +*/ +enum SkYUVColorSpace : int { + kJPEG_Full_SkYUVColorSpace, //!< describes full range + kRec601_Limited_SkYUVColorSpace, //!< describes SDTV range + kRec709_Full_SkYUVColorSpace, //!< describes HDTV range + kRec709_Limited_SkYUVColorSpace, + kBT2020_8bit_Full_SkYUVColorSpace, //!< describes UHDTV range, non-constant-luminance + kBT2020_8bit_Limited_SkYUVColorSpace, + kBT2020_10bit_Full_SkYUVColorSpace, + kBT2020_10bit_Limited_SkYUVColorSpace, + kBT2020_12bit_Full_SkYUVColorSpace, + kBT2020_12bit_Limited_SkYUVColorSpace, + kFCC_Full_SkYUVColorSpace, //!< describes FCC range + kFCC_Limited_SkYUVColorSpace, + kSMPTE240_Full_SkYUVColorSpace, //!< describes SMPTE240M range + kSMPTE240_Limited_SkYUVColorSpace, + kYDZDX_Full_SkYUVColorSpace, //!< describes YDZDX range + kYDZDX_Limited_SkYUVColorSpace, + kGBR_Full_SkYUVColorSpace, //!< describes GBR range + kGBR_Limited_SkYUVColorSpace, + kYCgCo_8bit_Full_SkYUVColorSpace, //!< describes YCgCo matrix + kYCgCo_8bit_Limited_SkYUVColorSpace, + kYCgCo_10bit_Full_SkYUVColorSpace, + kYCgCo_10bit_Limited_SkYUVColorSpace, + kYCgCo_12bit_Full_SkYUVColorSpace, + kYCgCo_12bit_Limited_SkYUVColorSpace, + kIdentity_SkYUVColorSpace, //!< maps Y->R, U->G, V->B + + kLastEnum_SkYUVColorSpace = kIdentity_SkYUVColorSpace, //!< last valid value + + // Legacy (deprecated) names: + kJPEG_SkYUVColorSpace = kJPEG_Full_SkYUVColorSpace, + kRec601_SkYUVColorSpace = kRec601_Limited_SkYUVColorSpace, + kRec709_SkYUVColorSpace = kRec709_Limited_SkYUVColorSpace, + kBT2020_SkYUVColorSpace = kBT2020_8bit_Limited_SkYUVColorSpace, +}; + +/** \struct SkColorInfo + Describes pixel and encoding. SkImageInfo can be created from SkColorInfo by + providing dimensions. + + It encodes how pixel bits describe alpha, transparency; color components red, blue, + and green; and SkColorSpace, the range and linearity of colors. +*/ +class SK_API SkColorInfo { +public: + /** Creates an SkColorInfo with kUnknown_SkColorType, kUnknown_SkAlphaType, + and no SkColorSpace. + + @return empty SkImageInfo + */ + SkColorInfo(); + ~SkColorInfo(); + + /** Creates SkColorInfo from SkColorType ct, SkAlphaType at, and optionally SkColorSpace cs. + + If SkColorSpace cs is nullptr and SkColorInfo is part of drawing source: SkColorSpace + defaults to sRGB, mapping into SkSurface SkColorSpace. + + Parameters are not validated to see if their values are legal, or that the + combination is supported. + @return created SkColorInfo + */ + SkColorInfo(SkColorType ct, SkAlphaType at, sk_sp cs); + + SkColorInfo(const SkColorInfo&); + SkColorInfo(SkColorInfo&&); + + SkColorInfo& operator=(const SkColorInfo&); + SkColorInfo& operator=(SkColorInfo&&); + + SkColorSpace* colorSpace() const; + sk_sp refColorSpace() const; + SkColorType colorType() const { return fColorType; } + SkAlphaType alphaType() const { return fAlphaType; } + + bool isOpaque() const { + return SkAlphaTypeIsOpaque(fAlphaType) + || SkColorTypeIsAlwaysOpaque(fColorType); + } + + bool gammaCloseToSRGB() const; + + /** Does other represent the same color type, alpha type, and color space? */ + bool operator==(const SkColorInfo& other) const; + + /** Does other represent a different color type, alpha type, or color space? */ + bool operator!=(const SkColorInfo& other) const; + + /** Creates SkColorInfo with same SkColorType, SkColorSpace, with SkAlphaType set + to newAlphaType. + + Created SkColorInfo contains newAlphaType even if it is incompatible with + SkColorType, in which case SkAlphaType in SkColorInfo is ignored. + */ + SkColorInfo makeAlphaType(SkAlphaType newAlphaType) const; + + /** Creates new SkColorInfo with same SkAlphaType, SkColorSpace, with SkColorType + set to newColorType. + */ + SkColorInfo makeColorType(SkColorType newColorType) const; + + /** Creates SkColorInfo with same SkAlphaType, SkColorType, with SkColorSpace + set to cs. cs may be nullptr. + */ + SkColorInfo makeColorSpace(sk_sp cs) const; + + /** Returns number of bytes per pixel required by SkColorType. + Returns zero if colorType() is kUnknown_SkColorType. + + @return bytes in pixel + + example: https://fiddle.skia.org/c/@ImageInfo_bytesPerPixel + */ + int bytesPerPixel() const; + + /** Returns bit shift converting row bytes to row pixels. + Returns zero for kUnknown_SkColorType. + + @return one of: 0, 1, 2, 3, 4; left shift to convert pixels to bytes + + example: https://fiddle.skia.org/c/@ImageInfo_shiftPerPixel + */ + int shiftPerPixel() const; + +private: + sk_sp fColorSpace; + SkColorType fColorType = kUnknown_SkColorType; + SkAlphaType fAlphaType = kUnknown_SkAlphaType; +}; + +/** \struct SkImageInfo + Describes pixel dimensions and encoding. SkBitmap, SkImage, PixMap, and SkSurface + can be created from SkImageInfo. SkImageInfo can be retrieved from SkBitmap and + SkPixmap, but not from SkImage and SkSurface. For example, SkImage and SkSurface + implementations may defer pixel depth, so may not completely specify SkImageInfo. + + SkImageInfo contains dimensions, the pixel integral width and height. It encodes + how pixel bits describe alpha, transparency; color components red, blue, + and green; and SkColorSpace, the range and linearity of colors. +*/ +struct SK_API SkImageInfo { +public: + + /** Creates an empty SkImageInfo with kUnknown_SkColorType, kUnknown_SkAlphaType, + a width and height of zero, and no SkColorSpace. + + @return empty SkImageInfo + */ + SkImageInfo() = default; + + /** Creates SkImageInfo from integral dimensions width and height, SkColorType ct, + SkAlphaType at, and optionally SkColorSpace cs. + + If SkColorSpace cs is nullptr and SkImageInfo is part of drawing source: SkColorSpace + defaults to sRGB, mapping into SkSurface SkColorSpace. + + Parameters are not validated to see if their values are legal, or that the + combination is supported. + + @param width pixel column count; must be zero or greater + @param height pixel row count; must be zero or greater + @param cs range of colors; may be nullptr + @return created SkImageInfo + */ + static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at); + static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at, + sk_sp cs); + static SkImageInfo Make(SkISize dimensions, SkColorType ct, SkAlphaType at); + static SkImageInfo Make(SkISize dimensions, SkColorType ct, SkAlphaType at, + sk_sp cs); + + /** Creates SkImageInfo from integral dimensions and SkColorInfo colorInfo, + + Parameters are not validated to see if their values are legal, or that the + combination is supported. + + @param dimensions pixel column and row count; must be zeros or greater + @param SkColorInfo the pixel encoding consisting of SkColorType, SkAlphaType, and + SkColorSpace (which may be nullptr) + @return created SkImageInfo + */ + static SkImageInfo Make(SkISize dimensions, const SkColorInfo& colorInfo) { + return SkImageInfo(dimensions, colorInfo); + } + static SkImageInfo Make(SkISize dimensions, SkColorInfo&& colorInfo) { + return SkImageInfo(dimensions, std::move(colorInfo)); + } + + /** Creates SkImageInfo from integral dimensions width and height, kN32_SkColorType, + SkAlphaType at, and optionally SkColorSpace cs. kN32_SkColorType will equal either + kBGRA_8888_SkColorType or kRGBA_8888_SkColorType, whichever is optimal. + + If SkColorSpace cs is nullptr and SkImageInfo is part of drawing source: SkColorSpace + defaults to sRGB, mapping into SkSurface SkColorSpace. + + Parameters are not validated to see if their values are legal, or that the + combination is supported. + + @param width pixel column count; must be zero or greater + @param height pixel row count; must be zero or greater + @param cs range of colors; may be nullptr + @return created SkImageInfo + */ + static SkImageInfo MakeN32(int width, int height, SkAlphaType at); + static SkImageInfo MakeN32(int width, int height, SkAlphaType at, sk_sp cs); + + /** Creates SkImageInfo from integral dimensions width and height, kN32_SkColorType, + SkAlphaType at, with sRGB SkColorSpace. + + Parameters are not validated to see if their values are legal, or that the + combination is supported. + + @param width pixel column count; must be zero or greater + @param height pixel row count; must be zero or greater + @return created SkImageInfo + + example: https://fiddle.skia.org/c/@ImageInfo_MakeS32 + */ + static SkImageInfo MakeS32(int width, int height, SkAlphaType at); + + /** Creates SkImageInfo from integral dimensions width and height, kN32_SkColorType, + kPremul_SkAlphaType, with optional SkColorSpace. + + If SkColorSpace cs is nullptr and SkImageInfo is part of drawing source: SkColorSpace + defaults to sRGB, mapping into SkSurface SkColorSpace. + + Parameters are not validated to see if their values are legal, or that the + combination is supported. + + @param width pixel column count; must be zero or greater + @param height pixel row count; must be zero or greater + @param cs range of colors; may be nullptr + @return created SkImageInfo + */ + static SkImageInfo MakeN32Premul(int width, int height); + static SkImageInfo MakeN32Premul(int width, int height, sk_sp cs); + + /** Creates SkImageInfo from integral dimensions width and height, kN32_SkColorType, + kPremul_SkAlphaType, with SkColorSpace set to nullptr. + + If SkImageInfo is part of drawing source: SkColorSpace defaults to sRGB, mapping + into SkSurface SkColorSpace. + + Parameters are not validated to see if their values are legal, or that the + combination is supported. + + @param dimensions width and height, each must be zero or greater + @param cs range of colors; may be nullptr + @return created SkImageInfo + */ + static SkImageInfo MakeN32Premul(SkISize dimensions); + static SkImageInfo MakeN32Premul(SkISize dimensions, sk_sp cs); + + /** Creates SkImageInfo from integral dimensions width and height, kAlpha_8_SkColorType, + kPremul_SkAlphaType, with SkColorSpace set to nullptr. + + @param width pixel column count; must be zero or greater + @param height pixel row count; must be zero or greater + @return created SkImageInfo + */ + static SkImageInfo MakeA8(int width, int height); + /** Creates SkImageInfo from integral dimensions, kAlpha_8_SkColorType, + kPremul_SkAlphaType, with SkColorSpace set to nullptr. + + @param dimensions pixel row and column count; must be zero or greater + @return created SkImageInfo + */ + static SkImageInfo MakeA8(SkISize dimensions); + + /** Creates SkImageInfo from integral dimensions width and height, kUnknown_SkColorType, + kUnknown_SkAlphaType, with SkColorSpace set to nullptr. + + Returned SkImageInfo as part of source does not draw, and as part of destination + can not be drawn to. + + @param width pixel column count; must be zero or greater + @param height pixel row count; must be zero or greater + @return created SkImageInfo + */ + static SkImageInfo MakeUnknown(int width, int height); + + /** Creates SkImageInfo from integral dimensions width and height set to zero, + kUnknown_SkColorType, kUnknown_SkAlphaType, with SkColorSpace set to nullptr. + + Returned SkImageInfo as part of source does not draw, and as part of destination + can not be drawn to. + + @return created SkImageInfo + */ + static SkImageInfo MakeUnknown() { + return MakeUnknown(0, 0); + } + + /** Returns pixel count in each row. + + @return pixel width + */ + int width() const { return fDimensions.width(); } + + /** Returns pixel row count. + + @return pixel height + */ + int height() const { return fDimensions.height(); } + + SkColorType colorType() const { return fColorInfo.colorType(); } + + SkAlphaType alphaType() const { return fColorInfo.alphaType(); } + + /** Returns SkColorSpace, the range of colors. The reference count of + SkColorSpace is unchanged. The returned SkColorSpace is immutable. + + @return SkColorSpace, or nullptr + */ + SkColorSpace* colorSpace() const; + + /** Returns smart pointer to SkColorSpace, the range of colors. The smart pointer + tracks the number of objects sharing this SkColorSpace reference so the memory + is released when the owners destruct. + + The returned SkColorSpace is immutable. + + @return SkColorSpace wrapped in a smart pointer + */ + sk_sp refColorSpace() const; + + /** Returns if SkImageInfo describes an empty area of pixels by checking if either + width or height is zero or smaller. + + @return true if either dimension is zero or smaller + */ + bool isEmpty() const { return fDimensions.isEmpty(); } + + /** Returns the dimensionless SkColorInfo that represents the same color type, + alpha type, and color space as this SkImageInfo. + */ + const SkColorInfo& colorInfo() const { return fColorInfo; } + + /** Returns true if SkAlphaType is set to hint that all pixels are opaque; their + alpha value is implicitly or explicitly 1.0. If true, and all pixels are + not opaque, Skia may draw incorrectly. + + Does not check if SkColorType allows alpha, or if any pixel value has + transparency. + + @return true if SkAlphaType is kOpaque_SkAlphaType + */ + bool isOpaque() const { return fColorInfo.isOpaque(); } + + /** Returns SkISize { width(), height() }. + + @return integral size of width() and height() + */ + SkISize dimensions() const { return fDimensions; } + + /** Returns SkIRect { 0, 0, width(), height() }. + + @return integral rectangle from origin to width() and height() + */ + SkIRect bounds() const { return SkIRect::MakeSize(fDimensions); } + + /** Returns true if associated SkColorSpace is not nullptr, and SkColorSpace gamma + is approximately the same as sRGB. + This includes the + + @return true if SkColorSpace gamma is approximately the same as sRGB + */ + bool gammaCloseToSRGB() const { return fColorInfo.gammaCloseToSRGB(); } + + /** Creates SkImageInfo with the same SkColorType, SkColorSpace, and SkAlphaType, + with dimensions set to width and height. + + @param newWidth pixel column count; must be zero or greater + @param newHeight pixel row count; must be zero or greater + @return created SkImageInfo + */ + SkImageInfo makeWH(int newWidth, int newHeight) const { + return Make({newWidth, newHeight}, fColorInfo); + } + + /** Creates SkImageInfo with the same SkColorType, SkColorSpace, and SkAlphaType, + with dimensions set to newDimensions. + + @param newSize pixel column and row count; must be zero or greater + @return created SkImageInfo + */ + SkImageInfo makeDimensions(SkISize newSize) const { + return Make(newSize, fColorInfo); + } + + /** Creates SkImageInfo with same SkColorType, SkColorSpace, width, and height, + with SkAlphaType set to newAlphaType. + + Created SkImageInfo contains newAlphaType even if it is incompatible with + SkColorType, in which case SkAlphaType in SkImageInfo is ignored. + + @return created SkImageInfo + */ + SkImageInfo makeAlphaType(SkAlphaType newAlphaType) const { + return Make(fDimensions, fColorInfo.makeAlphaType(newAlphaType)); + } + + /** Creates SkImageInfo with same SkAlphaType, SkColorSpace, width, and height, + with SkColorType set to newColorType. + + @return created SkImageInfo + */ + SkImageInfo makeColorType(SkColorType newColorType) const { + return Make(fDimensions, fColorInfo.makeColorType(newColorType)); + } + + /** Creates SkImageInfo with same SkAlphaType, SkColorType, width, and height, + with SkColorSpace set to cs. + + @param cs range of colors; may be nullptr + @return created SkImageInfo + */ + SkImageInfo makeColorSpace(sk_sp cs) const; + + /** Returns number of bytes per pixel required by SkColorType. + Returns zero if colorType( is kUnknown_SkColorType. + + @return bytes in pixel + */ + int bytesPerPixel() const { return fColorInfo.bytesPerPixel(); } + + /** Returns bit shift converting row bytes to row pixels. + Returns zero for kUnknown_SkColorType. + + @return one of: 0, 1, 2, 3; left shift to convert pixels to bytes + */ + int shiftPerPixel() const { return fColorInfo.shiftPerPixel(); } + + /** Returns minimum bytes per row, computed from pixel width() and SkColorType, which + specifies bytesPerPixel(). SkBitmap maximum value for row bytes must fit + in 31 bits. + + @return width() times bytesPerPixel() as unsigned 64-bit integer + */ + uint64_t minRowBytes64() const { + return (uint64_t)sk_64_mul(this->width(), this->bytesPerPixel()); + } + + /** Returns minimum bytes per row, computed from pixel width() and SkColorType, which + specifies bytesPerPixel(). SkBitmap maximum value for row bytes must fit + in 31 bits. + + @return width() times bytesPerPixel() as size_t + */ + size_t minRowBytes() const { + uint64_t minRowBytes = this->minRowBytes64(); + if (!SkTFitsIn(minRowBytes)) { + return 0; + } + return (size_t)minRowBytes; + } + + /** Returns byte offset of pixel from pixel base address. + + Asserts in debug build if x or y is outside of bounds. Does not assert if + rowBytes is smaller than minRowBytes(), even though result may be incorrect. + + @param x column index, zero or greater, and less than width() + @param y row index, zero or greater, and less than height() + @param rowBytes size of pixel row or larger + @return offset within pixel array + + example: https://fiddle.skia.org/c/@ImageInfo_computeOffset + */ + size_t computeOffset(int x, int y, size_t rowBytes) const; + + /** Compares SkImageInfo with other, and returns true if width, height, SkColorType, + SkAlphaType, and SkColorSpace are equivalent. + + @param other SkImageInfo to compare + @return true if SkImageInfo equals other + */ + bool operator==(const SkImageInfo& other) const { + return fDimensions == other.fDimensions && fColorInfo == other.fColorInfo; + } + + /** Compares SkImageInfo with other, and returns true if width, height, SkColorType, + SkAlphaType, and SkColorSpace are not equivalent. + + @param other SkImageInfo to compare + @return true if SkImageInfo is not equal to other + */ + bool operator!=(const SkImageInfo& other) const { + return !(*this == other); + } + + /** Returns storage required by pixel array, given SkImageInfo dimensions, SkColorType, + and rowBytes. rowBytes is assumed to be at least as large as minRowBytes(). + + Returns zero if height is zero. + Returns SIZE_MAX if answer exceeds the range of size_t. + + @param rowBytes size of pixel row or larger + @return memory required by pixel buffer + */ + size_t computeByteSize(size_t rowBytes) const; + + /** Returns storage required by pixel array, given SkImageInfo dimensions, and + SkColorType. Uses minRowBytes() to compute bytes for pixel row. + + Returns zero if height is zero. + Returns SIZE_MAX if answer exceeds the range of size_t. + + @return least memory required by pixel buffer + */ + size_t computeMinByteSize() const { + return this->computeByteSize(this->minRowBytes()); + } + + /** Returns true if byteSize equals SIZE_MAX. computeByteSize() and + computeMinByteSize() return SIZE_MAX if size_t can not hold buffer size. + + @param byteSize result of computeByteSize() or computeMinByteSize() + @return true if computeByteSize() or computeMinByteSize() result exceeds size_t + */ + static bool ByteSizeOverflowed(size_t byteSize) { + return SIZE_MAX == byteSize; + } + + /** Returns true if rowBytes is valid for this SkImageInfo. + + @param rowBytes size of pixel row including padding + @return true if rowBytes is large enough to contain pixel row and is properly + aligned + */ + bool validRowBytes(size_t rowBytes) const { + if (rowBytes < this->minRowBytes64()) { + return false; + } + int shift = this->shiftPerPixel(); + size_t alignedRowBytes = rowBytes >> shift << shift; + return alignedRowBytes == rowBytes; + } + + /** Creates an empty SkImageInfo with kUnknown_SkColorType, kUnknown_SkAlphaType, + a width and height of zero, and no SkColorSpace. + */ + void reset() { *this = {}; } + + /** Asserts if internal values are illegal or inconsistent. Only available if + SK_DEBUG is defined at compile time. + */ + SkDEBUGCODE(void validate() const;) + +private: + SkColorInfo fColorInfo; + SkISize fDimensions = {0, 0}; + + SkImageInfo(SkISize dimensions, const SkColorInfo& colorInfo) + : fColorInfo(colorInfo), fDimensions(dimensions) {} + + SkImageInfo(SkISize dimensions, SkColorInfo&& colorInfo) + : fColorInfo(std::move(colorInfo)), fDimensions(dimensions) {} +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkM44.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkM44.h new file mode 100644 index 0000000000..d8c77ea07a --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkM44.h @@ -0,0 +1,442 @@ +/* + * Copyright 2020 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkM44_DEFINED +#define SkM44_DEFINED + +#include "include/core/SkMatrix.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" + +#include + +struct SkRect; + +struct SK_API SkV2 { + float x, y; + + bool operator==(const SkV2 v) const { return x == v.x && y == v.y; } + bool operator!=(const SkV2 v) const { return !(*this == v); } + + static SkScalar Dot(SkV2 a, SkV2 b) { return a.x * b.x + a.y * b.y; } + static SkScalar Cross(SkV2 a, SkV2 b) { return a.x * b.y - a.y * b.x; } + static SkV2 Normalize(SkV2 v) { return v * (1.0f / v.length()); } + + SkV2 operator-() const { return {-x, -y}; } + SkV2 operator+(SkV2 v) const { return {x+v.x, y+v.y}; } + SkV2 operator-(SkV2 v) const { return {x-v.x, y-v.y}; } + + SkV2 operator*(SkV2 v) const { return {x*v.x, y*v.y}; } + friend SkV2 operator*(SkV2 v, SkScalar s) { return {v.x*s, v.y*s}; } + friend SkV2 operator*(SkScalar s, SkV2 v) { return {v.x*s, v.y*s}; } + friend SkV2 operator/(SkV2 v, SkScalar s) { return {v.x/s, v.y/s}; } + friend SkV2 operator/(SkScalar s, SkV2 v) { return {s/v.x, s/v.y}; } + + void operator+=(SkV2 v) { *this = *this + v; } + void operator-=(SkV2 v) { *this = *this - v; } + void operator*=(SkV2 v) { *this = *this * v; } + void operator*=(SkScalar s) { *this = *this * s; } + void operator/=(SkScalar s) { *this = *this / s; } + + SkScalar lengthSquared() const { return Dot(*this, *this); } + SkScalar length() const { return SkScalarSqrt(this->lengthSquared()); } + + SkScalar dot(SkV2 v) const { return Dot(*this, v); } + SkScalar cross(SkV2 v) const { return Cross(*this, v); } + SkV2 normalize() const { return Normalize(*this); } + + const float* ptr() const { return &x; } + float* ptr() { return &x; } +}; + +struct SK_API SkV3 { + float x, y, z; + + bool operator==(const SkV3& v) const { + return x == v.x && y == v.y && z == v.z; + } + bool operator!=(const SkV3& v) const { return !(*this == v); } + + static SkScalar Dot(const SkV3& a, const SkV3& b) { return a.x*b.x + a.y*b.y + a.z*b.z; } + static SkV3 Cross(const SkV3& a, const SkV3& b) { + return { a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x }; + } + static SkV3 Normalize(const SkV3& v) { return v * (1.0f / v.length()); } + + SkV3 operator-() const { return {-x, -y, -z}; } + SkV3 operator+(const SkV3& v) const { return { x + v.x, y + v.y, z + v.z }; } + SkV3 operator-(const SkV3& v) const { return { x - v.x, y - v.y, z - v.z }; } + + SkV3 operator*(const SkV3& v) const { + return { x*v.x, y*v.y, z*v.z }; + } + friend SkV3 operator*(const SkV3& v, SkScalar s) { + return { v.x*s, v.y*s, v.z*s }; + } + friend SkV3 operator*(SkScalar s, const SkV3& v) { return v*s; } + + void operator+=(SkV3 v) { *this = *this + v; } + void operator-=(SkV3 v) { *this = *this - v; } + void operator*=(SkV3 v) { *this = *this * v; } + void operator*=(SkScalar s) { *this = *this * s; } + + SkScalar lengthSquared() const { return Dot(*this, *this); } + SkScalar length() const { return SkScalarSqrt(Dot(*this, *this)); } + + SkScalar dot(const SkV3& v) const { return Dot(*this, v); } + SkV3 cross(const SkV3& v) const { return Cross(*this, v); } + SkV3 normalize() const { return Normalize(*this); } + + const float* ptr() const { return &x; } + float* ptr() { return &x; } +}; + +struct SK_API SkV4 { + float x, y, z, w; + + bool operator==(const SkV4& v) const { + return x == v.x && y == v.y && z == v.z && w == v.w; + } + bool operator!=(const SkV4& v) const { return !(*this == v); } + + static SkScalar Dot(const SkV4& a, const SkV4& b) { + return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w; + } + static SkV4 Normalize(const SkV4& v) { return v * (1.0f / v.length()); } + + SkV4 operator-() const { return {-x, -y, -z, -w}; } + SkV4 operator+(const SkV4& v) const { return { x + v.x, y + v.y, z + v.z, w + v.w }; } + SkV4 operator-(const SkV4& v) const { return { x - v.x, y - v.y, z - v.z, w - v.w }; } + + SkV4 operator*(const SkV4& v) const { + return { x*v.x, y*v.y, z*v.z, w*v.w }; + } + friend SkV4 operator*(const SkV4& v, SkScalar s) { + return { v.x*s, v.y*s, v.z*s, v.w*s }; + } + friend SkV4 operator*(SkScalar s, const SkV4& v) { return v*s; } + + SkScalar lengthSquared() const { return Dot(*this, *this); } + SkScalar length() const { return SkScalarSqrt(Dot(*this, *this)); } + + SkScalar dot(const SkV4& v) const { return Dot(*this, v); } + SkV4 normalize() const { return Normalize(*this); } + + const float* ptr() const { return &x; } + float* ptr() { return &x; } + + float operator[](int i) const { + SkASSERT(i >= 0 && i < 4); + return this->ptr()[i]; + } + float& operator[](int i) { + SkASSERT(i >= 0 && i < 4); + return this->ptr()[i]; + } +}; + +/** + * 4x4 matrix used by SkCanvas and other parts of Skia. + * + * Skia assumes a right-handed coordinate system: + * +X goes to the right + * +Y goes down + * +Z goes into the screen (away from the viewer) + */ +class SK_API SkM44 { +public: + SkM44(const SkM44& src) = default; + SkM44& operator=(const SkM44& src) = default; + + constexpr SkM44() + : fMat{1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1} + {} + + SkM44(const SkM44& a, const SkM44& b) { + this->setConcat(a, b); + } + + enum Uninitialized_Constructor { + kUninitialized_Constructor + }; + SkM44(Uninitialized_Constructor) {} + + enum NaN_Constructor { + kNaN_Constructor + }; + constexpr SkM44(NaN_Constructor) + : fMat{SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, + SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, + SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, + SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN} + {} + + /** + * The constructor parameters are in row-major order. + */ + constexpr SkM44(SkScalar m0, SkScalar m4, SkScalar m8, SkScalar m12, + SkScalar m1, SkScalar m5, SkScalar m9, SkScalar m13, + SkScalar m2, SkScalar m6, SkScalar m10, SkScalar m14, + SkScalar m3, SkScalar m7, SkScalar m11, SkScalar m15) + // fMat is column-major order in memory. + : fMat{m0, m1, m2, m3, + m4, m5, m6, m7, + m8, m9, m10, m11, + m12, m13, m14, m15} + {} + + static SkM44 Rows(const SkV4& r0, const SkV4& r1, const SkV4& r2, const SkV4& r3) { + SkM44 m(kUninitialized_Constructor); + m.setRow(0, r0); + m.setRow(1, r1); + m.setRow(2, r2); + m.setRow(3, r3); + return m; + } + static SkM44 Cols(const SkV4& c0, const SkV4& c1, const SkV4& c2, const SkV4& c3) { + SkM44 m(kUninitialized_Constructor); + m.setCol(0, c0); + m.setCol(1, c1); + m.setCol(2, c2); + m.setCol(3, c3); + return m; + } + + static SkM44 RowMajor(const SkScalar r[16]) { + return SkM44(r[ 0], r[ 1], r[ 2], r[ 3], + r[ 4], r[ 5], r[ 6], r[ 7], + r[ 8], r[ 9], r[10], r[11], + r[12], r[13], r[14], r[15]); + } + static SkM44 ColMajor(const SkScalar c[16]) { + return SkM44(c[0], c[4], c[ 8], c[12], + c[1], c[5], c[ 9], c[13], + c[2], c[6], c[10], c[14], + c[3], c[7], c[11], c[15]); + } + + static SkM44 Translate(SkScalar x, SkScalar y, SkScalar z = 0) { + return SkM44(1, 0, 0, x, + 0, 1, 0, y, + 0, 0, 1, z, + 0, 0, 0, 1); + } + + static SkM44 Scale(SkScalar x, SkScalar y, SkScalar z = 1) { + return SkM44(x, 0, 0, 0, + 0, y, 0, 0, + 0, 0, z, 0, + 0, 0, 0, 1); + } + + static SkM44 Rotate(SkV3 axis, SkScalar radians) { + SkM44 m(kUninitialized_Constructor); + m.setRotate(axis, radians); + return m; + } + + // Scales and translates 'src' to fill 'dst' exactly. + static SkM44 RectToRect(const SkRect& src, const SkRect& dst); + + static SkM44 LookAt(const SkV3& eye, const SkV3& center, const SkV3& up); + static SkM44 Perspective(float near, float far, float angle); + + bool operator==(const SkM44& other) const; + bool operator!=(const SkM44& other) const { + return !(other == *this); + } + + void getColMajor(SkScalar v[]) const { + memcpy(v, fMat, sizeof(fMat)); + } + void getRowMajor(SkScalar v[]) const; + + SkScalar rc(int r, int c) const { + SkASSERT(r >= 0 && r <= 3); + SkASSERT(c >= 0 && c <= 3); + return fMat[c*4 + r]; + } + void setRC(int r, int c, SkScalar value) { + SkASSERT(r >= 0 && r <= 3); + SkASSERT(c >= 0 && c <= 3); + fMat[c*4 + r] = value; + } + + SkV4 row(int i) const { + SkASSERT(i >= 0 && i <= 3); + return {fMat[i + 0], fMat[i + 4], fMat[i + 8], fMat[i + 12]}; + } + SkV4 col(int i) const { + SkASSERT(i >= 0 && i <= 3); + return {fMat[i*4 + 0], fMat[i*4 + 1], fMat[i*4 + 2], fMat[i*4 + 3]}; + } + + void setRow(int i, const SkV4& v) { + SkASSERT(i >= 0 && i <= 3); + fMat[i + 0] = v.x; + fMat[i + 4] = v.y; + fMat[i + 8] = v.z; + fMat[i + 12] = v.w; + } + void setCol(int i, const SkV4& v) { + SkASSERT(i >= 0 && i <= 3); + memcpy(&fMat[i*4], v.ptr(), sizeof(v)); + } + + SkM44& setIdentity() { + *this = { 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 }; + return *this; + } + + SkM44& setTranslate(SkScalar x, SkScalar y, SkScalar z = 0) { + *this = { 1, 0, 0, x, + 0, 1, 0, y, + 0, 0, 1, z, + 0, 0, 0, 1 }; + return *this; + } + + SkM44& setScale(SkScalar x, SkScalar y, SkScalar z = 1) { + *this = { x, 0, 0, 0, + 0, y, 0, 0, + 0, 0, z, 0, + 0, 0, 0, 1 }; + return *this; + } + + /** + * Set this matrix to rotate about the specified unit-length axis vector, + * by an angle specified by its sin() and cos(). + * + * This does not attempt to verify that axis.length() == 1 or that the sin,cos values + * are correct. + */ + SkM44& setRotateUnitSinCos(SkV3 axis, SkScalar sinAngle, SkScalar cosAngle); + + /** + * Set this matrix to rotate about the specified unit-length axis vector, + * by an angle specified in radians. + * + * This does not attempt to verify that axis.length() == 1. + */ + SkM44& setRotateUnit(SkV3 axis, SkScalar radians) { + return this->setRotateUnitSinCos(axis, SkScalarSin(radians), SkScalarCos(radians)); + } + + /** + * Set this matrix to rotate about the specified axis vector, + * by an angle specified in radians. + * + * Note: axis is not assumed to be unit-length, so it will be normalized internally. + * If axis is already unit-length, call setRotateAboutUnitRadians() instead. + */ + SkM44& setRotate(SkV3 axis, SkScalar radians); + + SkM44& setConcat(const SkM44& a, const SkM44& b); + + friend SkM44 operator*(const SkM44& a, const SkM44& b) { + return SkM44(a, b); + } + + SkM44& preConcat(const SkM44& m) { + return this->setConcat(*this, m); + } + + SkM44& postConcat(const SkM44& m) { + return this->setConcat(m, *this); + } + + /** + * A matrix is categorized as 'perspective' if the bottom row is not [0, 0, 0, 1]. + * For most uses, a bottom row of [0, 0, 0, X] behaves like a non-perspective matrix, though + * it will be categorized as perspective. Calling normalizePerspective() will change the + * matrix such that, if its bottom row was [0, 0, 0, X], it will be changed to [0, 0, 0, 1] + * by scaling the rest of the matrix by 1/X. + * + * | A B C D | | A/X B/X C/X D/X | + * | E F G H | -> | E/X F/X G/X H/X | for X != 0 + * | I J K L | | I/X J/X K/X L/X | + * | 0 0 0 X | | 0 0 0 1 | + */ + void normalizePerspective(); + + /** Returns true if all elements of the matrix are finite. Returns false if any + element is infinity, or NaN. + + @return true if matrix has only finite elements + */ + bool isFinite() const { return SkIsFinite(fMat, 16); } + + /** If this is invertible, return that in inverse and return true. If it is + * not invertible, return false and leave the inverse parameter unchanged. + */ + [[nodiscard]] bool invert(SkM44* inverse) const; + + [[nodiscard]] SkM44 transpose() const; + + void dump() const; + + //////////// + + SkV4 map(float x, float y, float z, float w) const; + SkV4 operator*(const SkV4& v) const { + return this->map(v.x, v.y, v.z, v.w); + } + SkV3 operator*(SkV3 v) const { + auto v4 = this->map(v.x, v.y, v.z, 0); + return {v4.x, v4.y, v4.z}; + } + ////////////////////// Converting to/from SkMatrix + + /* When converting from SkM44 to SkMatrix, the third row and + * column is dropped. When converting from SkMatrix to SkM44 + * the third row and column remain as identity: + * [ a b c ] [ a b 0 c ] + * [ d e f ] -> [ d e 0 f ] + * [ g h i ] [ 0 0 1 0 ] + * [ g h 0 i ] + */ + SkMatrix asM33() const { + return SkMatrix::MakeAll(fMat[0], fMat[4], fMat[12], + fMat[1], fMat[5], fMat[13], + fMat[3], fMat[7], fMat[15]); + } + + explicit SkM44(const SkMatrix& src) + : SkM44(src[SkMatrix::kMScaleX], src[SkMatrix::kMSkewX], 0, src[SkMatrix::kMTransX], + src[SkMatrix::kMSkewY], src[SkMatrix::kMScaleY], 0, src[SkMatrix::kMTransY], + 0, 0, 1, 0, + src[SkMatrix::kMPersp0], src[SkMatrix::kMPersp1], 0, src[SkMatrix::kMPersp2]) + {} + + SkM44& preTranslate(SkScalar x, SkScalar y, SkScalar z = 0); + SkM44& postTranslate(SkScalar x, SkScalar y, SkScalar z = 0); + + SkM44& preScale(SkScalar x, SkScalar y); + SkM44& preScale(SkScalar x, SkScalar y, SkScalar z); + SkM44& preConcat(const SkMatrix&); + +private: + /* Stored in column-major. + * Indices + * 0 4 8 12 1 0 0 trans_x + * 1 5 9 13 e.g. 0 1 0 trans_y + * 2 6 10 14 0 0 1 trans_z + * 3 7 11 15 0 0 0 1 + */ + SkScalar fMat[16]; + + friend class SkMatrixPriv; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkMallocPixelRef.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkMallocPixelRef.h new file mode 100644 index 0000000000..5f37348583 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkMallocPixelRef.h @@ -0,0 +1,45 @@ +/* + * Copyright 2008 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkMallocPixelRef_DEFINED +#define SkMallocPixelRef_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkTypes.h" + +#include + +class SkData; +class SkPixelRef; +struct SkImageInfo; + +/** We explicitly use the same allocator for our pixels that SkMask does, + so that we can freely assign memory allocated by one class to the other. +*/ +namespace SkMallocPixelRef { + /** + * Return a new SkMallocPixelRef, automatically allocating storage for the + * pixels. If rowBytes are 0, an optimal value will be chosen automatically. + * If rowBytes is > 0, then it will be respected, or NULL will be returned + * if rowBytes is invalid for the specified info. + * + * All pixel bytes are zeroed. + * + * Returns NULL on failure. + */ + SK_API sk_sp MakeAllocate(const SkImageInfo&, size_t rowBytes); + + /** + * Return a new SkMallocPixelRef that will use the provided SkData and + * rowBytes as pixel storage. The SkData will be ref()ed and on + * destruction of the PixelRef, the SkData will be unref()ed. + * + * Returns NULL on failure. + */ + SK_API sk_sp MakeWithData(const SkImageInfo&, size_t rowBytes, sk_sp data); +} // namespace SkMallocPixelRef +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkMaskFilter.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkMaskFilter.h new file mode 100644 index 0000000000..9d03e98c0c --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkMaskFilter.h @@ -0,0 +1,53 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkMaskFilter_DEFINED +#define SkMaskFilter_DEFINED + +#include "include/core/SkFlattenable.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" + +#include + +enum SkBlurStyle : int; +struct SkDeserialProcs; +struct SkRect; + +/** \class SkMaskFilter + + SkMaskFilter is the base class for object that perform transformations on + the mask before drawing it. An example subclass is Blur. +*/ +class SK_API SkMaskFilter : public SkFlattenable { +public: + /** Create a blur maskfilter. + * @param style The SkBlurStyle to use + * @param sigma Standard deviation of the Gaussian blur to apply. Must be > 0. + * @param respectCTM if true the blur's sigma is modified by the CTM. + * @return The new blur maskfilter + */ + static sk_sp 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 Deserialize(const void* data, size_t size, + const SkDeserialProcs* procs = nullptr); + +private: + static void RegisterFlattenables(); + friend class SkFlattenable; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkMatrix.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkMatrix.h new file mode 100644 index 0000000000..10ee699db1 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkMatrix.h @@ -0,0 +1,1997 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkMatrix_DEFINED +#define SkMatrix_DEFINED + +#include "include/core/SkPoint.h" +#include "include/core/SkRect.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" +#include "include/private/base/SkFloatingPoint.h" +#include "include/private/base/SkMacros.h" +#include "include/private/base/SkTo.h" + +#include +#include + +struct SkPoint3; +struct SkRSXform; +struct SkSize; + +// Remove when clients are updated to live without this +#define SK_SUPPORT_LEGACY_MATRIX_RECTTORECT + +/** + * When we transform points through a matrix containing perspective (the bottom row is something + * other than 0,0,1), the bruteforce math can produce confusing results (since we might divide + * by 0, or a negative w value). By default, methods that map rects and paths will apply + * perspective clipping, but this can be changed by specifying kYes to those methods. + */ +enum class SkApplyPerspectiveClip { + kNo, //!< Don't pre-clip the geometry before applying the (perspective) matrix + kYes, //!< Do pre-clip the geometry before applying the (perspective) matrix +}; + +/** \class SkMatrix + SkMatrix holds a 3x3 matrix for transforming coordinates. This allows mapping + SkPoint and vectors with translation, scaling, skewing, rotation, and + perspective. + + SkMatrix elements are in row major order. + SkMatrix constexpr default constructs to identity. + + SkMatrix includes a hidden variable that classifies the type of matrix to + improve performance. SkMatrix is not thread safe unless getType() is called first. + + example: https://fiddle.skia.org/c/@Matrix_063 +*/ +SK_BEGIN_REQUIRE_DENSE +class SK_API SkMatrix { +public: + + /** Creates an identity SkMatrix: + + | 1 0 0 | + | 0 1 0 | + | 0 0 1 | + */ + constexpr SkMatrix() : SkMatrix(1,0,0, 0,1,0, 0,0,1, kIdentity_Mask | kRectStaysRect_Mask) {} + + /** Sets SkMatrix to scale by (sx, sy). Returned matrix is: + + | sx 0 0 | + | 0 sy 0 | + | 0 0 1 | + + @param sx horizontal scale factor + @param sy vertical scale factor + @return SkMatrix with scale + */ + [[nodiscard]] static SkMatrix Scale(SkScalar sx, SkScalar sy) { + SkMatrix m; + m.setScale(sx, sy); + return m; + } + + /** Sets SkMatrix to translate by (dx, dy). Returned matrix is: + + | 1 0 dx | + | 0 1 dy | + | 0 0 1 | + + @param dx horizontal translation + @param dy vertical translation + @return SkMatrix with translation + */ + [[nodiscard]] static SkMatrix Translate(SkScalar dx, SkScalar dy) { + SkMatrix m; + m.setTranslate(dx, dy); + return m; + } + [[nodiscard]] static SkMatrix Translate(SkVector t) { return Translate(t.x(), t.y()); } + [[nodiscard]] static SkMatrix Translate(SkIVector t) { return Translate(t.x(), t.y()); } + + /** Sets SkMatrix to rotate by |deg| about a pivot point at (0, 0). + + @param deg rotation angle in degrees (positive rotates clockwise) + @return SkMatrix with rotation + */ + [[nodiscard]] static SkMatrix RotateDeg(SkScalar deg) { + SkMatrix m; + m.setRotate(deg); + return m; + } + [[nodiscard]] static SkMatrix RotateDeg(SkScalar deg, SkPoint pt) { + SkMatrix m; + m.setRotate(deg, pt.x(), pt.y()); + return m; + } + [[nodiscard]] static SkMatrix RotateRad(SkScalar rad) { + return RotateDeg(SkRadiansToDegrees(rad)); + } + + /** Sets SkMatrix to skew by (kx, ky) about pivot point (0, 0). + + @param kx horizontal skew factor + @param ky vertical skew factor + @return SkMatrix with skew + */ + [[nodiscard]] static SkMatrix Skew(SkScalar kx, SkScalar ky) { + SkMatrix m; + m.setSkew(kx, ky); + return m; + } + + /** \enum SkMatrix::ScaleToFit + ScaleToFit describes how SkMatrix is constructed to map one SkRect to another. + ScaleToFit may allow SkMatrix to have unequal horizontal and vertical scaling, + or may restrict SkMatrix to square scaling. If restricted, ScaleToFit specifies + how SkMatrix maps to the side or center of the destination SkRect. + */ + enum ScaleToFit { + kFill_ScaleToFit, //!< scales in x and y to fill destination SkRect + kStart_ScaleToFit, //!< scales and aligns to left and top + kCenter_ScaleToFit, //!< scales and aligns to center + kEnd_ScaleToFit, //!< scales and aligns to right and bottom + }; + + /** Returns SkMatrix set to scale and translate src to dst. ScaleToFit selects + whether mapping completely fills dst or preserves the aspect ratio, and how to + align src within dst. Returns the identity SkMatrix if src is empty. If dst is + empty, returns SkMatrix set to: + + | 0 0 0 | + | 0 0 0 | + | 0 0 1 | + + @param src SkRect to map from + @param dst SkRect to map to + @param mode How to handle the mapping + @return SkMatrix mapping src to dst + */ + [[nodiscard]] static SkMatrix RectToRect(const SkRect& src, const SkRect& dst, + ScaleToFit mode = kFill_ScaleToFit) { + return MakeRectToRect(src, dst, mode); + } + + /** Sets SkMatrix to: + + | scaleX skewX transX | + | skewY scaleY transY | + | pers0 pers1 pers2 | + + @param scaleX horizontal scale factor + @param skewX horizontal skew factor + @param transX horizontal translation + @param skewY vertical skew factor + @param scaleY vertical scale factor + @param transY vertical translation + @param pers0 input x-axis perspective factor + @param pers1 input y-axis perspective factor + @param pers2 perspective scale factor + @return SkMatrix constructed from parameters + */ + [[nodiscard]] static SkMatrix MakeAll(SkScalar scaleX, SkScalar skewX, SkScalar transX, + SkScalar skewY, SkScalar scaleY, SkScalar transY, + SkScalar pers0, SkScalar pers1, SkScalar pers2) { + SkMatrix m; + m.setAll(scaleX, skewX, transX, skewY, scaleY, transY, pers0, pers1, pers2); + return m; + } + + /** \enum SkMatrix::TypeMask + Enum of bit fields for mask returned by getType(). + Used to identify the complexity of SkMatrix, to optimize performance. + */ + enum TypeMask { + kIdentity_Mask = 0, //!< identity SkMatrix; all bits clear + kTranslate_Mask = 0x01, //!< translation SkMatrix + kScale_Mask = 0x02, //!< scale SkMatrix + kAffine_Mask = 0x04, //!< skew or rotate SkMatrix + kPerspective_Mask = 0x08, //!< perspective SkMatrix + }; + + /** Returns a bit field describing the transformations the matrix may + perform. The bit field is computed conservatively, so it may include + false positives. For example, when kPerspective_Mask is set, all + other bits are set. + + @return kIdentity_Mask, or combinations of: kTranslate_Mask, kScale_Mask, + kAffine_Mask, kPerspective_Mask + */ + TypeMask getType() const { + if (fTypeMask & kUnknown_Mask) { + fTypeMask = this->computeTypeMask(); + } + // only return the public masks + return (TypeMask)(fTypeMask & 0xF); + } + + /** Returns true if SkMatrix is identity. Identity matrix is: + + | 1 0 0 | + | 0 1 0 | + | 0 0 1 | + + @return true if SkMatrix has no effect + */ + bool isIdentity() const { + return this->getType() == 0; + } + + /** Returns true if SkMatrix at most scales and translates. SkMatrix may be identity, + contain only scale elements, only translate elements, or both. SkMatrix form is: + + | scale-x 0 translate-x | + | 0 scale-y translate-y | + | 0 0 1 | + + @return true if SkMatrix is identity; or scales, translates, or both + */ + bool isScaleTranslate() const { + return !(this->getType() & ~(kScale_Mask | kTranslate_Mask)); + } + + /** Returns true if SkMatrix is identity, or translates. SkMatrix form is: + + | 1 0 translate-x | + | 0 1 translate-y | + | 0 0 1 | + + @return true if SkMatrix is identity, or translates + */ + bool isTranslate() const { return !(this->getType() & ~(kTranslate_Mask)); } + + /** Returns true SkMatrix maps SkRect to another SkRect. If true, SkMatrix is identity, + or scales, or rotates a multiple of 90 degrees, or mirrors on axes. In all + cases, SkMatrix may also have translation. SkMatrix form is either: + + | scale-x 0 translate-x | + | 0 scale-y translate-y | + | 0 0 1 | + + or + + | 0 rotate-x translate-x | + | rotate-y 0 translate-y | + | 0 0 1 | + + for non-zero values of scale-x, scale-y, rotate-x, and rotate-y. + + Also called preservesAxisAlignment(); use the one that provides better inline + documentation. + + @return true if SkMatrix maps one SkRect into another + */ + bool rectStaysRect() const { + if (fTypeMask & kUnknown_Mask) { + fTypeMask = this->computeTypeMask(); + } + return (fTypeMask & kRectStaysRect_Mask) != 0; + } + + /** Returns true SkMatrix maps SkRect to another SkRect. If true, SkMatrix is identity, + or scales, or rotates a multiple of 90 degrees, or mirrors on axes. In all + cases, SkMatrix may also have translation. SkMatrix form is either: + + | scale-x 0 translate-x | + | 0 scale-y translate-y | + | 0 0 1 | + + or + + | 0 rotate-x translate-x | + | rotate-y 0 translate-y | + | 0 0 1 | + + for non-zero values of scale-x, scale-y, rotate-x, and rotate-y. + + Also called rectStaysRect(); use the one that provides better inline + documentation. + + @return true if SkMatrix maps one SkRect into another + */ + bool preservesAxisAlignment() const { return this->rectStaysRect(); } + + /** Returns true if the matrix contains perspective elements. SkMatrix form is: + + | -- -- -- | + | -- -- -- | + | perspective-x perspective-y perspective-scale | + + where perspective-x or perspective-y is non-zero, or perspective-scale is + not one. All other elements may have any value. + + @return true if SkMatrix is in most general form + */ + bool hasPerspective() const { + return SkToBool(this->getPerspectiveTypeMaskOnly() & + kPerspective_Mask); + } + + /** Returns true if SkMatrix contains only translation, rotation, reflection, and + uniform scale. + Returns false if SkMatrix contains different scales, skewing, perspective, or + degenerate forms that collapse to a line or point. + + Describes that the SkMatrix makes rendering with and without the matrix are + visually alike; a transformed circle remains a circle. Mathematically, this is + referred to as similarity of a Euclidean space, or a similarity transformation. + + Preserves right angles, keeping the arms of the angle equal lengths. + + @param tol to be deprecated + @return true if SkMatrix only rotates, uniformly scales, translates + + example: https://fiddle.skia.org/c/@Matrix_isSimilarity + */ + bool isSimilarity(SkScalar tol = SK_ScalarNearlyZero) const; + + /** Returns true if SkMatrix contains only translation, rotation, reflection, and + scale. Scale may differ along rotated axes. + Returns false if SkMatrix skewing, perspective, or degenerate forms that collapse + to a line or point. + + Preserves right angles, but not requiring that the arms of the angle + retain equal lengths. + + @param tol to be deprecated + @return true if SkMatrix only rotates, scales, translates + + example: https://fiddle.skia.org/c/@Matrix_preservesRightAngles + */ + bool preservesRightAngles(SkScalar tol = SK_ScalarNearlyZero) const; + + /** SkMatrix organizes its values in row-major order. These members correspond to + each value in SkMatrix. + */ + static constexpr int kMScaleX = 0; //!< horizontal scale factor + static constexpr int kMSkewX = 1; //!< horizontal skew factor + static constexpr int kMTransX = 2; //!< horizontal translation + static constexpr int kMSkewY = 3; //!< vertical skew factor + static constexpr int kMScaleY = 4; //!< vertical scale factor + static constexpr int kMTransY = 5; //!< vertical translation + static constexpr int kMPersp0 = 6; //!< input x perspective factor + static constexpr int kMPersp1 = 7; //!< input y perspective factor + static constexpr int kMPersp2 = 8; //!< perspective bias + + /** Affine arrays are in column-major order to match the matrix used by + PDF and XPS. + */ + static constexpr int kAScaleX = 0; //!< horizontal scale factor + static constexpr int kASkewY = 1; //!< vertical skew factor + static constexpr int kASkewX = 2; //!< horizontal skew factor + static constexpr int kAScaleY = 3; //!< vertical scale factor + static constexpr int kATransX = 4; //!< horizontal translation + static constexpr int kATransY = 5; //!< vertical translation + + /** Returns one matrix value. Asserts if index is out of range and SK_DEBUG is + defined. + + @param index one of: kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY, + kMPersp0, kMPersp1, kMPersp2 + @return value corresponding to index + */ + SkScalar operator[](int index) const { + SkASSERT((unsigned)index < 9); + return fMat[index]; + } + + /** Returns one matrix value. Asserts if index is out of range and SK_DEBUG is + defined. + + @param index one of: kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY, + kMPersp0, kMPersp1, kMPersp2 + @return value corresponding to index + */ + SkScalar get(int index) const { + SkASSERT((unsigned)index < 9); + return fMat[index]; + } + + /** Returns one matrix value from a particular row/column. Asserts if index is out + of range and SK_DEBUG is defined. + + @param r matrix row to fetch + @param c matrix column to fetch + @return value at the given matrix position + */ + SkScalar rc(int r, int c) const { + SkASSERT(r >= 0 && r <= 2); + SkASSERT(c >= 0 && c <= 2); + return fMat[r*3 + c]; + } + + /** Returns scale factor multiplied by x-axis input, contributing to x-axis output. + With mapPoints(), scales SkPoint along the x-axis. + + @return horizontal scale factor + */ + SkScalar getScaleX() const { return fMat[kMScaleX]; } + + /** Returns scale factor multiplied by y-axis input, contributing to y-axis output. + With mapPoints(), scales SkPoint along the y-axis. + + @return vertical scale factor + */ + SkScalar getScaleY() const { return fMat[kMScaleY]; } + + /** Returns scale factor multiplied by x-axis input, contributing to y-axis output. + With mapPoints(), skews SkPoint along the y-axis. + Skewing both axes can rotate SkPoint. + + @return vertical skew factor + */ + SkScalar getSkewY() const { return fMat[kMSkewY]; } + + /** Returns scale factor multiplied by y-axis input, contributing to x-axis output. + With mapPoints(), skews SkPoint along the x-axis. + Skewing both axes can rotate SkPoint. + + @return horizontal scale factor + */ + SkScalar getSkewX() const { return fMat[kMSkewX]; } + + /** Returns translation contributing to x-axis output. + With mapPoints(), moves SkPoint along the x-axis. + + @return horizontal translation factor + */ + SkScalar getTranslateX() const { return fMat[kMTransX]; } + + /** Returns translation contributing to y-axis output. + With mapPoints(), moves SkPoint along the y-axis. + + @return vertical translation factor + */ + SkScalar getTranslateY() const { return fMat[kMTransY]; } + + /** Returns factor scaling input x-axis relative to input y-axis. + + @return input x-axis perspective factor + */ + SkScalar getPerspX() const { return fMat[kMPersp0]; } + + /** Returns factor scaling input y-axis relative to input x-axis. + + @return input y-axis perspective factor + */ + SkScalar getPerspY() const { return fMat[kMPersp1]; } + + /** Returns writable SkMatrix value. Asserts if index is out of range and SK_DEBUG is + defined. Clears internal cache anticipating that caller will change SkMatrix value. + + Next call to read SkMatrix state may recompute cache; subsequent writes to SkMatrix + value must be followed by dirtyMatrixTypeCache(). + + @param index one of: kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY, + kMPersp0, kMPersp1, kMPersp2 + @return writable value corresponding to index + */ + SkScalar& operator[](int index) { + SkASSERT((unsigned)index < 9); + this->setTypeMask(kUnknown_Mask); + return fMat[index]; + } + + /** Sets SkMatrix value. Asserts if index is out of range and SK_DEBUG is + defined. Safer than operator[]; internal cache is always maintained. + + @param index one of: kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY, + kMPersp0, kMPersp1, kMPersp2 + @param value scalar to store in SkMatrix + */ + SkMatrix& set(int index, SkScalar value) { + SkASSERT((unsigned)index < 9); + fMat[index] = value; + this->setTypeMask(kUnknown_Mask); + return *this; + } + + /** Sets horizontal scale factor. + + @param v horizontal scale factor to store + */ + SkMatrix& setScaleX(SkScalar v) { return this->set(kMScaleX, v); } + + /** Sets vertical scale factor. + + @param v vertical scale factor to store + */ + SkMatrix& setScaleY(SkScalar v) { return this->set(kMScaleY, v); } + + /** Sets vertical skew factor. + + @param v vertical skew factor to store + */ + SkMatrix& setSkewY(SkScalar v) { return this->set(kMSkewY, v); } + + /** Sets horizontal skew factor. + + @param v horizontal skew factor to store + */ + SkMatrix& setSkewX(SkScalar v) { return this->set(kMSkewX, v); } + + /** Sets horizontal translation. + + @param v horizontal translation to store + */ + SkMatrix& setTranslateX(SkScalar v) { return this->set(kMTransX, v); } + + /** Sets vertical translation. + + @param v vertical translation to store + */ + SkMatrix& setTranslateY(SkScalar v) { return this->set(kMTransY, v); } + + /** Sets input x-axis perspective factor, which causes mapXY() to vary input x-axis values + inversely proportional to input y-axis values. + + @param v perspective factor + */ + SkMatrix& setPerspX(SkScalar v) { return this->set(kMPersp0, v); } + + /** Sets input y-axis perspective factor, which causes mapXY() to vary input y-axis values + inversely proportional to input x-axis values. + + @param v perspective factor + */ + SkMatrix& setPerspY(SkScalar v) { return this->set(kMPersp1, v); } + + /** Sets all values from parameters. Sets matrix to: + + | scaleX skewX transX | + | skewY scaleY transY | + | persp0 persp1 persp2 | + + @param scaleX horizontal scale factor to store + @param skewX horizontal skew factor to store + @param transX horizontal translation to store + @param skewY vertical skew factor to store + @param scaleY vertical scale factor to store + @param transY vertical translation to store + @param persp0 input x-axis values perspective factor to store + @param persp1 input y-axis values perspective factor to store + @param persp2 perspective scale factor to store + */ + SkMatrix& setAll(SkScalar scaleX, SkScalar skewX, SkScalar transX, + SkScalar skewY, SkScalar scaleY, SkScalar transY, + SkScalar persp0, SkScalar persp1, SkScalar persp2) { + fMat[kMScaleX] = scaleX; + fMat[kMSkewX] = skewX; + fMat[kMTransX] = transX; + fMat[kMSkewY] = skewY; + fMat[kMScaleY] = scaleY; + fMat[kMTransY] = transY; + fMat[kMPersp0] = persp0; + fMat[kMPersp1] = persp1; + fMat[kMPersp2] = persp2; + this->setTypeMask(kUnknown_Mask); + return *this; + } + + /** Copies nine scalar values contained by SkMatrix into buffer, in member value + ascending order: kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY, + kMPersp0, kMPersp1, kMPersp2. + + @param buffer storage for nine scalar values + */ + void get9(SkScalar buffer[9]) const { + memcpy(buffer, fMat, 9 * sizeof(SkScalar)); + } + + /** Sets SkMatrix to nine scalar values in buffer, in member value ascending order: + kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY, kMPersp0, kMPersp1, + kMPersp2. + + Sets matrix to: + + | buffer[0] buffer[1] buffer[2] | + | buffer[3] buffer[4] buffer[5] | + | buffer[6] buffer[7] buffer[8] | + + In the future, set9 followed by get9 may not return the same values. Since SkMatrix + maps non-homogeneous coordinates, scaling all nine values produces an equivalent + transformation, possibly improving precision. + + @param buffer nine scalar values + */ + SkMatrix& set9(const SkScalar buffer[9]); + + /** Sets SkMatrix to identity; which has no effect on mapped SkPoint. Sets SkMatrix to: + + | 1 0 0 | + | 0 1 0 | + | 0 0 1 | + + Also called setIdentity(); use the one that provides better inline + documentation. + */ + SkMatrix& reset(); + + /** Sets SkMatrix to identity; which has no effect on mapped SkPoint. Sets SkMatrix to: + + | 1 0 0 | + | 0 1 0 | + | 0 0 1 | + + Also called reset(); use the one that provides better inline + documentation. + */ + SkMatrix& setIdentity() { return this->reset(); } + + /** Sets SkMatrix to translate by (dx, dy). + + @param dx horizontal translation + @param dy vertical translation + */ + SkMatrix& setTranslate(SkScalar dx, SkScalar dy); + + /** Sets SkMatrix to translate by (v.fX, v.fY). + + @param v vector containing horizontal and vertical translation + */ + SkMatrix& setTranslate(const SkVector& v) { return this->setTranslate(v.fX, v.fY); } + + /** Sets SkMatrix to scale by sx and sy, about a pivot point at (px, py). + The pivot point is unchanged when mapped with SkMatrix. + + @param sx horizontal scale factor + @param sy vertical scale factor + @param px pivot on x-axis + @param py pivot on y-axis + */ + SkMatrix& setScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py); + + /** Sets SkMatrix to scale by sx and sy about at pivot point at (0, 0). + + @param sx horizontal scale factor + @param sy vertical scale factor + */ + SkMatrix& setScale(SkScalar sx, SkScalar sy); + + /** Sets SkMatrix to rotate by degrees about a pivot point at (px, py). + The pivot point is unchanged when mapped with SkMatrix. + + Positive degrees rotates clockwise. + + @param degrees angle of axes relative to upright axes + @param px pivot on x-axis + @param py pivot on y-axis + */ + SkMatrix& setRotate(SkScalar degrees, SkScalar px, SkScalar py); + + /** Sets SkMatrix to rotate by degrees about a pivot point at (0, 0). + Positive degrees rotates clockwise. + + @param degrees angle of axes relative to upright axes + */ + SkMatrix& setRotate(SkScalar degrees); + + /** Sets SkMatrix to rotate by sinValue and cosValue, about a pivot point at (px, py). + The pivot point is unchanged when mapped with SkMatrix. + + Vector (sinValue, cosValue) describes the angle of rotation relative to (0, 1). + Vector length specifies scale. + + @param sinValue rotation vector x-axis component + @param cosValue rotation vector y-axis component + @param px pivot on x-axis + @param py pivot on y-axis + */ + SkMatrix& setSinCos(SkScalar sinValue, SkScalar cosValue, + SkScalar px, SkScalar py); + + /** Sets SkMatrix to rotate by sinValue and cosValue, about a pivot point at (0, 0). + + Vector (sinValue, cosValue) describes the angle of rotation relative to (0, 1). + Vector length specifies scale. + + @param sinValue rotation vector x-axis component + @param cosValue rotation vector y-axis component + */ + SkMatrix& setSinCos(SkScalar sinValue, SkScalar cosValue); + + /** Sets SkMatrix to rotate, scale, and translate using a compressed matrix form. + + Vector (rsxForm.fSSin, rsxForm.fSCos) describes the angle of rotation relative + to (0, 1). Vector length specifies scale. Mapped point is rotated and scaled + by vector, then translated by (rsxForm.fTx, rsxForm.fTy). + + @param rsxForm compressed SkRSXform matrix + @return reference to SkMatrix + + example: https://fiddle.skia.org/c/@Matrix_setRSXform + */ + SkMatrix& setRSXform(const SkRSXform& rsxForm); + + /** Sets SkMatrix to skew by kx and ky, about a pivot point at (px, py). + The pivot point is unchanged when mapped with SkMatrix. + + @param kx horizontal skew factor + @param ky vertical skew factor + @param px pivot on x-axis + @param py pivot on y-axis + */ + SkMatrix& setSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py); + + /** Sets SkMatrix to skew by kx and ky, about a pivot point at (0, 0). + + @param kx horizontal skew factor + @param ky vertical skew factor + */ + SkMatrix& setSkew(SkScalar kx, SkScalar ky); + + /** Sets SkMatrix to SkMatrix a multiplied by SkMatrix b. Either a or b may be this. + + Given: + + | A B C | | J K L | + a = | D E F |, b = | M N O | + | G H I | | P Q R | + + sets SkMatrix to: + + | A B C | | J K L | | AJ+BM+CP AK+BN+CQ AL+BO+CR | + a * b = | D E F | * | M N O | = | DJ+EM+FP DK+EN+FQ DL+EO+FR | + | G H I | | P Q R | | GJ+HM+IP GK+HN+IQ GL+HO+IR | + + @param a SkMatrix on left side of multiply expression + @param b SkMatrix on right side of multiply expression + */ + SkMatrix& setConcat(const SkMatrix& a, const SkMatrix& b); + + /** Sets SkMatrix to SkMatrix multiplied by SkMatrix constructed from translation (dx, dy). + This can be thought of as moving the point to be mapped before applying SkMatrix. + + Given: + + | A B C | | 1 0 dx | + Matrix = | D E F |, T(dx, dy) = | 0 1 dy | + | G H I | | 0 0 1 | + + sets SkMatrix to: + + | A B C | | 1 0 dx | | A B A*dx+B*dy+C | + Matrix * T(dx, dy) = | D E F | | 0 1 dy | = | D E D*dx+E*dy+F | + | G H I | | 0 0 1 | | G H G*dx+H*dy+I | + + @param dx x-axis translation before applying SkMatrix + @param dy y-axis translation before applying SkMatrix + */ + SkMatrix& preTranslate(SkScalar dx, SkScalar dy); + + /** Sets SkMatrix to SkMatrix multiplied by SkMatrix constructed from scaling by (sx, sy) + about pivot point (px, py). + This can be thought of as scaling about a pivot point before applying SkMatrix. + + Given: + + | A B C | | sx 0 dx | + Matrix = | D E F |, S(sx, sy, px, py) = | 0 sy dy | + | G H I | | 0 0 1 | + + where + + dx = px - sx * px + dy = py - sy * py + + sets SkMatrix to: + + | A B C | | sx 0 dx | | A*sx B*sy A*dx+B*dy+C | + Matrix * S(sx, sy, px, py) = | D E F | | 0 sy dy | = | D*sx E*sy D*dx+E*dy+F | + | G H I | | 0 0 1 | | G*sx H*sy G*dx+H*dy+I | + + @param sx horizontal scale factor + @param sy vertical scale factor + @param px pivot on x-axis + @param py pivot on y-axis + */ + SkMatrix& preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py); + + /** Sets SkMatrix to SkMatrix multiplied by SkMatrix constructed from scaling by (sx, sy) + about pivot point (0, 0). + This can be thought of as scaling about the origin before applying SkMatrix. + + Given: + + | A B C | | sx 0 0 | + Matrix = | D E F |, S(sx, sy) = | 0 sy 0 | + | G H I | | 0 0 1 | + + sets SkMatrix to: + + | A B C | | sx 0 0 | | A*sx B*sy C | + Matrix * S(sx, sy) = | D E F | | 0 sy 0 | = | D*sx E*sy F | + | G H I | | 0 0 1 | | G*sx H*sy I | + + @param sx horizontal scale factor + @param sy vertical scale factor + */ + SkMatrix& preScale(SkScalar sx, SkScalar sy); + + /** Sets SkMatrix to SkMatrix multiplied by SkMatrix constructed from rotating by degrees + about pivot point (px, py). + This can be thought of as rotating about a pivot point before applying SkMatrix. + + Positive degrees rotates clockwise. + + Given: + + | A B C | | c -s dx | + Matrix = | D E F |, R(degrees, px, py) = | s c dy | + | G H I | | 0 0 1 | + + where + + c = cos(degrees) + s = sin(degrees) + dx = s * py + (1 - c) * px + dy = -s * px + (1 - c) * py + + sets SkMatrix to: + + | A B C | | c -s dx | | Ac+Bs -As+Bc A*dx+B*dy+C | + Matrix * R(degrees, px, py) = | D E F | | s c dy | = | Dc+Es -Ds+Ec D*dx+E*dy+F | + | G H I | | 0 0 1 | | Gc+Hs -Gs+Hc G*dx+H*dy+I | + + @param degrees angle of axes relative to upright axes + @param px pivot on x-axis + @param py pivot on y-axis + */ + SkMatrix& preRotate(SkScalar degrees, SkScalar px, SkScalar py); + + /** Sets SkMatrix to SkMatrix multiplied by SkMatrix constructed from rotating by degrees + about pivot point (0, 0). + This can be thought of as rotating about the origin before applying SkMatrix. + + Positive degrees rotates clockwise. + + Given: + + | A B C | | c -s 0 | + Matrix = | D E F |, R(degrees, px, py) = | s c 0 | + | G H I | | 0 0 1 | + + where + + c = cos(degrees) + s = sin(degrees) + + sets SkMatrix to: + + | A B C | | c -s 0 | | Ac+Bs -As+Bc C | + Matrix * R(degrees, px, py) = | D E F | | s c 0 | = | Dc+Es -Ds+Ec F | + | G H I | | 0 0 1 | | Gc+Hs -Gs+Hc I | + + @param degrees angle of axes relative to upright axes + */ + SkMatrix& preRotate(SkScalar degrees); + + /** Sets SkMatrix to SkMatrix multiplied by SkMatrix constructed from skewing by (kx, ky) + about pivot point (px, py). + This can be thought of as skewing about a pivot point before applying SkMatrix. + + Given: + + | A B C | | 1 kx dx | + Matrix = | D E F |, K(kx, ky, px, py) = | ky 1 dy | + | G H I | | 0 0 1 | + + where + + dx = -kx * py + dy = -ky * px + + sets SkMatrix to: + + | A B C | | 1 kx dx | | A+B*ky A*kx+B A*dx+B*dy+C | + Matrix * K(kx, ky, px, py) = | D E F | | ky 1 dy | = | D+E*ky D*kx+E D*dx+E*dy+F | + | G H I | | 0 0 1 | | G+H*ky G*kx+H G*dx+H*dy+I | + + @param kx horizontal skew factor + @param ky vertical skew factor + @param px pivot on x-axis + @param py pivot on y-axis + */ + SkMatrix& preSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py); + + /** Sets SkMatrix to SkMatrix multiplied by SkMatrix constructed from skewing by (kx, ky) + about pivot point (0, 0). + This can be thought of as skewing about the origin before applying SkMatrix. + + Given: + + | A B C | | 1 kx 0 | + Matrix = | D E F |, K(kx, ky) = | ky 1 0 | + | G H I | | 0 0 1 | + + sets SkMatrix to: + + | A B C | | 1 kx 0 | | A+B*ky A*kx+B C | + Matrix * K(kx, ky) = | D E F | | ky 1 0 | = | D+E*ky D*kx+E F | + | G H I | | 0 0 1 | | G+H*ky G*kx+H I | + + @param kx horizontal skew factor + @param ky vertical skew factor + */ + SkMatrix& preSkew(SkScalar kx, SkScalar ky); + + /** Sets SkMatrix to SkMatrix multiplied by SkMatrix other. + This can be thought of mapping by other before applying SkMatrix. + + Given: + + | A B C | | J K L | + Matrix = | D E F |, other = | M N O | + | G H I | | P Q R | + + sets SkMatrix to: + + | A B C | | J K L | | AJ+BM+CP AK+BN+CQ AL+BO+CR | + Matrix * other = | D E F | * | M N O | = | DJ+EM+FP DK+EN+FQ DL+EO+FR | + | G H I | | P Q R | | GJ+HM+IP GK+HN+IQ GL+HO+IR | + + @param other SkMatrix on right side of multiply expression + */ + SkMatrix& preConcat(const SkMatrix& other); + + /** Sets SkMatrix to SkMatrix constructed from translation (dx, dy) multiplied by SkMatrix. + This can be thought of as moving the point to be mapped after applying SkMatrix. + + Given: + + | J K L | | 1 0 dx | + Matrix = | M N O |, T(dx, dy) = | 0 1 dy | + | P Q R | | 0 0 1 | + + sets SkMatrix to: + + | 1 0 dx | | J K L | | J+dx*P K+dx*Q L+dx*R | + T(dx, dy) * Matrix = | 0 1 dy | | M N O | = | M+dy*P N+dy*Q O+dy*R | + | 0 0 1 | | P Q R | | P Q R | + + @param dx x-axis translation after applying SkMatrix + @param dy y-axis translation after applying SkMatrix + */ + SkMatrix& postTranslate(SkScalar dx, SkScalar dy); + + /** Sets SkMatrix to SkMatrix constructed from scaling by (sx, sy) about pivot point + (px, py), multiplied by SkMatrix. + This can be thought of as scaling about a pivot point after applying SkMatrix. + + Given: + + | J K L | | sx 0 dx | + Matrix = | M N O |, S(sx, sy, px, py) = | 0 sy dy | + | P Q R | | 0 0 1 | + + where + + dx = px - sx * px + dy = py - sy * py + + sets SkMatrix to: + + | sx 0 dx | | J K L | | sx*J+dx*P sx*K+dx*Q sx*L+dx+R | + S(sx, sy, px, py) * Matrix = | 0 sy dy | | M N O | = | sy*M+dy*P sy*N+dy*Q sy*O+dy*R | + | 0 0 1 | | P Q R | | P Q R | + + @param sx horizontal scale factor + @param sy vertical scale factor + @param px pivot on x-axis + @param py pivot on y-axis + */ + SkMatrix& postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py); + + /** Sets SkMatrix to SkMatrix constructed from scaling by (sx, sy) about pivot point + (0, 0), multiplied by SkMatrix. + This can be thought of as scaling about the origin after applying SkMatrix. + + Given: + + | J K L | | sx 0 0 | + Matrix = | M N O |, S(sx, sy) = | 0 sy 0 | + | P Q R | | 0 0 1 | + + sets SkMatrix to: + + | sx 0 0 | | J K L | | sx*J sx*K sx*L | + S(sx, sy) * Matrix = | 0 sy 0 | | M N O | = | sy*M sy*N sy*O | + | 0 0 1 | | P Q R | | P Q R | + + @param sx horizontal scale factor + @param sy vertical scale factor + */ + SkMatrix& postScale(SkScalar sx, SkScalar sy); + + /** Sets SkMatrix to SkMatrix constructed from rotating by degrees about pivot point + (px, py), multiplied by SkMatrix. + This can be thought of as rotating about a pivot point after applying SkMatrix. + + Positive degrees rotates clockwise. + + Given: + + | J K L | | c -s dx | + Matrix = | M N O |, R(degrees, px, py) = | s c dy | + | P Q R | | 0 0 1 | + + where + + c = cos(degrees) + s = sin(degrees) + dx = s * py + (1 - c) * px + dy = -s * px + (1 - c) * py + + sets SkMatrix to: + + |c -s dx| |J K L| |cJ-sM+dx*P cK-sN+dx*Q cL-sO+dx+R| + R(degrees, px, py) * Matrix = |s c dy| |M N O| = |sJ+cM+dy*P sK+cN+dy*Q sL+cO+dy*R| + |0 0 1| |P Q R| | P Q R| + + @param degrees angle of axes relative to upright axes + @param px pivot on x-axis + @param py pivot on y-axis + */ + SkMatrix& postRotate(SkScalar degrees, SkScalar px, SkScalar py); + + /** Sets SkMatrix to SkMatrix constructed from rotating by degrees about pivot point + (0, 0), multiplied by SkMatrix. + This can be thought of as rotating about the origin after applying SkMatrix. + + Positive degrees rotates clockwise. + + Given: + + | J K L | | c -s 0 | + Matrix = | M N O |, R(degrees, px, py) = | s c 0 | + | P Q R | | 0 0 1 | + + where + + c = cos(degrees) + s = sin(degrees) + + sets SkMatrix to: + + | c -s dx | | J K L | | cJ-sM cK-sN cL-sO | + R(degrees, px, py) * Matrix = | s c dy | | M N O | = | sJ+cM sK+cN sL+cO | + | 0 0 1 | | P Q R | | P Q R | + + @param degrees angle of axes relative to upright axes + */ + SkMatrix& postRotate(SkScalar degrees); + + /** Sets SkMatrix to SkMatrix constructed from skewing by (kx, ky) about pivot point + (px, py), multiplied by SkMatrix. + This can be thought of as skewing about a pivot point after applying SkMatrix. + + Given: + + | J K L | | 1 kx dx | + Matrix = | M N O |, K(kx, ky, px, py) = | ky 1 dy | + | P Q R | | 0 0 1 | + + where + + dx = -kx * py + dy = -ky * px + + sets SkMatrix to: + + | 1 kx dx| |J K L| |J+kx*M+dx*P K+kx*N+dx*Q L+kx*O+dx+R| + K(kx, ky, px, py) * Matrix = |ky 1 dy| |M N O| = |ky*J+M+dy*P ky*K+N+dy*Q ky*L+O+dy*R| + | 0 0 1| |P Q R| | P Q R| + + @param kx horizontal skew factor + @param ky vertical skew factor + @param px pivot on x-axis + @param py pivot on y-axis + */ + SkMatrix& postSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py); + + /** Sets SkMatrix to SkMatrix constructed from skewing by (kx, ky) about pivot point + (0, 0), multiplied by SkMatrix. + This can be thought of as skewing about the origin after applying SkMatrix. + + Given: + + | J K L | | 1 kx 0 | + Matrix = | M N O |, K(kx, ky) = | ky 1 0 | + | P Q R | | 0 0 1 | + + sets SkMatrix to: + + | 1 kx 0 | | J K L | | J+kx*M K+kx*N L+kx*O | + K(kx, ky) * Matrix = | ky 1 0 | | M N O | = | ky*J+M ky*K+N ky*L+O | + | 0 0 1 | | P Q R | | P Q R | + + @param kx horizontal skew factor + @param ky vertical skew factor + */ + SkMatrix& postSkew(SkScalar kx, SkScalar ky); + + /** Sets SkMatrix to SkMatrix other multiplied by SkMatrix. + This can be thought of mapping by other after applying SkMatrix. + + Given: + + | J K L | | A B C | + Matrix = | M N O |, other = | D E F | + | P Q R | | G H I | + + sets SkMatrix to: + + | A B C | | J K L | | AJ+BM+CP AK+BN+CQ AL+BO+CR | + other * Matrix = | D E F | * | M N O | = | DJ+EM+FP DK+EN+FQ DL+EO+FR | + | G H I | | P Q R | | GJ+HM+IP GK+HN+IQ GL+HO+IR | + + @param other SkMatrix on left side of multiply expression + */ + SkMatrix& postConcat(const SkMatrix& other); + +#ifndef SK_SUPPORT_LEGACY_MATRIX_RECTTORECT +private: +#endif + /** Sets SkMatrix to scale and translate src SkRect to dst SkRect. stf selects whether + mapping completely fills dst or preserves the aspect ratio, and how to align + src within dst. Returns false if src is empty, and sets SkMatrix to identity. + Returns true if dst is empty, and sets SkMatrix to: + + | 0 0 0 | + | 0 0 0 | + | 0 0 1 | + + @param src SkRect to map from + @param dst SkRect to map to + @return true if SkMatrix can represent SkRect mapping + + example: https://fiddle.skia.org/c/@Matrix_setRectToRect + */ + bool setRectToRect(const SkRect& src, const SkRect& dst, ScaleToFit stf); + + /** Returns SkMatrix set to scale and translate src SkRect to dst SkRect. stf selects + whether mapping completely fills dst or preserves the aspect ratio, and how to + align src within dst. Returns the identity SkMatrix if src is empty. If dst is + empty, returns SkMatrix set to: + + | 0 0 0 | + | 0 0 0 | + | 0 0 1 | + + @param src SkRect to map from + @param dst SkRect to map to + @return SkMatrix mapping src to dst + */ + static SkMatrix MakeRectToRect(const SkRect& src, const SkRect& dst, ScaleToFit stf) { + SkMatrix m; + m.setRectToRect(src, dst, stf); + return m; + } +#ifndef SK_SUPPORT_LEGACY_MATRIX_RECTTORECT +public: +#endif + + /** Sets SkMatrix to map src to dst. count must be zero or greater, and four or less. + + If count is zero, sets SkMatrix to identity and returns true. + If count is one, sets SkMatrix to translate and returns true. + If count is two or more, sets SkMatrix to map SkPoint if possible; returns false + if SkMatrix cannot be constructed. If count is four, SkMatrix may include + perspective. + + @param src SkPoint to map from + @param dst SkPoint to map to + @param count number of SkPoint in src and dst + @return true if SkMatrix was constructed successfully + + example: https://fiddle.skia.org/c/@Matrix_setPolyToPoly + */ + bool setPolyToPoly(const SkPoint src[], const SkPoint dst[], int count); + + /** Sets inverse to reciprocal matrix, returning true if SkMatrix can be inverted. + Geometrically, if SkMatrix maps from source to destination, inverse SkMatrix + maps from destination to source. If SkMatrix can not be inverted, inverse is + unchanged. + + @param inverse storage for inverted SkMatrix; may be nullptr + @return true if SkMatrix can be inverted + */ + [[nodiscard]] bool invert(SkMatrix* inverse) const { + // Allow the trivial case to be inlined. + if (this->isIdentity()) { + if (inverse) { + inverse->reset(); + } + return true; + } + return this->invertNonIdentity(inverse); + } + + /** Fills affine with identity values in column major order. + Sets affine to: + + | 1 0 0 | + | 0 1 0 | + + Affine 3 by 2 matrices in column major order are used by OpenGL and XPS. + + @param affine storage for 3 by 2 affine matrix + + example: https://fiddle.skia.org/c/@Matrix_SetAffineIdentity + */ + static void SetAffineIdentity(SkScalar affine[6]); + + /** Fills affine in column major order. Sets affine to: + + | scale-x skew-x translate-x | + | skew-y scale-y translate-y | + + If SkMatrix contains perspective, returns false and leaves affine unchanged. + + @param affine storage for 3 by 2 affine matrix; may be nullptr + @return true if SkMatrix does not contain perspective + */ + [[nodiscard]] bool asAffine(SkScalar affine[6]) const; + + /** Sets SkMatrix to affine values, passed in column major order. Given affine, + column, then row, as: + + | scale-x skew-x translate-x | + | skew-y scale-y translate-y | + + SkMatrix is set, row, then column, to: + + | scale-x skew-x translate-x | + | skew-y scale-y translate-y | + | 0 0 1 | + + @param affine 3 by 2 affine matrix + */ + SkMatrix& setAffine(const SkScalar affine[6]); + + /** + * A matrix is categorized as 'perspective' if the bottom row is not [0, 0, 1]. + * However, for most uses (e.g. mapPoints) a bottom row of [0, 0, X] behaves like a + * non-perspective matrix, though it will be categorized as perspective. Calling + * normalizePerspective() will change the matrix such that, if its bottom row was [0, 0, X], + * it will be changed to [0, 0, 1] by scaling the rest of the matrix by 1/X. + * + * | A B C | | A/X B/X C/X | + * | D E F | -> | D/X E/X F/X | for X != 0 + * | 0 0 X | | 0 0 1 | + */ + void normalizePerspective() { + if (fMat[8] != 1) { + this->doNormalizePerspective(); + } + } + + /** Maps src SkPoint array of length count to dst SkPoint array of equal or greater + length. SkPoint are mapped by multiplying each SkPoint by SkMatrix. Given: + + | A B C | | x | + Matrix = | D E F |, pt = | y | + | G H I | | 1 | + + where + + for (i = 0; i < count; ++i) { + x = src[i].fX + y = src[i].fY + } + + each dst SkPoint is computed as: + + |A B C| |x| Ax+By+C Dx+Ey+F + Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ------- , ------- + |G H I| |1| Gx+Hy+I Gx+Hy+I + + src and dst may point to the same storage. + + @param dst storage for mapped SkPoint + @param src SkPoint to transform + @param count number of SkPoint to transform + + example: https://fiddle.skia.org/c/@Matrix_mapPoints + */ + void mapPoints(SkPoint dst[], const SkPoint src[], int count) const; + + /** Maps pts SkPoint array of length count in place. SkPoint are mapped by multiplying + each SkPoint by SkMatrix. Given: + + | A B C | | x | + Matrix = | D E F |, pt = | y | + | G H I | | 1 | + + where + + for (i = 0; i < count; ++i) { + x = pts[i].fX + y = pts[i].fY + } + + each resulting pts SkPoint is computed as: + + |A B C| |x| Ax+By+C Dx+Ey+F + Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ------- , ------- + |G H I| |1| Gx+Hy+I Gx+Hy+I + + @param pts storage for mapped SkPoint + @param count number of SkPoint to transform + */ + void mapPoints(SkPoint pts[], int count) const { + this->mapPoints(pts, pts, count); + } + + /** Maps src SkPoint3 array of length count to dst SkPoint3 array, which must of length count or + greater. SkPoint3 array is mapped by multiplying each SkPoint3 by SkMatrix. Given: + + | A B C | | x | + Matrix = | D E F |, src = | y | + | G H I | | z | + + each resulting dst SkPoint is computed as: + + |A B C| |x| + Matrix * src = |D E F| |y| = |Ax+By+Cz Dx+Ey+Fz Gx+Hy+Iz| + |G H I| |z| + + @param dst storage for mapped SkPoint3 array + @param src SkPoint3 array to transform + @param count items in SkPoint3 array to transform + + example: https://fiddle.skia.org/c/@Matrix_mapHomogeneousPoints + */ + void mapHomogeneousPoints(SkPoint3 dst[], const SkPoint3 src[], int count) const; + + /** + * Returns homogeneous points, starting with 2D src points (with implied w = 1). + */ + void mapHomogeneousPoints(SkPoint3 dst[], const SkPoint src[], int count) const; + + /** Returns SkPoint pt multiplied by SkMatrix. Given: + + | A B C | | x | + Matrix = | D E F |, pt = | y | + | G H I | | 1 | + + result is computed as: + + |A B C| |x| Ax+By+C Dx+Ey+F + Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ------- , ------- + |G H I| |1| Gx+Hy+I Gx+Hy+I + + @param p SkPoint to map + @return mapped SkPoint + */ + SkPoint mapPoint(SkPoint pt) const { + SkPoint result; + this->mapXY(pt.x(), pt.y(), &result); + return result; + } + + /** Maps SkPoint (x, y) to result. SkPoint is mapped by multiplying by SkMatrix. Given: + + | A B C | | x | + Matrix = | D E F |, pt = | y | + | G H I | | 1 | + + result is computed as: + + |A B C| |x| Ax+By+C Dx+Ey+F + Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ------- , ------- + |G H I| |1| Gx+Hy+I Gx+Hy+I + + @param x x-axis value of SkPoint to map + @param y y-axis value of SkPoint to map + @param result storage for mapped SkPoint + + example: https://fiddle.skia.org/c/@Matrix_mapXY + */ + void mapXY(SkScalar x, SkScalar y, SkPoint* result) const; + + /** Returns SkPoint (x, y) multiplied by SkMatrix. Given: + + | A B C | | x | + Matrix = | D E F |, pt = | y | + | G H I | | 1 | + + result is computed as: + + |A B C| |x| Ax+By+C Dx+Ey+F + Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ------- , ------- + |G H I| |1| Gx+Hy+I Gx+Hy+I + + @param x x-axis value of SkPoint to map + @param y y-axis value of SkPoint to map + @return mapped SkPoint + */ + SkPoint mapXY(SkScalar x, SkScalar y) const { + SkPoint result; + this->mapXY(x,y, &result); + return result; + } + + + /** Returns (0, 0) multiplied by SkMatrix. Given: + + | A B C | | 0 | + Matrix = | D E F |, pt = | 0 | + | G H I | | 1 | + + result is computed as: + + |A B C| |0| C F + Matrix * pt = |D E F| |0| = |C F I| = - , - + |G H I| |1| I I + + @return mapped (0, 0) + */ + SkPoint mapOrigin() const { + SkScalar x = this->getTranslateX(), + y = this->getTranslateY(); + if (this->hasPerspective()) { + SkScalar w = fMat[kMPersp2]; + if (w) { w = 1 / w; } + x *= w; + y *= w; + } + return {x, y}; + } + + /** Maps src vector array of length count to vector SkPoint array of equal or greater + length. Vectors are mapped by multiplying each vector by SkMatrix, treating + SkMatrix translation as zero. Given: + + | A B 0 | | x | + Matrix = | D E 0 |, src = | y | + | G H I | | 1 | + + where + + for (i = 0; i < count; ++i) { + x = src[i].fX + y = src[i].fY + } + + each dst vector is computed as: + + |A B 0| |x| Ax+By Dx+Ey + Matrix * src = |D E 0| |y| = |Ax+By Dx+Ey Gx+Hy+I| = ------- , ------- + |G H I| |1| Gx+Hy+I Gx+Hy+I + + src and dst may point to the same storage. + + @param dst storage for mapped vectors + @param src vectors to transform + @param count number of vectors to transform + + example: https://fiddle.skia.org/c/@Matrix_mapVectors + */ + void mapVectors(SkVector dst[], const SkVector src[], int count) const; + + /** Maps vecs vector array of length count in place, multiplying each vector by + SkMatrix, treating SkMatrix translation as zero. Given: + + | A B 0 | | x | + Matrix = | D E 0 |, vec = | y | + | G H I | | 1 | + + where + + for (i = 0; i < count; ++i) { + x = vecs[i].fX + y = vecs[i].fY + } + + each result vector is computed as: + + |A B 0| |x| Ax+By Dx+Ey + Matrix * vec = |D E 0| |y| = |Ax+By Dx+Ey Gx+Hy+I| = ------- , ------- + |G H I| |1| Gx+Hy+I Gx+Hy+I + + @param vecs vectors to transform, and storage for mapped vectors + @param count number of vectors to transform + */ + void mapVectors(SkVector vecs[], int count) const { + this->mapVectors(vecs, vecs, count); + } + + /** Maps vector (dx, dy) to result. Vector is mapped by multiplying by SkMatrix, + treating SkMatrix translation as zero. Given: + + | A B 0 | | dx | + Matrix = | D E 0 |, vec = | dy | + | G H I | | 1 | + + each result vector is computed as: + + |A B 0| |dx| A*dx+B*dy D*dx+E*dy + Matrix * vec = |D E 0| |dy| = |A*dx+B*dy D*dx+E*dy G*dx+H*dy+I| = ----------- , ----------- + |G H I| | 1| G*dx+H*dy+I G*dx+*dHy+I + + @param dx x-axis value of vector to map + @param dy y-axis value of vector to map + @param result storage for mapped vector + */ + void mapVector(SkScalar dx, SkScalar dy, SkVector* result) const { + SkVector vec = { dx, dy }; + this->mapVectors(result, &vec, 1); + } + + /** Returns vector (dx, dy) multiplied by SkMatrix, treating SkMatrix translation as zero. + Given: + + | A B 0 | | dx | + Matrix = | D E 0 |, vec = | dy | + | G H I | | 1 | + + each result vector is computed as: + + |A B 0| |dx| A*dx+B*dy D*dx+E*dy + Matrix * vec = |D E 0| |dy| = |A*dx+B*dy D*dx+E*dy G*dx+H*dy+I| = ----------- , ----------- + |G H I| | 1| G*dx+H*dy+I G*dx+*dHy+I + + @param dx x-axis value of vector to map + @param dy y-axis value of vector to map + @return mapped vector + */ + SkVector mapVector(SkScalar dx, SkScalar dy) const { + SkVector vec = { dx, dy }; + this->mapVectors(&vec, &vec, 1); + return vec; + } + + /** Sets dst to bounds of src corners mapped by SkMatrix. + Returns true if mapped corners are dst corners. + + Returned value is the same as calling rectStaysRect(). + + @param dst storage for bounds of mapped SkPoint + @param src SkRect to map + @param pc whether to apply perspective clipping + @return true if dst is equivalent to mapped src + + example: https://fiddle.skia.org/c/@Matrix_mapRect + */ + bool mapRect(SkRect* dst, const SkRect& src, + SkApplyPerspectiveClip pc = SkApplyPerspectiveClip::kYes) const; + + /** Sets rect to bounds of rect corners mapped by SkMatrix. + Returns true if mapped corners are computed rect corners. + + Returned value is the same as calling rectStaysRect(). + + @param rect rectangle to map, and storage for bounds of mapped corners + @param pc whether to apply perspective clipping + @return true if result is equivalent to mapped rect + */ + bool mapRect(SkRect* rect, SkApplyPerspectiveClip pc = SkApplyPerspectiveClip::kYes) const { + return this->mapRect(rect, *rect, pc); + } + + /** Returns bounds of src corners mapped by SkMatrix. + + @param src rectangle to map + @return mapped bounds + */ + SkRect mapRect(const SkRect& src, + SkApplyPerspectiveClip pc = SkApplyPerspectiveClip::kYes) const { + SkRect dst; + (void)this->mapRect(&dst, src, pc); + return dst; + } + + /** Maps four corners of rect to dst. SkPoint are mapped by multiplying each + rect corner by SkMatrix. rect corner is processed in this order: + (rect.fLeft, rect.fTop), (rect.fRight, rect.fTop), (rect.fRight, rect.fBottom), + (rect.fLeft, rect.fBottom). + + rect may be empty: rect.fLeft may be greater than or equal to rect.fRight; + rect.fTop may be greater than or equal to rect.fBottom. + + Given: + + | A B C | | x | + Matrix = | D E F |, pt = | y | + | G H I | | 1 | + + where pt is initialized from each of (rect.fLeft, rect.fTop), + (rect.fRight, rect.fTop), (rect.fRight, rect.fBottom), (rect.fLeft, rect.fBottom), + each dst SkPoint is computed as: + + |A B C| |x| Ax+By+C Dx+Ey+F + Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ------- , ------- + |G H I| |1| Gx+Hy+I Gx+Hy+I + + @param dst storage for mapped corner SkPoint + @param rect SkRect to map + + Note: this does not perform perspective clipping (as that might result in more than + 4 points, so results are suspect if the matrix contains perspective. + */ + void mapRectToQuad(SkPoint dst[4], const SkRect& rect) const { + // This could potentially be faster if we only transformed each x and y of the rect once. + rect.toQuad(dst); + this->mapPoints(dst, 4); + } + + /** Sets dst to bounds of src corners mapped by SkMatrix. If matrix contains + elements other than scale or translate: asserts if SK_DEBUG is defined; + otherwise, results are undefined. + + @param dst storage for bounds of mapped SkPoint + @param src SkRect to map + + example: https://fiddle.skia.org/c/@Matrix_mapRectScaleTranslate + */ + void mapRectScaleTranslate(SkRect* dst, const SkRect& src) const; + + /** Returns geometric mean radius of ellipse formed by constructing circle of + size radius, and mapping constructed circle with SkMatrix. The result squared is + equal to the major axis length times the minor axis length. + Result is not meaningful if SkMatrix contains perspective elements. + + @param radius circle size to map + @return average mapped radius + + example: https://fiddle.skia.org/c/@Matrix_mapRadius + */ + SkScalar mapRadius(SkScalar radius) const; + + /** Compares a and b; returns true if a and b are numerically equal. Returns true + even if sign of zero values are different. Returns false if either SkMatrix + contains NaN, even if the other SkMatrix also contains NaN. + + @param a SkMatrix to compare + @param b SkMatrix to compare + @return true if SkMatrix a and SkMatrix b are numerically equal + */ + friend SK_API bool operator==(const SkMatrix& a, const SkMatrix& b); + + /** Compares a and b; returns true if a and b are not numerically equal. Returns false + even if sign of zero values are different. Returns true if either SkMatrix + contains NaN, even if the other SkMatrix also contains NaN. + + @param a SkMatrix to compare + @param b SkMatrix to compare + @return true if SkMatrix a and SkMatrix b are numerically not equal + */ + friend SK_API bool operator!=(const SkMatrix& a, const SkMatrix& b) { + return !(a == b); + } + + /** Writes text representation of SkMatrix to standard output. Floating point values + are written with limited precision; it may not be possible to reconstruct + original SkMatrix from output. + + example: https://fiddle.skia.org/c/@Matrix_dump + */ + void dump() const; + + /** Returns the minimum scaling factor of SkMatrix by decomposing the scaling and + skewing elements. + Returns -1 if scale factor overflows or SkMatrix contains perspective. + + @return minimum scale factor + + example: https://fiddle.skia.org/c/@Matrix_getMinScale + */ + SkScalar getMinScale() const; + + /** Returns the maximum scaling factor of SkMatrix by decomposing the scaling and + skewing elements. + Returns -1 if scale factor overflows or SkMatrix contains perspective. + + @return maximum scale factor + + example: https://fiddle.skia.org/c/@Matrix_getMaxScale + */ + SkScalar getMaxScale() const; + + /** Sets scaleFactors[0] to the minimum scaling factor, and scaleFactors[1] to the + maximum scaling factor. Scaling factors are computed by decomposing + the SkMatrix scaling and skewing elements. + + Returns true if scaleFactors are found; otherwise, returns false and sets + scaleFactors to undefined values. + + @param scaleFactors storage for minimum and maximum scale factors + @return true if scale factors were computed correctly + */ + [[nodiscard]] bool getMinMaxScales(SkScalar scaleFactors[2]) const; + + /** Decomposes SkMatrix into scale components and whatever remains. Returns false if + SkMatrix could not be decomposed. + + Sets scale to portion of SkMatrix that scale axes. Sets remaining to SkMatrix + with scaling factored out. remaining may be passed as nullptr + to determine if SkMatrix can be decomposed without computing remainder. + + Returns true if scale components are found. scale and remaining are + unchanged if SkMatrix contains perspective; scale factors are not finite, or + are nearly zero. + + On success: Matrix = Remaining * scale. + + @param scale axes scaling factors; may be nullptr + @param remaining SkMatrix without scaling; may be nullptr + @return true if scale can be computed + + example: https://fiddle.skia.org/c/@Matrix_decomposeScale + */ + bool decomposeScale(SkSize* scale, SkMatrix* remaining = nullptr) const; + + /** Returns reference to const identity SkMatrix. Returned SkMatrix is set to: + + | 1 0 0 | + | 0 1 0 | + | 0 0 1 | + + @return const identity SkMatrix + + example: https://fiddle.skia.org/c/@Matrix_I + */ + static const SkMatrix& I(); + + /** Returns reference to a const SkMatrix with invalid values. Returned SkMatrix is set + to: + + | SK_ScalarMax SK_ScalarMax SK_ScalarMax | + | SK_ScalarMax SK_ScalarMax SK_ScalarMax | + | SK_ScalarMax SK_ScalarMax SK_ScalarMax | + + @return const invalid SkMatrix + + example: https://fiddle.skia.org/c/@Matrix_InvalidMatrix + */ + static const SkMatrix& InvalidMatrix(); + + /** Returns SkMatrix a multiplied by SkMatrix b. + + Given: + + | A B C | | J K L | + a = | D E F |, b = | M N O | + | G H I | | P Q R | + + sets SkMatrix to: + + | A B C | | J K L | | AJ+BM+CP AK+BN+CQ AL+BO+CR | + a * b = | D E F | * | M N O | = | DJ+EM+FP DK+EN+FQ DL+EO+FR | + | G H I | | P Q R | | GJ+HM+IP GK+HN+IQ GL+HO+IR | + + @param a SkMatrix on left side of multiply expression + @param b SkMatrix on right side of multiply expression + @return SkMatrix computed from a times b + */ + static SkMatrix Concat(const SkMatrix& a, const SkMatrix& b) { + SkMatrix result; + result.setConcat(a, b); + return result; + } + + friend SkMatrix operator*(const SkMatrix& a, const SkMatrix& b) { + return Concat(a, b); + } + + /** Sets internal cache to unknown state. Use to force update after repeated + modifications to SkMatrix element reference returned by operator[](int index). + */ + void dirtyMatrixTypeCache() { + this->setTypeMask(kUnknown_Mask); + } + + /** Initializes SkMatrix with scale and translate elements. + + | sx 0 tx | + | 0 sy ty | + | 0 0 1 | + + @param sx horizontal scale factor to store + @param sy vertical scale factor to store + @param tx horizontal translation to store + @param ty vertical translation to store + */ + void setScaleTranslate(SkScalar sx, SkScalar sy, SkScalar tx, SkScalar ty) { + fMat[kMScaleX] = sx; + fMat[kMSkewX] = 0; + fMat[kMTransX] = tx; + + fMat[kMSkewY] = 0; + fMat[kMScaleY] = sy; + fMat[kMTransY] = ty; + + fMat[kMPersp0] = 0; + fMat[kMPersp1] = 0; + fMat[kMPersp2] = 1; + + int mask = 0; + if (sx != 1 || sy != 1) { + mask |= kScale_Mask; + } + if (tx != 0.0f || ty != 0.0f) { + mask |= kTranslate_Mask; + } + if (sx != 0 && sy != 0) { + mask |= kRectStaysRect_Mask; + } + this->setTypeMask(mask); + } + + /** Returns true if all elements of the matrix are finite. Returns false if any + element is infinity, or NaN. + + @return true if matrix has only finite elements + */ + bool isFinite() const { return SkIsFinite(fMat, 9); } + +private: + /** Set if the matrix will map a rectangle to another rectangle. This + can be true if the matrix is scale-only, or rotates a multiple of + 90 degrees. + + This bit will be set on identity matrices + */ + static constexpr int kRectStaysRect_Mask = 0x10; + + /** Set if the perspective bit is valid even though the rest of + the matrix is Unknown. + */ + static constexpr int kOnlyPerspectiveValid_Mask = 0x40; + + static constexpr int kUnknown_Mask = 0x80; + + static constexpr int kORableMasks = kTranslate_Mask | + kScale_Mask | + kAffine_Mask | + kPerspective_Mask; + + static constexpr int kAllMasks = kTranslate_Mask | + kScale_Mask | + kAffine_Mask | + kPerspective_Mask | + kRectStaysRect_Mask; + + SkScalar fMat[9]; + mutable int32_t fTypeMask; + + constexpr SkMatrix(SkScalar sx, SkScalar kx, SkScalar tx, + SkScalar ky, SkScalar sy, SkScalar ty, + SkScalar p0, SkScalar p1, SkScalar p2, int typeMask) + : fMat{sx, kx, tx, + ky, sy, ty, + p0, p1, p2} + , fTypeMask(typeMask) {} + + static void ComputeInv(SkScalar dst[9], const SkScalar src[9], double invDet, bool isPersp); + + uint8_t computeTypeMask() const; + uint8_t computePerspectiveTypeMask() const; + + void setTypeMask(int mask) { + // allow kUnknown or a valid mask + SkASSERT(kUnknown_Mask == mask || (mask & kAllMasks) == mask || + ((kUnknown_Mask | kOnlyPerspectiveValid_Mask) & mask) + == (kUnknown_Mask | kOnlyPerspectiveValid_Mask)); + fTypeMask = mask; + } + + void orTypeMask(int mask) { + SkASSERT((mask & kORableMasks) == mask); + fTypeMask |= mask; + } + + void clearTypeMask(int mask) { + // only allow a valid mask + SkASSERT((mask & kAllMasks) == mask); + fTypeMask &= ~mask; + } + + TypeMask getPerspectiveTypeMaskOnly() const { + if ((fTypeMask & kUnknown_Mask) && + !(fTypeMask & kOnlyPerspectiveValid_Mask)) { + fTypeMask = this->computePerspectiveTypeMask(); + } + return (TypeMask)(fTypeMask & 0xF); + } + + /** Returns true if we already know that the matrix is identity; + false otherwise. + */ + bool isTriviallyIdentity() const { + if (fTypeMask & kUnknown_Mask) { + return false; + } + return ((fTypeMask & 0xF) == 0); + } + + inline void updateTranslateMask() { + if ((fMat[kMTransX] != 0) | (fMat[kMTransY] != 0)) { + fTypeMask |= kTranslate_Mask; + } else { + fTypeMask &= ~kTranslate_Mask; + } + } + + typedef void (*MapXYProc)(const SkMatrix& mat, SkScalar x, SkScalar y, + SkPoint* result); + + static MapXYProc GetMapXYProc(TypeMask mask) { + SkASSERT((mask & ~kAllMasks) == 0); + return gMapXYProcs[mask & kAllMasks]; + } + + MapXYProc getMapXYProc() const { + return GetMapXYProc(this->getType()); + } + + typedef void (*MapPtsProc)(const SkMatrix& mat, SkPoint dst[], + const SkPoint src[], int count); + + static MapPtsProc GetMapPtsProc(TypeMask mask) { + SkASSERT((mask & ~kAllMasks) == 0); + return gMapPtsProcs[mask & kAllMasks]; + } + + MapPtsProc getMapPtsProc() const { + return GetMapPtsProc(this->getType()); + } + + [[nodiscard]] bool invertNonIdentity(SkMatrix* inverse) const; + + static bool Poly2Proc(const SkPoint[], SkMatrix*); + static bool Poly3Proc(const SkPoint[], SkMatrix*); + static bool Poly4Proc(const SkPoint[], SkMatrix*); + + static void Identity_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); + static void Trans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); + static void Scale_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); + static void ScaleTrans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); + static void Rot_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); + static void RotTrans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); + static void Persp_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); + + static const MapXYProc gMapXYProcs[]; + + static void Identity_pts(const SkMatrix&, SkPoint[], const SkPoint[], int); + static void Trans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int); + static void Scale_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int); + static void ScaleTrans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], + int count); + static void Persp_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int); + + static void Affine_vpts(const SkMatrix&, SkPoint dst[], const SkPoint[], int); + + static const MapPtsProc gMapPtsProcs[]; + + // return the number of bytes written, whether or not buffer is null + size_t writeToMemory(void* buffer) const; + /** + * Reads data from the buffer parameter + * + * @param buffer Memory to read from + * @param length Amount of memory available in the buffer + * @return number of bytes read (must be a multiple of 4) or + * 0 if there was not enough memory available + */ + size_t readFromMemory(const void* buffer, size_t length); + + // legacy method -- still needed? why not just postScale(1/divx, ...)? + bool postIDiv(int divx, int divy); + void doNormalizePerspective(); + + friend class SkPerspIter; + friend class SkMatrixPriv; + friend class SerializationTest; +}; +SK_END_REQUIRE_DENSE + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkMesh.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkMesh.h new file mode 100644 index 0000000000..6eccc937fd --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkMesh.h @@ -0,0 +1,429 @@ +/* + * Copyright 2021 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkMesh_DEFINED +#define SkMesh_DEFINED + +#include "include/core/SkData.h" +#include "include/core/SkRect.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkSpan.h" +#include "include/core/SkString.h" +#include "include/effects/SkRuntimeEffect.h" +#include "include/private/base/SkAPI.h" +#include "include/private/base/SkTArray.h" + +#include +#include +#include +#include +#include +#include + +class GrDirectContext; +class SkColorSpace; +enum SkAlphaType : int; + +namespace SkSL { struct Program; } + +/** + * A specification for custom meshes. Specifies the vertex buffer attributes and stride, the + * vertex program that produces a user-defined set of varyings, and a fragment program that ingests + * the interpolated varyings and produces local coordinates for shading and optionally a color. + * + * The varyings must include a float2 named "position". If the passed varyings does not + * contain such a varying then one is implicitly added to the final specification and the SkSL + * Varyings struct described below. It is an error to have a varying named "position" that has a + * type other than float2. + * + * The provided attributes and varyings are used to create Attributes and Varyings structs in SkSL + * that are used by the shaders. Each attribute from the Attribute span becomes a member of the + * SkSL Attributes struct and likewise for the varyings. + * + * The signature of the vertex program must be: + * Varyings main(const Attributes). + * + * The signature of the fragment program must be either: + * float2 main(const Varyings) + * or + * float2 main(const Varyings, out (half4|float4) color) + * + * where the return value is the local coordinates that will be used to access SkShader. If the + * color variant is used, the returned color will be blended with SkPaint's SkShader (or SkPaint + * color in absence of a SkShader) using the SkBlender passed to SkCanvas drawMesh(). To use + * interpolated local space positions as the shader coordinates, equivalent to how SkPaths are + * shaded, return the position field from the Varying struct as the coordinates. + * + * The vertex and fragment programs may both contain uniforms. Uniforms with the same name are + * assumed to be shared between stages. It is an error to specify uniforms in the vertex and + * fragment program with the same name but different types, dimensionality, or layouts. + */ +class SK_API SkMeshSpecification : public SkNVRefCnt { +public: + /** These values are enforced when creating a specification. */ + static constexpr size_t kMaxStride = 1024; + static constexpr size_t kMaxAttributes = 8; + static constexpr size_t kStrideAlignment = 4; + static constexpr size_t kOffsetAlignment = 4; + static constexpr size_t kMaxVaryings = 6; + + struct Attribute { + enum class Type : uint32_t { // CPU representation Shader Type + kFloat, // float float + kFloat2, // two floats float2 + kFloat3, // three floats float3 + kFloat4, // four floats float4 + kUByte4_unorm, // four bytes half4 + + kLast = kUByte4_unorm + }; + Type type; + size_t offset; + SkString name; + }; + + struct Varying { + enum class Type : uint32_t { + kFloat, // "float" + kFloat2, // "float2" + kFloat3, // "float3" + kFloat4, // "float4" + kHalf, // "half" + kHalf2, // "half2" + kHalf3, // "half3" + kHalf4, // "half4" + + kLast = kHalf4 + }; + Type type; + SkString name; + }; + + using Uniform = SkRuntimeEffect::Uniform; + using Child = SkRuntimeEffect::Child; + + ~SkMeshSpecification(); + + struct Result { + sk_sp specification; + SkString error; + }; + + /** + * If successful the return is a specification and an empty error string. Otherwise, it is a + * null specification a non-empty error string. + * + * @param attributes The vertex attributes that will be consumed by 'vs'. Attributes need + * not be tightly packed but attribute offsets must be aligned to + * kOffsetAlignment and offset + size may not be greater than + * 'vertexStride'. At least one attribute is required. + * @param vertexStride The offset between successive attribute values. This must be aligned to + * kStrideAlignment. + * @param varyings The varyings that will be written by 'vs' and read by 'fs'. This may + * be empty. + * @param vs The vertex shader code that computes a vertex position and the varyings + * from the attributes. + * @param fs The fragment code that computes a local coordinate and optionally a + * color from the varyings. The local coordinate is used to sample + * SkShader. + * @param cs The colorspace of the color produced by 'fs'. Ignored if 'fs's main() + * function does not have a color out param. + * @param at The alpha type of the color produced by 'fs'. Ignored if 'fs's main() + * function does not have a color out param. Cannot be kUnknown. + */ + static Result Make(SkSpan attributes, + size_t vertexStride, + SkSpan varyings, + const SkString& vs, + const SkString& fs); + static Result Make(SkSpan attributes, + size_t vertexStride, + SkSpan varyings, + const SkString& vs, + const SkString& fs, + sk_sp cs); + static Result Make(SkSpan attributes, + size_t vertexStride, + SkSpan varyings, + const SkString& vs, + const SkString& fs, + sk_sp cs, + SkAlphaType at); + + SkSpan attributes() const { return SkSpan(fAttributes); } + + /** + * Combined size of all 'uniform' variables. When creating a SkMesh with this specification + * provide an SkData of this size, containing values for all of those variables. Use uniforms() + * to get the offset of each uniform within the SkData. + */ + size_t uniformSize() const; + + /** + * Provides info about individual uniforms including the offset into an SkData where each + * uniform value should be placed. + */ + SkSpan uniforms() const { return SkSpan(fUniforms); } + + /** Provides basic info about individual children: names, indices and runtime effect type. */ + SkSpan children() const { return SkSpan(fChildren); } + + /** Returns a pointer to the named child's description, or nullptr if not found. */ + const Child* findChild(std::string_view name) const; + + /** Returns a pointer to the named uniform variable's description, or nullptr if not found. */ + const Uniform* findUniform(std::string_view name) const; + + /** Returns a pointer to the named attribute, or nullptr if not found. */ + const Attribute* findAttribute(std::string_view name) const; + + /** Returns a pointer to the named varying, or nullptr if not found. */ + const Varying* findVarying(std::string_view name) const; + + size_t stride() const { return fStride; } + + SkColorSpace* colorSpace() const { return fColorSpace.get(); } + +private: + friend struct SkMeshSpecificationPriv; + + enum class ColorType { + kNone, + kHalf4, + kFloat4, + }; + + static Result MakeFromSourceWithStructs(SkSpan attributes, + size_t stride, + SkSpan varyings, + const SkString& vs, + const SkString& fs, + sk_sp cs, + SkAlphaType at); + + SkMeshSpecification(SkSpan, + size_t, + SkSpan, + int passthroughLocalCoordsVaryingIndex, + uint32_t deadVaryingMask, + std::vector uniforms, + std::vector children, + std::unique_ptr, + std::unique_ptr, + ColorType, + sk_sp, + SkAlphaType); + + SkMeshSpecification(const SkMeshSpecification&) = delete; + SkMeshSpecification(SkMeshSpecification&&) = delete; + + SkMeshSpecification& operator=(const SkMeshSpecification&) = delete; + SkMeshSpecification& operator=(SkMeshSpecification&&) = delete; + + const std::vector fAttributes; + const std::vector fVaryings; + const std::vector fUniforms; + const std::vector fChildren; + const std::unique_ptr fVS; + const std::unique_ptr fFS; + const size_t fStride; + uint32_t fHash; + const int fPassthroughLocalCoordsVaryingIndex; + const uint32_t fDeadVaryingMask; + const ColorType fColorType; + const sk_sp fColorSpace; + const SkAlphaType fAlphaType; +}; + +/** + * A vertex buffer, a topology, optionally an index buffer, and a compatible SkMeshSpecification. + * + * The data in the vertex buffer is expected to contain the attributes described by the spec + * for vertexCount vertices, beginning at vertexOffset. vertexOffset must be aligned to the + * SkMeshSpecification's vertex stride. The size of the buffer must be at least vertexOffset + + * spec->stride()*vertexCount (even if vertex attributes contains pad at the end of the stride). If + * the specified bounds do not contain all the points output by the spec's vertex program when + * applied to the vertices in the custom mesh, then the result is undefined. + * + * MakeIndexed may be used to create an indexed mesh. indexCount indices are read from the index + * buffer at the specified offset, which must be aligned to 2. The indices are always unsigned + * 16-bit integers. The index count must be at least 3. + * + * If Make() is used, the implicit index sequence is 0, 1, 2, 3, ... and vertexCount must be at + * least 3. + * + * Both Make() and MakeIndexed() take a SkData with the uniform values. See + * SkMeshSpecification::uniformSize() and SkMeshSpecification::uniforms() for sizing and packing + * uniforms into the SkData. + */ +class SK_API SkMesh { +public: + class IndexBuffer : public SkRefCnt { + public: + virtual size_t size() const = 0; + + /** + * Modifies the data in the IndexBuffer by copying size bytes from data into the buffer + * at offset. Fails if offset + size > this->size() or if either offset or size is not + * aligned to 4 bytes. The GrDirectContext* must match that used to create the buffer. We + * take it as a parameter to emphasize that the context must be used to update the data and + * thus the context must be valid for the current thread. + */ + bool update(GrDirectContext*, const void* data, size_t offset, size_t size); + + private: + virtual bool onUpdate(GrDirectContext*, const void* data, size_t offset, size_t size) = 0; + }; + + class VertexBuffer : public SkRefCnt { + public: + virtual size_t size() const = 0; + + /** + * Modifies the data in the IndexBuffer by copying size bytes from data into the buffer + * at offset. Fails if offset + size > this->size() or if either offset or size is not + * aligned to 4 bytes. The GrDirectContext* must match that used to create the buffer. We + * take it as a parameter to emphasize that the context must be used to update the data and + * thus the context must be valid for the current thread. + */ + bool update(GrDirectContext*, const void* data, size_t offset, size_t size); + + private: + virtual bool onUpdate(GrDirectContext*, const void* data, size_t offset, size_t size) = 0; + }; + + SkMesh(); + ~SkMesh(); + + SkMesh(const SkMesh&); + SkMesh(SkMesh&&); + + SkMesh& operator=(const SkMesh&); + SkMesh& operator=(SkMesh&&); + + enum class Mode { kTriangles, kTriangleStrip }; + + struct Result; + + using ChildPtr = SkRuntimeEffect::ChildPtr; + + /** + * Creates a non-indexed SkMesh. The returned SkMesh can be tested for validity using + * SkMesh::isValid(). An invalid mesh simply fails to draws if passed to SkCanvas::drawMesh(). + * If the mesh is invalid the returned string give contain the reason for the failure (e.g. the + * vertex buffer was null or uniform data too small). + */ + static Result Make(sk_sp, + Mode, + sk_sp, + size_t vertexCount, + size_t vertexOffset, + sk_sp uniforms, + SkSpan children, + const SkRect& bounds); + + /** + * Creates an indexed SkMesh. The returned SkMesh can be tested for validity using + * SkMesh::isValid(). A invalid mesh simply fails to draw if passed to SkCanvas::drawMesh(). + * If the mesh is invalid the returned string give contain the reason for the failure (e.g. the + * index buffer was null or uniform data too small). + */ + static Result MakeIndexed(sk_sp, + Mode, + sk_sp, + size_t vertexCount, + size_t vertexOffset, + sk_sp, + size_t indexCount, + size_t indexOffset, + sk_sp uniforms, + SkSpan children, + const SkRect& bounds); + + sk_sp refSpec() const { return fSpec; } + SkMeshSpecification* spec() const { return fSpec.get(); } + + Mode mode() const { return fMode; } + + sk_sp refVertexBuffer() const { return fVB; } + VertexBuffer* vertexBuffer() const { return fVB.get(); } + + size_t vertexOffset() const { return fVOffset; } + size_t vertexCount() const { return fVCount; } + + sk_sp refIndexBuffer() const { return fIB; } + IndexBuffer* indexBuffer() const { return fIB.get(); } + + size_t indexOffset() const { return fIOffset; } + size_t indexCount() const { return fICount; } + + sk_sp refUniforms() const { return fUniforms; } + const SkData* uniforms() const { return fUniforms.get(); } + + SkSpan children() const { return SkSpan(fChildren); } + + SkRect bounds() const { return fBounds; } + + bool isValid() const; + +private: + std::tuple validate() const; + + sk_sp fSpec; + + sk_sp fVB; + sk_sp fIB; + + sk_sp fUniforms; + skia_private::STArray<2, ChildPtr> fChildren; + + size_t fVOffset = 0; // Must be a multiple of spec->stride() + size_t fVCount = 0; + + size_t fIOffset = 0; // Must be a multiple of sizeof(uint16_t) + size_t fICount = 0; + + Mode fMode = Mode::kTriangles; + + SkRect fBounds = SkRect::MakeEmpty(); +}; + +struct SkMesh::Result { SkMesh mesh; SkString error; }; + +namespace SkMeshes { +/** + * Makes a CPU-backed index buffer to be used with SkMeshes. + * + * @param data The data used to populate the buffer, or nullptr to create a zero- + * initialized buffer. + * @param size Both the size of the data in 'data' and the size of the resulting + * buffer, in bytes. + */ +SK_API sk_sp MakeIndexBuffer(const void* data, size_t size); + +/** + * Makes a copy of an index buffer. The copy will be CPU-backed. + */ +SK_API sk_sp CopyIndexBuffer(const sk_sp&); + +/** + * Makes a CPU-backed vertex buffer to be used with SkMeshes. + * + * @param data The data used to populate the buffer, or nullptr to create a zero- + * initialized buffer. + * @param size Both the size of the data in 'data' and the size of the resulting + * buffer, in bytes. + */ +SK_API sk_sp MakeVertexBuffer(const void*, size_t size); + +/** + * Makes a copy of a vertex buffer. The copy will be CPU-backed. + */ +SK_API sk_sp CopyVertexBuffer(const sk_sp&); +} // namespace SkMeshes + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkMilestone.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkMilestone.h new file mode 100644 index 0000000000..05671302e5 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkMilestone.h @@ -0,0 +1,9 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SK_MILESTONE +#define SK_MILESTONE 127 +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkOpenTypeSVGDecoder.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkOpenTypeSVGDecoder.h new file mode 100644 index 0000000000..5a2e48a9df --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkOpenTypeSVGDecoder.h @@ -0,0 +1,30 @@ +/* + * 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 SkOpenTypeSVGDecoder_DEFINED +#define SkOpenTypeSVGDecoder_DEFINED + +#include "include/core/SkColor.h" +#include "include/core/SkSpan.h" +#include "include/core/SkTypes.h" + +#include + +class SkCanvas; + +class SkOpenTypeSVGDecoder { +public: + /** Each instance probably owns an SVG DOM. + * The instance may be cached so needs to report how much memory it retains. + */ + virtual size_t approximateSize() = 0; + virtual bool render(SkCanvas&, int upem, SkGlyphID glyphId, + SkColor foregroundColor, SkSpan palette) = 0; + virtual ~SkOpenTypeSVGDecoder() = default; +}; + +#endif // SkOpenTypeSVGDecoder_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkOverdrawCanvas.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkOverdrawCanvas.h new file mode 100644 index 0000000000..5dea52ae69 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkOverdrawCanvas.h @@ -0,0 +1,94 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkOverdrawCanvas_DEFINED +#define SkOverdrawCanvas_DEFINED + +#include "include/core/SkCanvas.h" +#include "include/core/SkCanvasVirtualEnforcer.h" +#include "include/core/SkColor.h" +#include "include/core/SkPaint.h" +#include "include/core/SkSamplingOptions.h" +#include "include/core/SkScalar.h" +#include "include/private/base/SkAPI.h" +#include "include/utils/SkNWayCanvas.h" + +#include + +class SkData; +class SkDrawable; +class SkImage; +class SkMatrix; +class SkPath; +class SkPicture; +class SkRRect; +class SkRegion; +class SkTextBlob; +class SkVertices; +enum class SkBlendMode; +namespace sktext { class GlyphRunList; } +struct SkDrawShadowRec; +struct SkPoint; +struct SkRSXform; +struct SkRect; + +/** + * Captures all drawing commands. Rather than draw the actual content, this device + * increments the alpha channel of each pixel every time it would have been touched + * by a draw call. This is useful for detecting overdraw. + */ +class SK_API SkOverdrawCanvas : public SkCanvasVirtualEnforcer { +public: + /* Does not take ownership of canvas */ + SkOverdrawCanvas(SkCanvas*); + + void onDrawTextBlob(const SkTextBlob*, SkScalar, SkScalar, const SkPaint&) override; + void onDrawGlyphRunList( + const sktext::GlyphRunList& glyphRunList, const SkPaint& paint) override; + void onDrawPatch(const SkPoint[12], const SkColor[4], const SkPoint[4], SkBlendMode, + const SkPaint&) override; + void onDrawPaint(const SkPaint&) override; + void onDrawBehind(const SkPaint& paint) override; + void onDrawRect(const SkRect&, const SkPaint&) override; + void onDrawRegion(const SkRegion&, const SkPaint&) override; + void onDrawOval(const SkRect&, const SkPaint&) override; + void onDrawArc(const SkRect&, SkScalar, SkScalar, bool, const SkPaint&) override; + void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override; + void onDrawRRect(const SkRRect&, const SkPaint&) override; + void onDrawPoints(PointMode, size_t, const SkPoint[], const SkPaint&) override; + void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) override; + void onDrawPath(const SkPath&, const SkPaint&) override; + + void onDrawImage2(const SkImage*, SkScalar, SkScalar, const SkSamplingOptions&, + const SkPaint*) override; + void onDrawImageRect2(const SkImage*, const SkRect&, const SkRect&, const SkSamplingOptions&, + const SkPaint*, SrcRectConstraint) override; + void onDrawImageLattice2(const SkImage*, const Lattice&, const SkRect&, SkFilterMode, + const SkPaint*) override; + void onDrawAtlas2(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int, + SkBlendMode, const SkSamplingOptions&, const SkRect*, const SkPaint*) override; + + void onDrawDrawable(SkDrawable*, const SkMatrix*) override; + void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override; + + void onDrawAnnotation(const SkRect&, const char key[], SkData* value) override; + void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override; + + void onDrawEdgeAAQuad(const SkRect&, const SkPoint[4], SkCanvas::QuadAAFlags, const SkColor4f&, + SkBlendMode) override; + void onDrawEdgeAAImageSet2(const ImageSetEntry[], int count, const SkPoint[], const SkMatrix[], + const SkSamplingOptions&,const SkPaint*, SrcRectConstraint) override; + +private: + inline SkPaint overdrawPaint(const SkPaint& paint); + + SkPaint fPaint; + + using INHERITED = SkCanvasVirtualEnforcer; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPaint.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPaint.h new file mode 100644 index 0000000000..300f0ea088 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPaint.h @@ -0,0 +1,695 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkPaint_DEFINED +#define SkPaint_DEFINED + +#include "include/core/SkColor.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" +#include "include/private/base/SkCPUTypes.h" +#include "include/private/base/SkFloatingPoint.h" +#include "include/private/base/SkTo.h" +#include "include/private/base/SkTypeTraits.h" + +#include +#include +#include + +class SkBlender; +class SkColorFilter; +class SkColorSpace; +class SkImageFilter; +class SkMaskFilter; +class SkPathEffect; +class SkShader; +enum class SkBlendMode; +struct SkRect; + +/** \class SkPaint + SkPaint controls options applied when drawing. SkPaint collects all + options outside of the SkCanvas clip and SkCanvas matrix. + + Various options apply to strokes and fills, and images. + + SkPaint collects effects and filters that describe single-pass and multiple-pass + algorithms that alter the drawing geometry, color, and transparency. For instance, + SkPaint does not directly implement dashing or blur, but contains the objects that do so. +*/ +class SK_API SkPaint { +public: + + /** Constructs SkPaint with default values. + + @return default initialized SkPaint + + example: https://fiddle.skia.org/c/@Paint_empty_constructor + */ + SkPaint(); + + /** Constructs SkPaint with default values and the given color. + + Sets alpha and RGB used when stroking and filling. The color is four floating + point values, unpremultiplied. The color values are interpreted as being in + the colorSpace. If colorSpace is nullptr, then color is assumed to be in the + sRGB color space. + + @param color unpremultiplied RGBA + @param colorSpace SkColorSpace describing the encoding of color + @return SkPaint with the given color + */ + explicit SkPaint(const SkColor4f& color, SkColorSpace* colorSpace = nullptr); + + /** Makes a shallow copy of SkPaint. SkPathEffect, SkShader, + SkMaskFilter, SkColorFilter, and SkImageFilter are shared + between the original paint and the copy. Objects containing SkRefCnt increment + their references by one. + + The referenced objects SkPathEffect, SkShader, SkMaskFilter, SkColorFilter, + and SkImageFilter cannot be modified after they are created. + This prevents objects with SkRefCnt from being modified once SkPaint refers to them. + + @param paint original to copy + @return shallow copy of paint + + example: https://fiddle.skia.org/c/@Paint_copy_const_SkPaint + */ + SkPaint(const SkPaint& paint); + + /** Implements a move constructor to avoid increasing the reference counts + of objects referenced by the paint. + + After the call, paint is undefined, and can be safely destructed. + + @param paint original to move + @return content of paint + + example: https://fiddle.skia.org/c/@Paint_move_SkPaint + */ + SkPaint(SkPaint&& paint); + + /** Decreases SkPaint SkRefCnt of owned objects: SkPathEffect, SkShader, + SkMaskFilter, SkColorFilter, and SkImageFilter. If the + objects containing SkRefCnt go to zero, they are deleted. + */ + ~SkPaint(); + + /** Makes a shallow copy of SkPaint. SkPathEffect, SkShader, + SkMaskFilter, SkColorFilter, and SkImageFilter are shared + between the original paint and the copy. Objects containing SkRefCnt in the + prior destination are decreased by one, and the referenced objects are deleted if the + resulting count is zero. Objects containing SkRefCnt in the parameter paint + are increased by one. paint is unmodified. + + @param paint original to copy + @return content of paint + + example: https://fiddle.skia.org/c/@Paint_copy_operator + */ + SkPaint& operator=(const SkPaint& paint); + + /** Moves the paint to avoid increasing the reference counts + of objects referenced by the paint parameter. Objects containing SkRefCnt in the + prior destination are decreased by one; those objects are deleted if the resulting count + is zero. + + After the call, paint is undefined, and can be safely destructed. + + @param paint original to move + @return content of paint + + example: https://fiddle.skia.org/c/@Paint_move_operator + */ + SkPaint& operator=(SkPaint&& paint); + + /** Compares a and b, and returns true if a and b are equivalent. May return false + if SkPathEffect, SkShader, SkMaskFilter, SkColorFilter, + or SkImageFilter have identical contents but different pointers. + + @param a SkPaint to compare + @param b SkPaint to compare + @return true if SkPaint pair are equivalent + */ + SK_API friend bool operator==(const SkPaint& a, const SkPaint& b); + + /** Compares a and b, and returns true if a and b are not equivalent. May return true + if SkPathEffect, SkShader, SkMaskFilter, SkColorFilter, + or SkImageFilter have identical contents but different pointers. + + @param a SkPaint to compare + @param b SkPaint to compare + @return true if SkPaint pair are not equivalent + */ + friend bool operator!=(const SkPaint& a, const SkPaint& b) { + return !(a == b); + } + + /** Sets all SkPaint contents to their initial values. This is equivalent to replacing + SkPaint with the result of SkPaint(). + + example: https://fiddle.skia.org/c/@Paint_reset + */ + void reset(); + + /** Returns true if pixels on the active edges of SkPath may be drawn with partial transparency. + @return antialiasing state + */ + bool isAntiAlias() const { + return SkToBool(fBitfields.fAntiAlias); + } + + /** Requests, but does not require, that edge pixels draw opaque or with + partial transparency. + @param aa setting for antialiasing + */ + void setAntiAlias(bool aa) { fBitfields.fAntiAlias = static_cast(aa); } + + /** Returns true if color error may be distributed to smooth color transition. + @return dithering state + */ + bool isDither() const { + return SkToBool(fBitfields.fDither); + } + + /** Requests, but does not require, to distribute color error. + @param dither setting for ditering + */ + void setDither(bool dither) { fBitfields.fDither = static_cast(dither); } + + /** \enum SkPaint::Style + Set Style to fill, stroke, or both fill and stroke geometry. + The stroke and fill + share all paint attributes; for instance, they are drawn with the same color. + + Use kStrokeAndFill_Style to avoid hitting the same pixels twice with a stroke draw and + a fill draw. + */ + enum Style : uint8_t { + kFill_Style, //!< set to fill geometry + kStroke_Style, //!< set to stroke geometry + kStrokeAndFill_Style, //!< sets to stroke and fill geometry + }; + + /** May be used to verify that SkPaint::Style is a legal value. + */ + static constexpr int kStyleCount = kStrokeAndFill_Style + 1; + + /** Returns whether the geometry is filled, stroked, or filled and stroked. + */ + Style getStyle() const { return (Style)fBitfields.fStyle; } + + /** Sets whether the geometry is filled, stroked, or filled and stroked. + Has no effect if style is not a legal SkPaint::Style value. + + example: https://fiddle.skia.org/c/@Paint_setStyle + example: https://fiddle.skia.org/c/@Stroke_Width + */ + void setStyle(Style style); + + /** + * Set paint's style to kStroke if true, or kFill if false. + */ + void setStroke(bool); + + /** Retrieves alpha and RGB, unpremultiplied, packed into 32 bits. + Use helpers SkColorGetA(), SkColorGetR(), SkColorGetG(), and SkColorGetB() to extract + a color component. + + @return unpremultiplied ARGB + */ + SkColor getColor() const { return fColor4f.toSkColor(); } + + /** Retrieves alpha and RGB, unpremultiplied, as four floating point values. RGB are + extended sRGB values (sRGB gamut, and encoded with the sRGB transfer function). + + @return unpremultiplied RGBA + */ + SkColor4f getColor4f() const { return fColor4f; } + + /** Sets alpha and RGB used when stroking and filling. The color is a 32-bit value, + unpremultiplied, packing 8-bit components for alpha, red, blue, and green. + + @param color unpremultiplied ARGB + + example: https://fiddle.skia.org/c/@Paint_setColor + */ + void setColor(SkColor color); + + /** Sets alpha and RGB used when stroking and filling. The color is four floating + point values, unpremultiplied. The color values are interpreted as being in + the colorSpace. If colorSpace is nullptr, then color is assumed to be in the + sRGB color space. + + @param color unpremultiplied RGBA + @param colorSpace SkColorSpace describing the encoding of color + */ + void setColor(const SkColor4f& color, SkColorSpace* colorSpace = nullptr); + + void setColor4f(const SkColor4f& color, SkColorSpace* colorSpace = nullptr) { + this->setColor(color, colorSpace); + } + + /** Retrieves alpha from the color used when stroking and filling. + + @return alpha ranging from zero, fully transparent, to one, fully opaque + */ + float getAlphaf() const { return fColor4f.fA; } + + // Helper that scales the alpha by 255. + uint8_t getAlpha() const { + return static_cast(sk_float_round2int(this->getAlphaf() * 255)); + } + + /** Replaces alpha, leaving RGB + unchanged. An out of range value triggers an assert in the debug + build. a is a value from 0.0 to 1.0. + a set to zero makes color fully transparent; a set to 1.0 makes color + fully opaque. + + @param a alpha component of color + */ + void setAlphaf(float a); + + // Helper that accepts an int between 0 and 255, and divides it by 255.0 + void setAlpha(U8CPU a) { + this->setAlphaf(a * (1.0f / 255)); + } + + /** Sets color used when drawing solid fills. The color components range from 0 to 255. + The color is unpremultiplied; alpha sets the transparency independent of RGB. + + @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) + + example: https://fiddle.skia.org/c/@Paint_setARGB + */ + void setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b); + + /** Returns the thickness of the pen used by SkPaint to + outline the shape. + + @return zero for hairline, greater than zero for pen thickness + */ + SkScalar getStrokeWidth() const { return fWidth; } + + /** Sets the thickness of the pen used by the paint to outline the shape. + A stroke-width of zero is treated as "hairline" width. Hairlines are always exactly one + pixel wide in device space (their thickness does not change as the canvas is scaled). + Negative stroke-widths are invalid; setting a negative width will have no effect. + + @param width zero thickness for hairline; greater than zero for pen thickness + + example: https://fiddle.skia.org/c/@Miter_Limit + example: https://fiddle.skia.org/c/@Paint_setStrokeWidth + */ + void setStrokeWidth(SkScalar width); + + /** Returns the limit at which a sharp corner is drawn beveled. + + @return zero and greater miter limit + */ + 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. + + @param miter zero and greater miter limit + + example: https://fiddle.skia.org/c/@Paint_setStrokeMiter + */ + void setStrokeMiter(SkScalar miter); + + /** \enum SkPaint::Cap + Cap draws at the beginning and end of an open path contour. + */ + enum Cap { + kButt_Cap, //!< no stroke extension + kRound_Cap, //!< adds circle + kSquare_Cap, //!< adds square + kLast_Cap = kSquare_Cap, //!< largest Cap value + kDefault_Cap = kButt_Cap, //!< equivalent to kButt_Cap + }; + + /** May be used to verify that SkPaint::Cap is a legal value. + */ + static constexpr int kCapCount = kLast_Cap + 1; + + /** \enum SkPaint::Join + Join specifies how corners are drawn when a shape is stroked. Join + affects the four corners of a stroked rectangle, and the connected segments in a + stroked path. + + Choose miter join to draw sharp corners. Choose round join to draw a circle with a + radius equal to the stroke width on top of the corner. Choose bevel join to minimally + connect the thick strokes. + + The fill path constructed to describe the stroked path respects the join setting but may + not contain the actual join. For instance, a fill path constructed with round joins does + not necessarily include circles at each connected segment. + */ + enum Join : uint8_t { + kMiter_Join, //!< extends to miter limit + kRound_Join, //!< adds circle + kBevel_Join, //!< connects outside edges + kLast_Join = kBevel_Join, //!< equivalent to the largest value for Join + kDefault_Join = kMiter_Join, //!< equivalent to kMiter_Join + }; + + /** May be used to verify that SkPaint::Join is a legal value. + */ + static constexpr int kJoinCount = kLast_Join + 1; + + /** Returns the geometry drawn at the beginning and end of strokes. + */ + Cap getStrokeCap() const { return (Cap)fBitfields.fCapType; } + + /** Sets the geometry drawn at the beginning and end of strokes. + + example: https://fiddle.skia.org/c/@Paint_setStrokeCap_a + example: https://fiddle.skia.org/c/@Paint_setStrokeCap_b + */ + void setStrokeCap(Cap cap); + + /** Returns the geometry drawn at the corners of strokes. + */ + Join getStrokeJoin() const { return (Join)fBitfields.fJoinType; } + + /** Sets the geometry drawn at the corners of strokes. + + example: https://fiddle.skia.org/c/@Paint_setStrokeJoin + */ + void setStrokeJoin(Join join); + + /** Returns optional colors used when filling a path, such as a gradient. + + Does not alter SkShader SkRefCnt. + + @return SkShader if previously set, nullptr otherwise + */ + SkShader* getShader() const { return fShader.get(); } + + /** Returns optional colors used when filling a path, such as a gradient. + + Increases SkShader SkRefCnt by one. + + @return SkShader if previously set, nullptr otherwise + + example: https://fiddle.skia.org/c/@Paint_refShader + */ + sk_sp refShader() const; + + /** Sets optional colors used when filling a path, such as a gradient. + + Sets SkShader to shader, decreasing SkRefCnt of the previous SkShader. + Increments shader SkRefCnt by one. + + @param shader how geometry is filled with color; if nullptr, color is used instead + + example: https://fiddle.skia.org/c/@Color_Filter_Methods + example: https://fiddle.skia.org/c/@Paint_setShader + */ + void setShader(sk_sp shader); + + /** Returns SkColorFilter if set, or nullptr. + Does not alter SkColorFilter SkRefCnt. + + @return SkColorFilter if previously set, nullptr otherwise + */ + SkColorFilter* getColorFilter() const { return fColorFilter.get(); } + + /** Returns SkColorFilter if set, or nullptr. + Increases SkColorFilter SkRefCnt by one. + + @return SkColorFilter if set, or nullptr + + example: https://fiddle.skia.org/c/@Paint_refColorFilter + */ + sk_sp refColorFilter() const; + + /** Sets SkColorFilter to filter, decreasing SkRefCnt of the previous + SkColorFilter. Pass nullptr to clear SkColorFilter. + + Increments filter SkRefCnt by one. + + @param colorFilter SkColorFilter to apply to subsequent draw + + example: https://fiddle.skia.org/c/@Blend_Mode_Methods + example: https://fiddle.skia.org/c/@Paint_setColorFilter + */ + void setColorFilter(sk_sp colorFilter); + + /** If the current blender can be represented as a SkBlendMode enum, this returns that + * enum in the optional's value(). If it cannot, then the returned optional does not + * contain a value. + */ + std::optional asBlendMode() const; + + /** + * Queries the blender, and if it can be represented as a SkBlendMode, return that mode, + * else return the defaultMode provided. + */ + SkBlendMode getBlendMode_or(SkBlendMode defaultMode) const; + + /** Returns true iff the current blender claims to be equivalent to SkBlendMode::kSrcOver. + * + * Also returns true of the current blender is nullptr. + */ + bool isSrcOver() const; + + /** Helper method for calling setBlender(). + * + * This sets a blender that implements the specified blendmode enum. + */ + void setBlendMode(SkBlendMode mode); + + /** Returns the user-supplied blend function, if one has been set. + * Does not alter SkBlender's SkRefCnt. + * + * A nullptr blender signifies the default SrcOver behavior. + * + * @return the SkBlender assigned to this paint, otherwise nullptr + */ + SkBlender* getBlender() const { return fBlender.get(); } + + /** Returns the user-supplied blend function, if one has been set. + * Increments the SkBlender's SkRefCnt by one. + * + * A nullptr blender signifies the default SrcOver behavior. + * + * @return the SkBlender assigned to this paint, otherwise nullptr + */ + sk_sp refBlender() const; + + /** Sets the current blender, increasing its refcnt, and if a blender is already + * present, decreasing that object's refcnt. + * + * A nullptr blender signifies the default SrcOver behavior. + * + * For convenience, you can call setBlendMode() if the blend effect can be expressed + * as one of those values. + */ + void setBlender(sk_sp blender); + + /** Returns SkPathEffect if set, or nullptr. + Does not alter SkPathEffect SkRefCnt. + + @return SkPathEffect if previously set, nullptr otherwise + */ + SkPathEffect* getPathEffect() const { return fPathEffect.get(); } + + /** Returns SkPathEffect if set, or nullptr. + Increases SkPathEffect SkRefCnt by one. + + @return SkPathEffect if previously set, nullptr otherwise + + example: https://fiddle.skia.org/c/@Paint_refPathEffect + */ + sk_sp refPathEffect() const; + + /** Sets SkPathEffect to pathEffect, decreasing SkRefCnt of the previous + SkPathEffect. Pass nullptr to leave the path geometry unaltered. + + Increments pathEffect SkRefCnt by one. + + @param pathEffect replace SkPath with a modification when drawn + + example: https://fiddle.skia.org/c/@Mask_Filter_Methods + example: https://fiddle.skia.org/c/@Paint_setPathEffect + */ + void setPathEffect(sk_sp pathEffect); + + /** Returns SkMaskFilter if set, or nullptr. + Does not alter SkMaskFilter SkRefCnt. + + @return SkMaskFilter if previously set, nullptr otherwise + */ + SkMaskFilter* getMaskFilter() const { return fMaskFilter.get(); } + + /** Returns SkMaskFilter if set, or nullptr. + + Increases SkMaskFilter SkRefCnt by one. + + @return SkMaskFilter if previously set, nullptr otherwise + + example: https://fiddle.skia.org/c/@Paint_refMaskFilter + */ + sk_sp refMaskFilter() const; + + /** Sets SkMaskFilter to maskFilter, decreasing SkRefCnt of the previous + SkMaskFilter. Pass nullptr to clear SkMaskFilter and leave SkMaskFilter effect on + mask alpha unaltered. + + Increments maskFilter SkRefCnt by one. + + @param maskFilter modifies clipping mask generated from drawn geometry + + example: https://fiddle.skia.org/c/@Paint_setMaskFilter + example: https://fiddle.skia.org/c/@Typeface_Methods + */ + void setMaskFilter(sk_sp maskFilter); + + /** Returns SkImageFilter if set, or nullptr. + Does not alter SkImageFilter SkRefCnt. + + @return SkImageFilter if previously set, nullptr otherwise + */ + SkImageFilter* getImageFilter() const { return fImageFilter.get(); } + + /** Returns SkImageFilter if set, or nullptr. + Increases SkImageFilter SkRefCnt by one. + + @return SkImageFilter if previously set, nullptr otherwise + + example: https://fiddle.skia.org/c/@Paint_refImageFilter + */ + sk_sp refImageFilter() const; + + /** Sets SkImageFilter to imageFilter, decreasing SkRefCnt of the previous + SkImageFilter. Pass nullptr to clear SkImageFilter, and remove SkImageFilter effect + on drawing. + + Increments imageFilter SkRefCnt by one. + + @param imageFilter how SkImage is sampled when transformed + + example: https://fiddle.skia.org/c/@Paint_setImageFilter + */ + void setImageFilter(sk_sp imageFilter); + + /** Returns true if SkPaint prevents all drawing; + otherwise, the SkPaint may or may not allow drawing. + + Returns true if, for example, SkBlendMode combined with alpha computes a + new alpha of zero. + + @return true if SkPaint prevents all drawing + + example: https://fiddle.skia.org/c/@Paint_nothingToDraw + */ + bool nothingToDraw() const; + + /** (to be made private) + Returns true if SkPaint does not include elements requiring extensive computation + to compute device bounds of drawn geometry. For instance, SkPaint with SkPathEffect + always returns false. + + @return true if SkPaint allows for fast computation of bounds + */ + bool canComputeFastBounds() const; + + /** (to be made private) + Only call this if canComputeFastBounds() returned true. This takes a + raw rectangle (the raw bounds of a shape), and adjusts it for stylistic + effects in the paint (e.g. stroking). If needed, it uses the storage + parameter. It returns the adjusted bounds that can then be used + for SkCanvas::quickReject tests. + + The returned SkRect will either be orig or storage, thus the caller + should not rely on storage being set to the result, but should always + use the returned value. It is legal for orig and storage to be the same + SkRect. + For example: + if (!path.isInverseFillType() && paint.canComputeFastBounds()) { + SkRect storage; + if (canvas->quickReject(paint.computeFastBounds(path.getBounds(), &storage))) { + return; // do not draw the path + } + } + // draw the path + + @param orig geometry modified by SkPaint when drawn + @param storage computed bounds of geometry; may not be nullptr + @return fast computed bounds + */ + const SkRect& computeFastBounds(const SkRect& orig, SkRect* storage) const; + + /** (to be made private) + + @param orig geometry modified by SkPaint when drawn + @param storage computed bounds of geometry + @return fast computed bounds + */ + const SkRect& computeFastStrokeBounds(const SkRect& orig, + SkRect* storage) const { + return this->doComputeFastBounds(orig, storage, kStroke_Style); + } + + /** (to be made private) + Computes the bounds, overriding the SkPaint SkPaint::Style. This can be used to + account for additional width required by stroking orig, without + altering SkPaint::Style set to fill. + + @param orig geometry modified by SkPaint when drawn + @param storage computed bounds of geometry + @param style overrides SkPaint::Style + @return fast computed bounds + */ + const SkRect& doComputeFastBounds(const SkRect& orig, SkRect* storage, + Style style) const; + + using sk_is_trivially_relocatable = std::true_type; + +private: + sk_sp fPathEffect; + sk_sp fShader; + sk_sp fMaskFilter; + sk_sp fColorFilter; + sk_sp fImageFilter; + sk_sp fBlender; + + SkColor4f fColor4f; + SkScalar fWidth; + SkScalar fMiterLimit; + union { + struct { + unsigned fAntiAlias : 1; + unsigned fDither : 1; + unsigned fCapType : 2; + unsigned fJoinType : 2; + unsigned fStyle : 2; + unsigned fPadding : 24; // 24 == 32 -1-1-2-2-2 + } fBitfields; + uint32_t fBitfieldsUInt; + }; + + static_assert(::sk_is_trivially_relocatable::value); + static_assert(::sk_is_trivially_relocatable::value); + static_assert(::sk_is_trivially_relocatable::value); + static_assert(::sk_is_trivially_relocatable::value); + static_assert(::sk_is_trivially_relocatable::value); + static_assert(::sk_is_trivially_relocatable::value); + static_assert(::sk_is_trivially_relocatable::value); + static_assert(::sk_is_trivially_relocatable::value); + + friend class SkPaintPriv; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPath.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPath.h new file mode 100644 index 0000000000..00324e989e --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPath.h @@ -0,0 +1,1943 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkPath_DEFINED +#define SkPath_DEFINED + +#include "include/core/SkMatrix.h" +#include "include/core/SkPathTypes.h" +#include "include/core/SkPoint.h" +#include "include/core/SkRect.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" +#include "include/private/base/SkDebug.h" +#include "include/private/base/SkTo.h" +#include "include/private/base/SkTypeTraits.h" + +#include +#include +#include +#include +#include +#include + +struct SkArc; +class SkData; +class SkPathRef; +class SkRRect; +class SkWStream; +enum class SkPathConvexity; +enum class SkPathFirstDirection; +struct SkPathVerbAnalysis; + +// WIP -- define this locally, and fix call-sites to use SkPathBuilder (skbug.com/9000) +//#define SK_HIDE_PATH_EDIT_METHODS + +/** \class SkPath + SkPath contain geometry. SkPath may be empty, or contain one or more verbs that + outline a figure. SkPath always starts with a move verb to a Cartesian coordinate, + and may be followed by additional verbs that add lines or curves. + Adding a close verb makes the geometry into a continuous loop, a closed contour. + SkPath may contain any number of contours, each beginning with a move verb. + + SkPath contours may contain only a move verb, or may also contain lines, + quadratic beziers, conics, and cubic beziers. SkPath contours may be open or + closed. + + When used to draw a filled area, SkPath describes whether the fill is inside or + outside the geometry. SkPath also describes the winding rule used to fill + overlapping contours. + + Internally, SkPath lazily computes metrics likes bounds and convexity. Call + SkPath::updateBoundsCache to make SkPath thread safe. +*/ +class SK_API SkPath { +public: + /** + * Create a new path with the specified segments. + * + * The points and weights arrays are read in order, based on the sequence of verbs. + * + * Move 1 point + * Line 1 point + * Quad 2 points + * Conic 2 points and 1 weight + * Cubic 3 points + * Close 0 points + * + * If an illegal sequence of verbs is encountered, or the specified number of points + * or weights is not sufficient given the verbs, an empty Path is returned. + * + * A legal sequence of verbs consists of any number of Contours. A contour always begins + * with a Move verb, followed by 0 or more segments: Line, Quad, Conic, Cubic, followed + * by an optional Close. + */ + static SkPath Make(const SkPoint[], int pointCount, + const uint8_t[], int verbCount, + const SkScalar[], int conicWeightCount, + SkPathFillType, bool isVolatile = false); + + static SkPath Rect(const SkRect&, SkPathDirection = SkPathDirection::kCW, + unsigned startIndex = 0); + static SkPath Oval(const SkRect&, SkPathDirection = SkPathDirection::kCW); + static SkPath Oval(const SkRect&, SkPathDirection, unsigned startIndex); + static SkPath Circle(SkScalar center_x, SkScalar center_y, SkScalar radius, + SkPathDirection dir = SkPathDirection::kCW); + static SkPath RRect(const SkRRect&, SkPathDirection dir = SkPathDirection::kCW); + static SkPath RRect(const SkRRect&, SkPathDirection, unsigned startIndex); + static SkPath RRect(const SkRect& bounds, SkScalar rx, SkScalar ry, + SkPathDirection dir = SkPathDirection::kCW); + + static SkPath Polygon(const SkPoint pts[], int count, bool isClosed, + SkPathFillType = SkPathFillType::kWinding, + bool isVolatile = false); + + static SkPath Polygon(const std::initializer_list& list, bool isClosed, + SkPathFillType fillType = SkPathFillType::kWinding, + bool isVolatile = false) { + return Polygon(list.begin(), SkToInt(list.size()), isClosed, fillType, isVolatile); + } + + static SkPath Line(const SkPoint a, const SkPoint b) { + return Polygon({a, b}, false); + } + + /** Constructs an empty SkPath. By default, SkPath has no verbs, no SkPoint, and no weights. + FillType is set to kWinding. + + @return empty SkPath + + example: https://fiddle.skia.org/c/@Path_empty_constructor + */ + SkPath(); + + /** Constructs a copy of an existing path. + Copy constructor makes two paths identical by value. Internally, path and + the returned result share pointer values. The underlying verb array, SkPoint array + and weights are copied when modified. + + Creating a SkPath copy is very efficient and never allocates memory. + SkPath are always copied by value from the interface; the underlying shared + pointers are not exposed. + + @param path SkPath to copy by value + @return copy of SkPath + + example: https://fiddle.skia.org/c/@Path_copy_const_SkPath + */ + SkPath(const SkPath& path); + + /** Releases ownership of any shared data and deletes data if SkPath is sole owner. + + example: https://fiddle.skia.org/c/@Path_destructor + */ + ~SkPath(); + + /** Returns a copy of this path in the current state. */ + SkPath snapshot() const { + return *this; + } + + /** Returns a copy of this path in the current state, and resets the path to empty. */ + SkPath detach() { + SkPath result = *this; + this->reset(); + return result; + } + + /** Constructs a copy of an existing path. + SkPath assignment makes two paths identical by value. Internally, assignment + shares pointer values. The underlying verb array, SkPoint array and weights + are copied when modified. + + Copying SkPath by assignment is very efficient and never allocates memory. + SkPath are always copied by value from the interface; the underlying shared + pointers are not exposed. + + @param path verb array, SkPoint array, weights, and SkPath::FillType to copy + @return SkPath copied by value + + example: https://fiddle.skia.org/c/@Path_copy_operator + */ + SkPath& operator=(const SkPath& path); + + /** Compares a and b; returns true if SkPath::FillType, verb array, SkPoint array, and weights + are equivalent. + + @param a SkPath to compare + @param b SkPath to compare + @return true if SkPath pair are equivalent + */ + friend SK_API bool operator==(const SkPath& a, const SkPath& b); + + /** Compares a and b; returns true if SkPath::FillType, verb array, SkPoint array, and weights + are not equivalent. + + @param a SkPath to compare + @param b SkPath to compare + @return true if SkPath pair are not equivalent + */ + friend bool operator!=(const SkPath& a, const SkPath& b) { + return !(a == b); + } + + /** Returns true if SkPath contain equal verbs and equal weights. + If SkPath contain one or more conics, the weights must match. + + conicTo() may add different verbs depending on conic weight, so it is not + trivial to interpolate a pair of SkPath containing conics with different + conic weight values. + + @param compare SkPath to compare + @return true if SkPath verb array and weights are equivalent + + example: https://fiddle.skia.org/c/@Path_isInterpolatable + */ + bool isInterpolatable(const SkPath& compare) const; + + /** Interpolates between SkPath with SkPoint array of equal size. + Copy verb array and weights to out, and set out SkPoint array to a weighted + average of this SkPoint array and ending SkPoint array, using the formula: + (Path Point * weight) + ending Point * (1 - weight). + + weight is most useful when between zero (ending SkPoint array) and + one (this Point_Array); will work with values outside of this + range. + + interpolate() returns false and leaves out unchanged if SkPoint array is not + the same size as ending SkPoint array. Call isInterpolatable() to check SkPath + compatibility prior to calling interpolate(). + + @param ending SkPoint array averaged with this SkPoint array + @param weight contribution of this SkPoint array, and + one minus contribution of ending SkPoint array + @param out SkPath replaced by interpolated averages + @return true if SkPath contain same number of SkPoint + + example: https://fiddle.skia.org/c/@Path_interpolate + */ + bool interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const; + + /** Returns SkPathFillType, the rule used to fill SkPath. + + @return current SkPathFillType setting + */ + SkPathFillType getFillType() const { return (SkPathFillType)fFillType; } + + /** Sets FillType, the rule used to fill SkPath. While there is no check + that ft is legal, values outside of FillType are not supported. + */ + void setFillType(SkPathFillType ft) { + fFillType = SkToU8(ft); + } + + /** Returns if FillType describes area outside SkPath geometry. The inverse fill area + extends indefinitely. + + @return true if FillType is kInverseWinding or kInverseEvenOdd + */ + bool isInverseFillType() const { return SkPathFillType_IsInverse(this->getFillType()); } + + /** Replaces FillType with its inverse. The inverse of FillType describes the area + unmodified by the original FillType. + */ + void toggleInverseFillType() { + fFillType ^= 2; + } + + /** Returns true if the path is convex. If necessary, it will first compute the convexity. + */ + bool isConvex() const; + + /** Returns true if this path is recognized as an oval or circle. + + bounds receives bounds of oval. + + bounds is unmodified if oval is not found. + + @param bounds storage for bounding SkRect of oval; may be nullptr + @return true if SkPath is recognized as an oval or circle + + example: https://fiddle.skia.org/c/@Path_isOval + */ + bool isOval(SkRect* bounds) const; + + /** Returns true if path is representable as SkRRect. + Returns false if path is representable as oval, circle, or SkRect. + + rrect receives bounds of SkRRect. + + rrect is unmodified if SkRRect is not found. + + @param rrect storage for bounding SkRect of SkRRect; may be nullptr + @return true if SkPath contains only SkRRect + + example: https://fiddle.skia.org/c/@Path_isRRect + */ + bool isRRect(SkRRect* rrect) const; + + /** Returns true if path is representable as an oval arc. In other words, could this + path be drawn using SkCanvas::drawArc. + + arc receives parameters of arc + + @param arc storage for arc; may be nullptr + @return true if SkPath contains only a single arc from an oval + */ + bool isArc(SkArc* arc) const; + + /** Sets SkPath to its initial state. + Removes verb array, SkPoint array, and weights, and sets FillType to kWinding. + Internal storage associated with SkPath is released. + + @return reference to SkPath + + example: https://fiddle.skia.org/c/@Path_reset + */ + SkPath& reset(); + + /** Sets SkPath to its initial state, preserving internal storage. + Removes verb array, SkPoint array, and weights, and sets FillType to kWinding. + Internal storage associated with SkPath is retained. + + Use rewind() instead of reset() if SkPath storage will be reused and performance + is critical. + + @return reference to SkPath + + example: https://fiddle.skia.org/c/@Path_rewind + */ + SkPath& rewind(); + + /** Returns if SkPath is empty. + Empty SkPath may have FillType but has no SkPoint, SkPath::Verb, or conic weight. + SkPath() constructs empty SkPath; reset() and rewind() make SkPath empty. + + @return true if the path contains no SkPath::Verb array + */ + bool isEmpty() const; + + /** Returns if contour is closed. + Contour is closed if SkPath SkPath::Verb array was last modified by close(). When stroked, + closed contour draws SkPaint::Join instead of SkPaint::Cap at first and last SkPoint. + + @return true if the last contour ends with a kClose_Verb + + example: https://fiddle.skia.org/c/@Path_isLastContourClosed + */ + bool isLastContourClosed() const; + + /** Returns true for finite SkPoint array values between negative SK_ScalarMax and + positive SK_ScalarMax. Returns false for any SkPoint array value of + SK_ScalarInfinity, SK_ScalarNegativeInfinity, or SK_ScalarNaN. + + @return true if all SkPoint values are finite + */ + bool isFinite() const; + + /** Returns true if the path is volatile; it will not be altered or discarded + by the caller after it is drawn. SkPath by default have volatile set false, allowing + SkSurface to attach a cache of data which speeds repeated drawing. If true, SkSurface + may not speed repeated drawing. + + @return true if caller will alter SkPath after drawing + */ + bool isVolatile() const { + return SkToBool(fIsVolatile); + } + + /** Specifies whether SkPath is volatile; whether it will be altered or discarded + by the caller after it is drawn. SkPath by default have volatile set false, allowing + Skia to attach a cache of data which speeds repeated drawing. + + Mark temporary paths, discarded or modified after use, as volatile + to inform Skia that the path need not be cached. + + Mark animating SkPath volatile to improve performance. + Mark unchanging SkPath non-volatile to improve repeated rendering. + + raster surface SkPath draws are affected by volatile for some shadows. + GPU surface SkPath draws are affected by volatile for some shadows and concave geometries. + + @param isVolatile true if caller will alter SkPath after drawing + @return reference to SkPath + */ + SkPath& setIsVolatile(bool isVolatile) { + fIsVolatile = isVolatile; + return *this; + } + + /** Tests if line between SkPoint pair is degenerate. + Line with no length or that moves a very short distance is degenerate; it is + treated as a point. + + exact changes the equality test. If true, returns true only if p1 equals p2. + If false, returns true if p1 equals or nearly equals p2. + + @param p1 line start point + @param p2 line end point + @param exact if false, allow nearly equals + @return true if line is degenerate; its length is effectively zero + + example: https://fiddle.skia.org/c/@Path_IsLineDegenerate + */ + static bool IsLineDegenerate(const SkPoint& p1, const SkPoint& p2, bool exact); + + /** Tests if quad is degenerate. + Quad with no length or that moves a very short distance is degenerate; it is + treated as a point. + + @param p1 quad start point + @param p2 quad control point + @param p3 quad end point + @param exact if true, returns true only if p1, p2, and p3 are equal; + if false, returns true if p1, p2, and p3 are equal or nearly equal + @return true if quad is degenerate; its length is effectively zero + */ + static bool IsQuadDegenerate(const SkPoint& p1, const SkPoint& p2, + const SkPoint& p3, bool exact); + + /** Tests if cubic is degenerate. + Cubic with no length or that moves a very short distance is degenerate; it is + treated as a point. + + @param p1 cubic start point + @param p2 cubic control point 1 + @param p3 cubic control point 2 + @param p4 cubic end point + @param exact if true, returns true only if p1, p2, p3, and p4 are equal; + if false, returns true if p1, p2, p3, and p4 are equal or nearly equal + @return true if cubic is degenerate; its length is effectively zero + */ + static bool IsCubicDegenerate(const SkPoint& p1, const SkPoint& p2, + const SkPoint& p3, const SkPoint& p4, bool exact); + + /** Returns true if SkPath contains only one line; + SkPath::Verb array has two entries: kMove_Verb, kLine_Verb. + If SkPath contains one line and line is not nullptr, line is set to + line start point and line end point. + Returns false if SkPath is not one line; line is unaltered. + + @param line storage for line. May be nullptr + @return true if SkPath contains exactly one line + + example: https://fiddle.skia.org/c/@Path_isLine + */ + bool isLine(SkPoint line[2]) const; + + /** Returns the number of points in SkPath. + SkPoint count is initially zero. + + @return SkPath SkPoint array length + + example: https://fiddle.skia.org/c/@Path_countPoints + */ + int countPoints() const; + + /** Returns SkPoint at index in SkPoint array. Valid range for index is + 0 to countPoints() - 1. + Returns (0, 0) if index is out of range. + + @param index SkPoint array element selector + @return SkPoint array value or (0, 0) + + example: https://fiddle.skia.org/c/@Path_getPoint + */ + SkPoint getPoint(int index) const; + + /** Returns number of points in SkPath. Up to max points are copied. + points may be nullptr; then, max must be zero. + If max is greater than number of points, excess points storage is unaltered. + + @param points storage for SkPath SkPoint array. May be nullptr + @param max maximum to copy; must be greater than or equal to zero + @return SkPath SkPoint array length + + example: https://fiddle.skia.org/c/@Path_getPoints + */ + int getPoints(SkPoint points[], int max) const; + + /** Returns the number of verbs: kMove_Verb, kLine_Verb, kQuad_Verb, kConic_Verb, + kCubic_Verb, and kClose_Verb; added to SkPath. + + @return length of verb array + + example: https://fiddle.skia.org/c/@Path_countVerbs + */ + int countVerbs() const; + + /** Returns the number of verbs in the path. Up to max verbs are copied. The + verbs are copied as one byte per verb. + + @param verbs storage for verbs, may be nullptr + @param max maximum number to copy into verbs + @return the actual number of verbs in the path + + example: https://fiddle.skia.org/c/@Path_getVerbs + */ + int getVerbs(uint8_t verbs[], int max) const; + + /** Returns the approximate byte size of the SkPath in memory. + + @return approximate size + */ + size_t approximateBytesUsed() const; + + /** Exchanges the verb array, SkPoint array, weights, and SkPath::FillType with other. + Cached state is also exchanged. swap() internally exchanges pointers, so + it is lightweight and does not allocate memory. + + swap() usage has largely been replaced by operator=(const SkPath& path). + SkPath do not copy their content on assignment until they are written to, + making assignment as efficient as swap(). + + @param other SkPath exchanged by value + + example: https://fiddle.skia.org/c/@Path_swap + */ + void swap(SkPath& other); + + /** Returns minimum and maximum axes values of SkPoint array. + Returns (0, 0, 0, 0) if SkPath contains no points. Returned bounds width and height may + be larger or smaller than area affected when SkPath is drawn. + + SkRect returned includes all SkPoint added to SkPath, including SkPoint associated with + kMove_Verb that define empty contours. + + @return bounds of all SkPoint in SkPoint array + */ + const SkRect& getBounds() const; + + /** Updates internal bounds so that subsequent calls to getBounds() are instantaneous. + Unaltered copies of SkPath may also access cached bounds through getBounds(). + + For now, identical to calling getBounds() and ignoring the returned value. + + Call to prepare SkPath subsequently drawn from multiple threads, + to avoid a race condition where each draw separately computes the bounds. + */ + void updateBoundsCache() const { + // for now, just calling getBounds() is sufficient + this->getBounds(); + } + + /** Returns minimum and maximum axes values of the lines and curves in SkPath. + Returns (0, 0, 0, 0) if SkPath contains no points. + Returned bounds width and height may be larger or smaller than area affected + when SkPath is drawn. + + Includes SkPoint associated with kMove_Verb that define empty + contours. + + Behaves identically to getBounds() when SkPath contains + only lines. If SkPath contains curves, computed bounds includes + the maximum extent of the quad, conic, or cubic; is slower than getBounds(); + and unlike getBounds(), does not cache the result. + + @return tight bounds of curves in SkPath + + example: https://fiddle.skia.org/c/@Path_computeTightBounds + */ + SkRect computeTightBounds() const; + + /** Returns true if rect is contained by SkPath. + May return false when rect is contained by SkPath. + + For now, only returns true if SkPath has one contour and is convex. + rect may share points and edges with SkPath and be contained. + Returns true if rect is empty, that is, it has zero width or height; and + the SkPoint or line described by rect is contained by SkPath. + + @param rect SkRect, line, or SkPoint checked for containment + @return true if rect is contained + + example: https://fiddle.skia.org/c/@Path_conservativelyContainsRect + */ + bool conservativelyContainsRect(const SkRect& rect) const; + + /** Grows SkPath verb array, SkPoint array, and conics to contain additional space. + May improve performance and use less memory by + reducing the number and size of allocations when creating SkPath. + + @param extraPtCount number of additional SkPoint to allocate + @param extraVerbCount number of additional verbs + @param extraConicCount number of additional conics + + example: https://fiddle.skia.org/c/@Path_incReserve + */ + void incReserve(int extraPtCount, int extraVerbCount = 0, int extraConicCount = 0); + +#ifdef SK_HIDE_PATH_EDIT_METHODS +private: +#endif + + /** Adds beginning of contour at SkPoint (x, y). + + @param x x-axis value of contour start + @param y y-axis value of contour start + @return reference to SkPath + + example: https://fiddle.skia.org/c/@Path_moveTo + */ + SkPath& moveTo(SkScalar x, SkScalar y); + + /** Adds beginning of contour at SkPoint p. + + @param p contour start + @return reference to SkPath + */ + SkPath& moveTo(const SkPoint& p) { + return this->moveTo(p.fX, p.fY); + } + + /** Adds beginning of contour relative to last point. + If SkPath is empty, starts contour at (dx, dy). + Otherwise, start contour at last point offset by (dx, dy). + Function name stands for "relative move to". + + @param dx offset from last point to contour start on x-axis + @param dy offset from last point to contour start on y-axis + @return reference to SkPath + + example: https://fiddle.skia.org/c/@Path_rMoveTo + */ + SkPath& rMoveTo(SkScalar dx, SkScalar dy); + + /** Adds line from last point to (x, y). If SkPath is empty, or last SkPath::Verb is + kClose_Verb, last point is set to (0, 0) before adding line. + + lineTo() appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed. + lineTo() then appends kLine_Verb to verb array and (x, y) to SkPoint array. + + @param x end of added line on x-axis + @param y end of added line on y-axis + @return reference to SkPath + + example: https://fiddle.skia.org/c/@Path_lineTo + */ + SkPath& lineTo(SkScalar x, SkScalar y); + + /** Adds line from last point to SkPoint p. If SkPath is empty, or last SkPath::Verb is + kClose_Verb, last point is set to (0, 0) before adding line. + + lineTo() first appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed. + lineTo() then appends kLine_Verb to verb array and SkPoint p to SkPoint array. + + @param p end SkPoint of added line + @return reference to SkPath + */ + SkPath& lineTo(const SkPoint& p) { + return this->lineTo(p.fX, p.fY); + } + + /** Adds line from last point to vector (dx, dy). If SkPath is empty, or last SkPath::Verb is + kClose_Verb, last point is set to (0, 0) before adding line. + + Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed; + then appends kLine_Verb to verb array and line end to SkPoint array. + Line end is last point plus vector (dx, dy). + Function name stands for "relative line to". + + @param dx offset from last point to line end on x-axis + @param dy offset from last point to line end on y-axis + @return reference to SkPath + + example: https://fiddle.skia.org/c/@Path_rLineTo + example: https://fiddle.skia.org/c/@Quad_a + example: https://fiddle.skia.org/c/@Quad_b + */ + SkPath& rLineTo(SkScalar dx, SkScalar dy); + + /** Adds quad from last point towards (x1, y1), to (x2, y2). + If SkPath is empty, or last SkPath::Verb is kClose_Verb, last point is set to (0, 0) + before adding quad. + + Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed; + then appends kQuad_Verb to verb array; and (x1, y1), (x2, y2) + to SkPoint array. + + @param x1 control SkPoint of quad on x-axis + @param y1 control SkPoint of quad on y-axis + @param x2 end SkPoint of quad on x-axis + @param y2 end SkPoint of quad on y-axis + @return reference to SkPath + + example: https://fiddle.skia.org/c/@Path_quadTo + */ + SkPath& quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2); + + /** Adds quad from last point towards SkPoint p1, to SkPoint p2. + If SkPath is empty, or last SkPath::Verb is kClose_Verb, last point is set to (0, 0) + before adding quad. + + Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed; + then appends kQuad_Verb to verb array; and SkPoint p1, p2 + to SkPoint array. + + @param p1 control SkPoint of added quad + @param p2 end SkPoint of added quad + @return reference to SkPath + */ + SkPath& quadTo(const SkPoint& p1, const SkPoint& p2) { + return this->quadTo(p1.fX, p1.fY, p2.fX, p2.fY); + } + + /** Adds quad from last point towards vector (dx1, dy1), to vector (dx2, dy2). + If SkPath is empty, or last SkPath::Verb + is kClose_Verb, last point is set to (0, 0) before adding quad. + + Appends kMove_Verb to verb array and (0, 0) to SkPoint array, + if needed; then appends kQuad_Verb to verb array; and appends quad + control and quad end to SkPoint array. + Quad control is last point plus vector (dx1, dy1). + Quad end is last point plus vector (dx2, dy2). + Function name stands for "relative quad to". + + @param dx1 offset from last point to quad control on x-axis + @param dy1 offset from last point to quad control on y-axis + @param dx2 offset from last point to quad end on x-axis + @param dy2 offset from last point to quad end on y-axis + @return reference to SkPath + + example: https://fiddle.skia.org/c/@Conic_Weight_a + example: https://fiddle.skia.org/c/@Conic_Weight_b + example: https://fiddle.skia.org/c/@Conic_Weight_c + example: https://fiddle.skia.org/c/@Path_rQuadTo + */ + SkPath& rQuadTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2); + + /** Adds conic from last point towards (x1, y1), to (x2, y2), weighted by w. + If SkPath is empty, or last SkPath::Verb is kClose_Verb, last point is set to (0, 0) + before adding conic. + + Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed. + + If w is finite and not one, appends kConic_Verb to verb array; + and (x1, y1), (x2, y2) to SkPoint array; and w to conic weights. + + If w is one, appends kQuad_Verb to verb array, and + (x1, y1), (x2, y2) to SkPoint array. + + If w is not finite, appends kLine_Verb twice to verb array, and + (x1, y1), (x2, y2) to SkPoint array. + + @param x1 control SkPoint of conic on x-axis + @param y1 control SkPoint of conic on y-axis + @param x2 end SkPoint of conic on x-axis + @param y2 end SkPoint of conic on y-axis + @param w weight of added conic + @return reference to SkPath + */ + SkPath& conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, + SkScalar w); + + /** Adds conic from last point towards SkPoint p1, to SkPoint p2, weighted by w. + If SkPath is empty, or last SkPath::Verb is kClose_Verb, last point is set to (0, 0) + before adding conic. + + Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed. + + If w is finite and not one, appends kConic_Verb to verb array; + and SkPoint p1, p2 to SkPoint array; and w to conic weights. + + If w is one, appends kQuad_Verb to verb array, and SkPoint p1, p2 + to SkPoint array. + + If w is not finite, appends kLine_Verb twice to verb array, and + SkPoint p1, p2 to SkPoint array. + + @param p1 control SkPoint of added conic + @param p2 end SkPoint of added conic + @param w weight of added conic + @return reference to SkPath + */ + SkPath& conicTo(const SkPoint& p1, const SkPoint& p2, SkScalar w) { + return this->conicTo(p1.fX, p1.fY, p2.fX, p2.fY, w); + } + + /** Adds conic from last point towards vector (dx1, dy1), to vector (dx2, dy2), + weighted by w. If SkPath is empty, or last SkPath::Verb + is kClose_Verb, last point is set to (0, 0) before adding conic. + + Appends kMove_Verb to verb array and (0, 0) to SkPoint array, + if needed. + + If w is finite and not one, next appends kConic_Verb to verb array, + and w is recorded as conic weight; otherwise, if w is one, appends + kQuad_Verb to verb array; or if w is not finite, appends kLine_Verb + twice to verb array. + + In all cases appends SkPoint control and end to SkPoint array. + control is last point plus vector (dx1, dy1). + end is last point plus vector (dx2, dy2). + + Function name stands for "relative conic to". + + @param dx1 offset from last point to conic control on x-axis + @param dy1 offset from last point to conic control on y-axis + @param dx2 offset from last point to conic end on x-axis + @param dy2 offset from last point to conic end on y-axis + @param w weight of added conic + @return reference to SkPath + */ + SkPath& rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2, + SkScalar w); + + /** Adds cubic from last point towards (x1, y1), then towards (x2, y2), ending at + (x3, y3). If SkPath is empty, or last SkPath::Verb is kClose_Verb, last point is set to + (0, 0) before adding cubic. + + Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed; + then appends kCubic_Verb to verb array; and (x1, y1), (x2, y2), (x3, y3) + to SkPoint array. + + @param x1 first control SkPoint of cubic on x-axis + @param y1 first control SkPoint of cubic on y-axis + @param x2 second control SkPoint of cubic on x-axis + @param y2 second control SkPoint of cubic on y-axis + @param x3 end SkPoint of cubic on x-axis + @param y3 end SkPoint of cubic on y-axis + @return reference to SkPath + */ + SkPath& cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, + SkScalar x3, SkScalar y3); + + /** Adds cubic from last point towards SkPoint p1, then towards SkPoint p2, ending at + SkPoint p3. If SkPath is empty, or last SkPath::Verb is kClose_Verb, last point is set to + (0, 0) before adding cubic. + + Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed; + then appends kCubic_Verb to verb array; and SkPoint p1, p2, p3 + to SkPoint array. + + @param p1 first control SkPoint of cubic + @param p2 second control SkPoint of cubic + @param p3 end SkPoint of cubic + @return reference to SkPath + */ + SkPath& cubicTo(const SkPoint& p1, const SkPoint& p2, const SkPoint& p3) { + return this->cubicTo(p1.fX, p1.fY, p2.fX, p2.fY, p3.fX, p3.fY); + } + + /** Adds cubic from last point towards vector (dx1, dy1), then towards + vector (dx2, dy2), to vector (dx3, dy3). + If SkPath is empty, or last SkPath::Verb + is kClose_Verb, last point is set to (0, 0) before adding cubic. + + Appends kMove_Verb to verb array and (0, 0) to SkPoint array, + if needed; then appends kCubic_Verb to verb array; and appends cubic + control and cubic end to SkPoint array. + Cubic control is last point plus vector (dx1, dy1). + Cubic end is last point plus vector (dx2, dy2). + Function name stands for "relative cubic to". + + @param dx1 offset from last point to first cubic control on x-axis + @param dy1 offset from last point to first cubic control on y-axis + @param dx2 offset from last point to second cubic control on x-axis + @param dy2 offset from last point to second cubic control on y-axis + @param dx3 offset from last point to cubic end on x-axis + @param dy3 offset from last point to cubic end on y-axis + @return reference to SkPath + */ + SkPath& rCubicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2, + SkScalar dx3, SkScalar dy3); + + /** Appends arc to SkPath. Arc added is part of ellipse + bounded by oval, from startAngle through sweepAngle. Both startAngle and + sweepAngle are measured in degrees, where zero degrees is aligned with the + positive x-axis, and positive sweeps extends arc clockwise. + + arcTo() adds line connecting SkPath last SkPoint to initial arc SkPoint if forceMoveTo + is false and SkPath is not empty. Otherwise, added contour begins with first point + of arc. Angles greater than -360 and less than 360 are treated modulo 360. + + @param oval bounds of ellipse containing arc + @param startAngle starting angle of arc in degrees + @param sweepAngle sweep, in degrees. Positive is clockwise; treated modulo 360 + @param forceMoveTo true to start a new contour with arc + @return reference to SkPath + + example: https://fiddle.skia.org/c/@Path_arcTo + */ + SkPath& arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool forceMoveTo); + + /** Appends arc to SkPath, after appending line if needed. Arc is implemented by conic + weighted to describe part of circle. Arc is contained by tangent from + last SkPath point to (x1, y1), and tangent from (x1, y1) to (x2, y2). Arc + is part of circle sized to radius, positioned so it touches both tangent lines. + + If last Path Point does not start Arc, arcTo appends connecting Line to Path. + The length of Vector from (x1, y1) to (x2, y2) does not affect Arc. + + Arc sweep is always less than 180 degrees. If radius is zero, or if + tangents are nearly parallel, arcTo appends Line from last Path Point to (x1, y1). + + arcTo appends at most one Line and one conic. + arcTo implements the functionality of PostScript arct and HTML Canvas arcTo. + + @param x1 x-axis value common to pair of tangents + @param y1 y-axis value common to pair of tangents + @param x2 x-axis value end of second tangent + @param y2 y-axis value end of second tangent + @param radius distance from arc to circle center + @return reference to SkPath + + example: https://fiddle.skia.org/c/@Path_arcTo_2_a + example: https://fiddle.skia.org/c/@Path_arcTo_2_b + example: https://fiddle.skia.org/c/@Path_arcTo_2_c + */ + SkPath& arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius); + + /** Appends arc to SkPath, after appending line if needed. Arc is implemented by conic + weighted to describe part of circle. Arc is contained by tangent from + last SkPath point to p1, and tangent from p1 to p2. Arc + is part of circle sized to radius, positioned so it touches both tangent lines. + + If last SkPath SkPoint does not start arc, arcTo() appends connecting line to SkPath. + The length of vector from p1 to p2 does not affect arc. + + Arc sweep is always less than 180 degrees. If radius is zero, or if + tangents are nearly parallel, arcTo() appends line from last SkPath SkPoint to p1. + + arcTo() appends at most one line and one conic. + arcTo() implements the functionality of PostScript arct and HTML Canvas arcTo. + + @param p1 SkPoint common to pair of tangents + @param p2 end of second tangent + @param radius distance from arc to circle center + @return reference to SkPath + */ + SkPath& arcTo(const SkPoint p1, const SkPoint p2, SkScalar radius) { + return this->arcTo(p1.fX, p1.fY, p2.fX, p2.fY, radius); + } + + /** \enum SkPath::ArcSize + Four oval parts with radii (rx, ry) start at last SkPath SkPoint and ends at (x, y). + ArcSize and Direction select one of the four oval parts. + */ + enum ArcSize { + kSmall_ArcSize, //!< smaller of arc pair + kLarge_ArcSize, //!< larger of arc pair + }; + + /** Appends arc to SkPath. Arc is implemented by one or more conics weighted to + describe part of oval with radii (rx, ry) rotated by xAxisRotate degrees. Arc + curves from last SkPath SkPoint to (x, y), choosing one of four possible routes: + clockwise or counterclockwise, and smaller or larger. + + Arc sweep is always less than 360 degrees. arcTo() appends line to (x, y) if + either radii are zero, or if last SkPath SkPoint equals (x, y). arcTo() scales radii + (rx, ry) to fit last SkPath SkPoint and (x, y) if both are greater than zero but + too small. + + arcTo() appends up to four conic curves. + arcTo() implements the functionality of SVG arc, although SVG sweep-flag value + is opposite the integer value of sweep; SVG sweep-flag uses 1 for clockwise, + while kCW_Direction cast to int is zero. + + @param rx radius on x-axis before x-axis rotation + @param ry radius on y-axis before x-axis rotation + @param xAxisRotate x-axis rotation in degrees; positive values are clockwise + @param largeArc chooses smaller or larger arc + @param sweep chooses clockwise or counterclockwise arc + @param x end of arc + @param y end of arc + @return reference to SkPath + */ + SkPath& arcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc, + SkPathDirection sweep, SkScalar x, SkScalar y); + + /** Appends arc to SkPath. Arc is implemented by one or more conic weighted to describe + part of oval with radii (r.fX, r.fY) rotated by xAxisRotate degrees. Arc curves + from last SkPath SkPoint to (xy.fX, xy.fY), choosing one of four possible routes: + clockwise or counterclockwise, + and smaller or larger. + + Arc sweep is always less than 360 degrees. arcTo() appends line to xy if either + radii are zero, or if last SkPath SkPoint equals (xy.fX, xy.fY). arcTo() scales radii r to + fit last SkPath SkPoint and xy if both are greater than zero but too small to describe + an arc. + + arcTo() appends up to four conic curves. + arcTo() implements the functionality of SVG arc, although SVG sweep-flag value is + opposite the integer value of sweep; SVG sweep-flag uses 1 for clockwise, while + kCW_Direction cast to int is zero. + + @param r radii on axes before x-axis rotation + @param xAxisRotate x-axis rotation in degrees; positive values are clockwise + @param largeArc chooses smaller or larger arc + @param sweep chooses clockwise or counterclockwise arc + @param xy end of arc + @return reference to SkPath + */ + SkPath& arcTo(const SkPoint r, SkScalar xAxisRotate, ArcSize largeArc, SkPathDirection sweep, + const SkPoint xy) { + return this->arcTo(r.fX, r.fY, xAxisRotate, largeArc, sweep, xy.fX, xy.fY); + } + + /** Appends arc to SkPath, relative to last SkPath SkPoint. Arc is implemented by one or + more conic, weighted to describe part of oval with radii (rx, ry) rotated by + xAxisRotate degrees. Arc curves from last SkPath SkPoint to relative end SkPoint: + (dx, dy), choosing one of four possible routes: clockwise or + counterclockwise, and smaller or larger. If SkPath is empty, the start arc SkPoint + is (0, 0). + + Arc sweep is always less than 360 degrees. arcTo() appends line to end SkPoint + if either radii are zero, or if last SkPath SkPoint equals end SkPoint. + arcTo() scales radii (rx, ry) to fit last SkPath SkPoint and end SkPoint if both are + greater than zero but too small to describe an arc. + + arcTo() appends up to four conic curves. + arcTo() implements the functionality of svg arc, although SVG "sweep-flag" value is + opposite the integer value of sweep; SVG "sweep-flag" uses 1 for clockwise, while + kCW_Direction cast to int is zero. + + @param rx radius before x-axis rotation + @param ry radius before x-axis rotation + @param xAxisRotate x-axis rotation in degrees; positive values are clockwise + @param largeArc chooses smaller or larger arc + @param sweep chooses clockwise or counterclockwise arc + @param dx x-axis offset end of arc from last SkPath SkPoint + @param dy y-axis offset end of arc from last SkPath SkPoint + @return reference to SkPath + */ + SkPath& rArcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc, + SkPathDirection sweep, SkScalar dx, SkScalar dy); + + /** Appends kClose_Verb to SkPath. A closed contour connects the first and last SkPoint + with line, forming a continuous loop. Open and closed contour draw the same + with SkPaint::kFill_Style. With SkPaint::kStroke_Style, open contour draws + SkPaint::Cap at contour start and end; closed contour draws + SkPaint::Join at contour start and end. + + close() has no effect if SkPath is empty or last SkPath SkPath::Verb is kClose_Verb. + + @return reference to SkPath + + example: https://fiddle.skia.org/c/@Path_close + */ + SkPath& close(); + +#ifdef SK_HIDE_PATH_EDIT_METHODS +public: +#endif + + /** Approximates conic with quad array. Conic is constructed from start SkPoint p0, + control SkPoint p1, end SkPoint p2, and weight w. + Quad array is stored in pts; this storage is supplied by caller. + Maximum quad count is 2 to the pow2. + Every third point in array shares last SkPoint of previous quad and first SkPoint of + next quad. Maximum pts storage size is given by: + (1 + 2 * (1 << pow2)) * sizeof(SkPoint). + + Returns quad count used the approximation, which may be smaller + than the number requested. + + conic weight determines the amount of influence conic control point has on the curve. + w less than one represents an elliptical section. w greater than one represents + a hyperbolic section. w equal to one represents a parabolic section. + + Two quad curves are sufficient to approximate an elliptical conic with a sweep + of up to 90 degrees; in this case, set pow2 to one. + + @param p0 conic start SkPoint + @param p1 conic control SkPoint + @param p2 conic end SkPoint + @param w conic weight + @param pts storage for quad array + @param pow2 quad count, as power of two, normally 0 to 5 (1 to 32 quad curves) + @return number of quad curves written to pts + */ + static int ConvertConicToQuads(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2, + SkScalar w, SkPoint pts[], int pow2); + + /** Returns true if SkPath is equivalent to SkRect when filled. + If false: rect, isClosed, and direction are unchanged. + If true: rect, isClosed, and direction are written to if not nullptr. + + rect may be smaller than the SkPath bounds. SkPath bounds may include kMove_Verb points + that do not alter the area drawn by the returned rect. + + @param rect storage for bounds of SkRect; may be nullptr + @param isClosed storage set to true if SkPath is closed; may be nullptr + @param direction storage set to SkRect direction; may be nullptr + @return true if SkPath contains SkRect + + example: https://fiddle.skia.org/c/@Path_isRect + */ + bool isRect(SkRect* rect, bool* isClosed = nullptr, SkPathDirection* direction = nullptr) const; + +#ifdef SK_HIDE_PATH_EDIT_METHODS +private: +#endif + + /** Adds a new contour to the path, defined by the rect, and wound in the + specified direction. The verbs added to the path will be: + + kMove, kLine, kLine, kLine, kClose + + start specifies which corner to begin the contour: + 0: upper-left corner + 1: upper-right corner + 2: lower-right corner + 3: lower-left corner + + This start point also acts as the implied beginning of the subsequent, + contour, if it does not have an explicit moveTo(). e.g. + + path.addRect(...) + // if we don't say moveTo() here, we will use the rect's start point + path.lineTo(...) + + @param rect SkRect to add as a closed contour + @param dir SkPath::Direction to orient the new contour + @param start initial corner of SkRect to add + @return reference to SkPath + + example: https://fiddle.skia.org/c/@Path_addRect_2 + */ + SkPath& addRect(const SkRect& rect, SkPathDirection dir, unsigned start); + + SkPath& addRect(const SkRect& rect, SkPathDirection dir = SkPathDirection::kCW) { + return this->addRect(rect, dir, 0); + } + + SkPath& addRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom, + SkPathDirection dir = SkPathDirection::kCW) { + return this->addRect({left, top, right, bottom}, dir, 0); + } + + /** Adds oval to path, appending kMove_Verb, four kConic_Verb, and kClose_Verb. + Oval is upright ellipse bounded by SkRect oval with radii equal to half oval width + and half oval height. Oval begins at (oval.fRight, oval.centerY()) and continues + clockwise if dir is kCW_Direction, counterclockwise if dir is kCCW_Direction. + + @param oval bounds of ellipse added + @param dir SkPath::Direction to wind ellipse + @return reference to SkPath + + example: https://fiddle.skia.org/c/@Path_addOval + */ + SkPath& addOval(const SkRect& oval, SkPathDirection dir = SkPathDirection::kCW); + + /** Adds oval to SkPath, appending kMove_Verb, four kConic_Verb, and kClose_Verb. + Oval is upright ellipse bounded by SkRect oval with radii equal to half oval width + and half oval height. Oval begins at start and continues + clockwise if dir is kCW_Direction, counterclockwise if dir is kCCW_Direction. + + @param oval bounds of ellipse added + @param dir SkPath::Direction to wind ellipse + @param start index of initial point of ellipse + @return reference to SkPath + + example: https://fiddle.skia.org/c/@Path_addOval_2 + */ + SkPath& addOval(const SkRect& oval, SkPathDirection dir, unsigned start); + + /** Experimental, subject to change or removal. + + Adds an "open" oval to SkPath. This follows canvas2D semantics: The oval is not + a separate contour. If the path was empty, then kMove_Verb is appended. Otherwise, + kLine_Verb is appended. Four kConic_Verbs are appended. kClose_Verb is not appended. + */ + SkPath& addOpenOval(const SkRect& oval, SkPathDirection dir, unsigned start); + + /** Adds circle centered at (x, y) of size radius to SkPath, appending kMove_Verb, + four kConic_Verb, and kClose_Verb. Circle begins at: (x + radius, y), continuing + clockwise if dir is kCW_Direction, and counterclockwise if dir is kCCW_Direction. + + Has no effect if radius is zero or negative. + + @param x center of circle + @param y center of circle + @param radius distance from center to edge + @param dir SkPath::Direction to wind circle + @return reference to SkPath + */ + SkPath& addCircle(SkScalar x, SkScalar y, SkScalar radius, + SkPathDirection dir = SkPathDirection::kCW); + + /** Appends arc to SkPath, as the start of new contour. Arc added is part of ellipse + bounded by oval, from startAngle through sweepAngle. Both startAngle and + sweepAngle are measured in degrees, where zero degrees is aligned with the + positive x-axis, and positive sweeps extends arc clockwise. + + If sweepAngle <= -360, or sweepAngle >= 360; and startAngle modulo 90 is nearly + zero, append oval instead of arc. Otherwise, sweepAngle values are treated + modulo 360, and arc may or may not draw depending on numeric rounding. + + @param oval bounds of ellipse containing arc + @param startAngle starting angle of arc in degrees + @param sweepAngle sweep, in degrees. Positive is clockwise; treated modulo 360 + @return reference to SkPath + + example: https://fiddle.skia.org/c/@Path_addArc + */ + SkPath& addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle); + + /** Appends SkRRect to SkPath, creating a new closed contour. SkRRect has bounds + equal to rect; each corner is 90 degrees of an ellipse with radii (rx, ry). If + dir is kCW_Direction, SkRRect starts at top-left of the lower-left corner and + winds clockwise. If dir is kCCW_Direction, SkRRect starts at the bottom-left + of the upper-left corner and winds counterclockwise. + + If either rx or ry is too large, rx and ry are scaled uniformly until the + corners fit. If rx or ry is less than or equal to zero, addRoundRect() appends + SkRect rect to SkPath. + + After appending, SkPath may be empty, or may contain: SkRect, oval, or SkRRect. + + @param rect bounds of SkRRect + @param rx x-axis radius of rounded corners on the SkRRect + @param ry y-axis radius of rounded corners on the SkRRect + @param dir SkPath::Direction to wind SkRRect + @return reference to SkPath + */ + SkPath& addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, + SkPathDirection dir = SkPathDirection::kCW); + + /** Appends SkRRect to SkPath, creating a new closed contour. SkRRect has bounds + equal to rect; each corner is 90 degrees of an ellipse with radii from the + array. + + @param rect bounds of SkRRect + @param radii array of 8 SkScalar values, a radius pair for each corner + @param dir SkPath::Direction to wind SkRRect + @return reference to SkPath + */ + SkPath& addRoundRect(const SkRect& rect, const SkScalar radii[], + SkPathDirection dir = SkPathDirection::kCW); + + /** Adds rrect to SkPath, creating a new closed contour. If + dir is kCW_Direction, rrect starts at top-left of the lower-left corner and + winds clockwise. If dir is kCCW_Direction, rrect starts at the bottom-left + of the upper-left corner and winds counterclockwise. + + After appending, SkPath may be empty, or may contain: SkRect, oval, or SkRRect. + + @param rrect bounds and radii of rounded rectangle + @param dir SkPath::Direction to wind SkRRect + @return reference to SkPath + + example: https://fiddle.skia.org/c/@Path_addRRect + */ + SkPath& addRRect(const SkRRect& rrect, SkPathDirection dir = SkPathDirection::kCW); + + /** Adds rrect to SkPath, creating a new closed contour. If dir is kCW_Direction, rrect + winds clockwise; if dir is kCCW_Direction, rrect winds counterclockwise. + start determines the first point of rrect to add. + + @param rrect bounds and radii of rounded rectangle + @param dir SkPath::Direction to wind SkRRect + @param start index of initial point of SkRRect + @return reference to SkPath + + example: https://fiddle.skia.org/c/@Path_addRRect_2 + */ + SkPath& addRRect(const SkRRect& rrect, SkPathDirection dir, unsigned start); + + /** Adds contour created from line array, adding (count - 1) line segments. + Contour added starts at pts[0], then adds a line for every additional SkPoint + in pts array. If close is true, appends kClose_Verb to SkPath, connecting + pts[count - 1] and pts[0]. + + If count is zero, append kMove_Verb to path. + Has no effect if count is less than one. + + @param pts array of line sharing end and start SkPoint + @param count length of SkPoint array + @param close true to add line connecting contour end and start + @return reference to SkPath + + example: https://fiddle.skia.org/c/@Path_addPoly + */ + SkPath& addPoly(const SkPoint pts[], int count, bool close); + + /** Adds contour created from list. Contour added starts at list[0], then adds a line + for every additional SkPoint in list. If close is true, appends kClose_Verb to SkPath, + connecting last and first SkPoint in list. + + If list is empty, append kMove_Verb to path. + + @param list array of SkPoint + @param close true to add line connecting contour end and start + @return reference to SkPath + */ + SkPath& addPoly(const std::initializer_list& list, bool close) { + return this->addPoly(list.begin(), SkToInt(list.size()), close); + } + +#ifdef SK_HIDE_PATH_EDIT_METHODS +public: +#endif + + /** \enum SkPath::AddPathMode + AddPathMode chooses how addPath() appends. Adding one SkPath to another can extend + the last contour or start a new contour. + */ + enum AddPathMode { + /** Contours are appended to the destination path as new contours. + */ + kAppend_AddPathMode, + /** Extends the last contour of the destination path with the first countour + of the source path, connecting them with a line. If the last contour is + closed, a new empty contour starting at its start point is extended instead. + If the destination path is empty, the result is the source path. + The last path of the result is closed only if the last path of the source is. + */ + kExtend_AddPathMode, + }; + + /** Appends src to SkPath, offset by (dx, dy). + + If mode is kAppend_AddPathMode, src verb array, SkPoint array, and conic weights are + added unaltered. If mode is kExtend_AddPathMode, add line before appending + verbs, SkPoint, and conic weights. + + @param src SkPath verbs, SkPoint, and conic weights to add + @param dx offset added to src SkPoint array x-axis coordinates + @param dy offset added to src SkPoint array y-axis coordinates + @param mode kAppend_AddPathMode or kExtend_AddPathMode + @return reference to SkPath + */ + SkPath& addPath(const SkPath& src, SkScalar dx, SkScalar dy, + AddPathMode mode = kAppend_AddPathMode); + + /** Appends src to SkPath. + + If mode is kAppend_AddPathMode, src verb array, SkPoint array, and conic weights are + added unaltered. If mode is kExtend_AddPathMode, add line before appending + verbs, SkPoint, and conic weights. + + @param src SkPath verbs, SkPoint, and conic weights to add + @param mode kAppend_AddPathMode or kExtend_AddPathMode + @return reference to SkPath + */ + SkPath& addPath(const SkPath& src, AddPathMode mode = kAppend_AddPathMode) { + SkMatrix m; + m.reset(); + return this->addPath(src, m, mode); + } + + /** Appends src to SkPath, transformed by matrix. Transformed curves may have different + verbs, SkPoint, and conic weights. + + If mode is kAppend_AddPathMode, src verb array, SkPoint array, and conic weights are + added unaltered. If mode is kExtend_AddPathMode, add line before appending + verbs, SkPoint, and conic weights. + + @param src SkPath verbs, SkPoint, and conic weights to add + @param matrix transform applied to src + @param mode kAppend_AddPathMode or kExtend_AddPathMode + @return reference to SkPath + */ + SkPath& addPath(const SkPath& src, const SkMatrix& matrix, + AddPathMode mode = kAppend_AddPathMode); + + /** Appends src to SkPath, from back to front. + Reversed src always appends a new contour to SkPath. + + @param src SkPath verbs, SkPoint, and conic weights to add + @return reference to SkPath + + example: https://fiddle.skia.org/c/@Path_reverseAddPath + */ + SkPath& reverseAddPath(const SkPath& src); + + /** Offsets SkPoint array by (dx, dy). Offset SkPath replaces dst. + If dst is nullptr, SkPath is replaced by offset data. + + @param dx offset added to SkPoint array x-axis coordinates + @param dy offset added to SkPoint array y-axis coordinates + @param dst overwritten, translated copy of SkPath; may be nullptr + + example: https://fiddle.skia.org/c/@Path_offset + */ + void offset(SkScalar dx, SkScalar dy, SkPath* dst) const; + + /** Offsets SkPoint array by (dx, dy). SkPath is replaced by offset data. + + @param dx offset added to SkPoint array x-axis coordinates + @param dy offset added to SkPoint array y-axis coordinates + */ + SkPath& offset(SkScalar dx, SkScalar dy) { + this->offset(dx, dy, this); + return *this; + } + + /** Transforms verb array, SkPoint array, and weight by matrix. + transform may change verbs and increase their number. + Transformed SkPath replaces dst; if dst is nullptr, original data + is replaced. + + @param matrix SkMatrix to apply to SkPath + @param dst overwritten, transformed copy of SkPath; may be nullptr + @param pc whether to apply perspective clipping + + example: https://fiddle.skia.org/c/@Path_transform + */ + void transform(const SkMatrix& matrix, SkPath* dst, + SkApplyPerspectiveClip pc = SkApplyPerspectiveClip::kYes) const; + + /** Transforms verb array, SkPoint array, and weight by matrix. + transform may change verbs and increase their number. + SkPath is replaced by transformed data. + + @param matrix SkMatrix to apply to SkPath + @param pc whether to apply perspective clipping + */ + SkPath& transform(const SkMatrix& matrix, + SkApplyPerspectiveClip pc = SkApplyPerspectiveClip::kYes) { + this->transform(matrix, this, pc); + return *this; + } + + SkPath makeTransform(const SkMatrix& m, + SkApplyPerspectiveClip pc = SkApplyPerspectiveClip::kYes) const { + SkPath dst; + this->transform(m, &dst, pc); + return dst; + } + + SkPath makeScale(SkScalar sx, SkScalar sy) { + return this->makeTransform(SkMatrix::Scale(sx, sy), SkApplyPerspectiveClip::kNo); + } + + /** Returns last point on SkPath in lastPt. Returns false if SkPoint array is empty, + storing (0, 0) if lastPt is not nullptr. + + @param lastPt storage for final SkPoint in SkPoint array; may be nullptr + @return true if SkPoint array contains one or more SkPoint + + example: https://fiddle.skia.org/c/@Path_getLastPt + */ + bool getLastPt(SkPoint* lastPt) const; + + /** Sets last point to (x, y). If SkPoint array is empty, append kMove_Verb to + verb array and append (x, y) to SkPoint array. + + @param x set x-axis value of last point + @param y set y-axis value of last point + + example: https://fiddle.skia.org/c/@Path_setLastPt + */ + void setLastPt(SkScalar x, SkScalar y); + + /** Sets the last point on the path. If SkPoint array is empty, append kMove_Verb to + verb array and append p to SkPoint array. + + @param p set value of last point + */ + void setLastPt(const SkPoint& p) { + this->setLastPt(p.fX, p.fY); + } + + /** \enum SkPath::SegmentMask + SegmentMask constants correspond to each drawing Verb type in SkPath; for + instance, if SkPath only contains lines, only the kLine_SegmentMask bit is set. + */ + enum SegmentMask { + kLine_SegmentMask = kLine_SkPathSegmentMask, + kQuad_SegmentMask = kQuad_SkPathSegmentMask, + kConic_SegmentMask = kConic_SkPathSegmentMask, + kCubic_SegmentMask = kCubic_SkPathSegmentMask, + }; + + /** Returns a mask, where each set bit corresponds to a SegmentMask constant + if SkPath contains one or more verbs of that type. + Returns zero if SkPath contains no lines, or curves: quads, conics, or cubics. + + getSegmentMasks() returns a cached result; it is very fast. + + @return SegmentMask bits or zero + */ + uint32_t getSegmentMasks() const; + + /** \enum SkPath::Verb + Verb instructs SkPath how to interpret one or more SkPoint and optional conic weight; + manage contour, and terminate SkPath. + */ + enum Verb { + kMove_Verb = static_cast(SkPathVerb::kMove), + kLine_Verb = static_cast(SkPathVerb::kLine), + kQuad_Verb = static_cast(SkPathVerb::kQuad), + kConic_Verb = static_cast(SkPathVerb::kConic), + kCubic_Verb = static_cast(SkPathVerb::kCubic), + kClose_Verb = static_cast(SkPathVerb::kClose), + kDone_Verb = kClose_Verb + 1 + }; + + /** \class SkPath::Iter + Iterates through verb array, and associated SkPoint array and conic weight. + Provides options to treat open contours as closed, and to ignore + degenerate data. + */ + class SK_API Iter { + public: + + /** Initializes SkPath::Iter with an empty SkPath. next() on SkPath::Iter returns + kDone_Verb. + Call setPath to initialize SkPath::Iter at a later time. + + @return SkPath::Iter of empty SkPath + + example: https://fiddle.skia.org/c/@Path_Iter_Iter + */ + Iter(); + + /** Sets SkPath::Iter to return elements of verb array, SkPoint array, and conic weight in + path. If forceClose is true, SkPath::Iter will add kLine_Verb and kClose_Verb after each + open contour. path is not altered. + + @param path SkPath to iterate + @param forceClose true if open contours generate kClose_Verb + @return SkPath::Iter of path + + example: https://fiddle.skia.org/c/@Path_Iter_const_SkPath + */ + Iter(const SkPath& path, bool forceClose); + + /** Sets SkPath::Iter to return elements of verb array, SkPoint array, and conic weight in + path. If forceClose is true, SkPath::Iter will add kLine_Verb and kClose_Verb after each + open contour. path is not altered. + + @param path SkPath to iterate + @param forceClose true if open contours generate kClose_Verb + + example: https://fiddle.skia.org/c/@Path_Iter_setPath + */ + void setPath(const SkPath& path, bool forceClose); + + /** Returns next SkPath::Verb in verb array, and advances SkPath::Iter. + When verb array is exhausted, returns kDone_Verb. + + Zero to four SkPoint are stored in pts, depending on the returned SkPath::Verb. + + @param pts storage for SkPoint data describing returned SkPath::Verb + @return next SkPath::Verb from verb array + + example: https://fiddle.skia.org/c/@Path_RawIter_next + */ + Verb next(SkPoint pts[4]); + + /** Returns conic weight if next() returned kConic_Verb. + + If next() has not been called, or next() did not return kConic_Verb, + result is undefined. + + @return conic weight for conic SkPoint returned by next() + */ + SkScalar conicWeight() const { return *fConicWeights; } + + /** Returns true if last kLine_Verb returned by next() was generated + by kClose_Verb. When true, the end point returned by next() is + also the start point of contour. + + If next() has not been called, or next() did not return kLine_Verb, + result is undefined. + + @return true if last kLine_Verb was generated by kClose_Verb + */ + bool isCloseLine() const { return SkToBool(fCloseLine); } + + /** Returns true if subsequent calls to next() return kClose_Verb before returning + kMove_Verb. if true, contour SkPath::Iter is processing may end with kClose_Verb, or + SkPath::Iter may have been initialized with force close set to true. + + @return true if contour is closed + + example: https://fiddle.skia.org/c/@Path_Iter_isClosedContour + */ + bool isClosedContour() const; + + private: + const SkPoint* fPts; + const uint8_t* fVerbs; + const uint8_t* fVerbStop; + const SkScalar* fConicWeights; + SkPoint fMoveTo; + SkPoint fLastPt; + bool fForceClose; + bool fNeedClose; + bool fCloseLine; + + Verb autoClose(SkPoint pts[2]); + }; + +private: + /** \class SkPath::RangeIter + Iterates through a raw range of path verbs, points, and conics. All values are returned + unaltered. + + NOTE: This class will be moved into SkPathPriv once RangeIter is removed. + */ + class RangeIter { + public: + RangeIter() = default; + RangeIter(const uint8_t* verbs, const SkPoint* points, const SkScalar* weights) + : fVerb(verbs), fPoints(points), fWeights(weights) { + SkDEBUGCODE(fInitialPoints = fPoints;) + } + bool operator!=(const RangeIter& that) const { + return fVerb != that.fVerb; + } + bool operator==(const RangeIter& that) const { + return fVerb == that.fVerb; + } + RangeIter& operator++() { + auto verb = static_cast(*fVerb++); + fPoints += pts_advance_after_verb(verb); + if (verb == SkPathVerb::kConic) { + ++fWeights; + } + return *this; + } + RangeIter operator++(int) { + RangeIter copy = *this; + this->operator++(); + return copy; + } + SkPathVerb peekVerb() const { + return static_cast(*fVerb); + } + std::tuple operator*() const { + SkPathVerb verb = this->peekVerb(); + // We provide the starting point for beziers by peeking backwards from the current + // point, which works fine as long as there is always a kMove before any geometry. + // (SkPath::injectMoveToIfNeeded should have guaranteed this to be the case.) + int backset = pts_backset_for_verb(verb); + SkASSERT(fPoints + backset >= fInitialPoints); + return {verb, fPoints + backset, fWeights}; + } + private: + constexpr static int pts_advance_after_verb(SkPathVerb verb) { + switch (verb) { + case SkPathVerb::kMove: return 1; + case SkPathVerb::kLine: return 1; + case SkPathVerb::kQuad: return 2; + case SkPathVerb::kConic: return 2; + case SkPathVerb::kCubic: return 3; + case SkPathVerb::kClose: return 0; + } + SkUNREACHABLE; + } + constexpr static int pts_backset_for_verb(SkPathVerb verb) { + switch (verb) { + case SkPathVerb::kMove: return 0; + case SkPathVerb::kLine: return -1; + case SkPathVerb::kQuad: return -1; + case SkPathVerb::kConic: return -1; + case SkPathVerb::kCubic: return -1; + case SkPathVerb::kClose: return -1; + } + SkUNREACHABLE; + } + const uint8_t* fVerb = nullptr; + const SkPoint* fPoints = nullptr; + const SkScalar* fWeights = nullptr; + SkDEBUGCODE(const SkPoint* fInitialPoints = nullptr;) + }; +public: + + /** \class SkPath::RawIter + Use Iter instead. This class will soon be removed and RangeIter will be made private. + */ + class SK_API RawIter { + public: + + /** Initializes RawIter with an empty SkPath. next() on RawIter returns kDone_Verb. + Call setPath to initialize SkPath::Iter at a later time. + + @return RawIter of empty SkPath + */ + RawIter() {} + + /** Sets RawIter to return elements of verb array, SkPoint array, and conic weight in path. + + @param path SkPath to iterate + @return RawIter of path + */ + RawIter(const SkPath& path) { + setPath(path); + } + + /** Sets SkPath::Iter to return elements of verb array, SkPoint array, and conic weight in + path. + + @param path SkPath to iterate + */ + void setPath(const SkPath&); + + /** Returns next SkPath::Verb in verb array, and advances RawIter. + When verb array is exhausted, returns kDone_Verb. + Zero to four SkPoint are stored in pts, depending on the returned SkPath::Verb. + + @param pts storage for SkPoint data describing returned SkPath::Verb + @return next SkPath::Verb from verb array + */ + Verb next(SkPoint[4]); + + /** Returns next SkPath::Verb, but does not advance RawIter. + + @return next SkPath::Verb from verb array + */ + Verb peek() const { + return (fIter != fEnd) ? static_cast(std::get<0>(*fIter)) : kDone_Verb; + } + + /** Returns conic weight if next() returned kConic_Verb. + + If next() has not been called, or next() did not return kConic_Verb, + result is undefined. + + @return conic weight for conic SkPoint returned by next() + */ + SkScalar conicWeight() const { + return fConicWeight; + } + + private: + RangeIter fIter; + RangeIter fEnd; + SkScalar fConicWeight = 0; + friend class SkPath; + + }; + + /** Returns true if the point (x, y) is contained by SkPath, taking into + account FillType. + + @param x x-axis value of containment test + @param y y-axis value of containment test + @return true if SkPoint is in SkPath + + example: https://fiddle.skia.org/c/@Path_contains + */ + bool contains(SkScalar x, SkScalar y) const; + + /** Writes text representation of SkPath to stream. If stream is nullptr, writes to + standard output. Set dumpAsHex true to generate exact binary representations + of floating point numbers used in SkPoint array and conic weights. + + @param stream writable SkWStream receiving SkPath text representation; may be nullptr + @param dumpAsHex true if SkScalar values are written as hexadecimal + + example: https://fiddle.skia.org/c/@Path_dump + */ + void dump(SkWStream* stream, bool dumpAsHex) const; + + void dump() const { this->dump(nullptr, false); } + void dumpHex() const { this->dump(nullptr, true); } + + // Like dump(), but outputs for the SkPath::Make() factory + void dumpArrays(SkWStream* stream, bool dumpAsHex) const; + void dumpArrays() const { this->dumpArrays(nullptr, false); } + + /** Writes SkPath to buffer, returning the number of bytes written. + Pass nullptr to obtain the storage size. + + Writes SkPath::FillType, verb array, SkPoint array, conic weight, and + additionally writes computed information like SkPath::Convexity and bounds. + + Use only be used in concert with readFromMemory(); + the format used for SkPath in memory is not guaranteed. + + @param buffer storage for SkPath; may be nullptr + @return size of storage required for SkPath; always a multiple of 4 + + example: https://fiddle.skia.org/c/@Path_writeToMemory + */ + size_t writeToMemory(void* buffer) const; + + /** Writes SkPath to buffer, returning the buffer written to, wrapped in SkData. + + serialize() writes SkPath::FillType, verb array, SkPoint array, conic weight, and + additionally writes computed information like SkPath::Convexity and bounds. + + serialize() should only be used in concert with readFromMemory(). + The format used for SkPath in memory is not guaranteed. + + @return SkPath data wrapped in SkData buffer + + example: https://fiddle.skia.org/c/@Path_serialize + */ + sk_sp serialize() const; + + /** Initializes SkPath from buffer of size length. Returns zero if the buffer is + data is inconsistent, or the length is too small. + + Reads SkPath::FillType, verb array, SkPoint array, conic weight, and + additionally reads computed information like SkPath::Convexity and bounds. + + Used only in concert with writeToMemory(); + the format used for SkPath in memory is not guaranteed. + + @param buffer storage for SkPath + @param length buffer size in bytes; must be multiple of 4 + @return number of bytes read, or zero on failure + + example: https://fiddle.skia.org/c/@Path_readFromMemory + */ + size_t readFromMemory(const void* buffer, size_t length); + + /** (See Skia bug 1762.) + Returns a non-zero, globally unique value. A different value is returned + if verb array, SkPoint array, or conic weight changes. + + Setting SkPath::FillType does not change generation identifier. + + Each time the path is modified, a different generation identifier will be returned. + SkPath::FillType does affect generation identifier on Android framework. + + @return non-zero, globally unique value + + example: https://fiddle.skia.org/c/@Path_getGenerationID + */ + uint32_t getGenerationID() const; + + /** Returns if SkPath data is consistent. Corrupt SkPath data is detected if + internal values are out of range or internal storage does not match + array dimensions. + + @return true if SkPath data is consistent + */ + bool isValid() const; + + using sk_is_trivially_relocatable = std::true_type; + +private: + SkPath(sk_sp, SkPathFillType, bool isVolatile, SkPathConvexity, + SkPathFirstDirection firstDirection); + + sk_sp fPathRef; + int fLastMoveToIndex; + mutable std::atomic fConvexity; // SkPathConvexity + mutable std::atomic fFirstDirection; // SkPathFirstDirection + uint8_t fFillType : 2; + uint8_t fIsVolatile : 1; + + static_assert(::sk_is_trivially_relocatable::value); + + /** Resets all fields other than fPathRef to their initial 'empty' values. + * Assumes the caller has already emptied fPathRef. + */ + void resetFields(); + + /** Sets all fields other than fPathRef to the values in 'that'. + * Assumes the caller has already set fPathRef. + * Doesn't change fGenerationID or fSourcePath on Android. + */ + void copyFields(const SkPath& that); + + size_t writeToMemoryAsRRect(void* buffer) const; + size_t readAsRRect(const void*, size_t); + size_t readFromMemory_EQ4Or5(const void*, size_t); + + friend class Iter; + friend class SkPathPriv; + friend class SkPathStroker; + + /* Append, in reverse order, the first contour of path, ignoring path's + last point. If no moveTo() call has been made for this contour, the + first point is automatically set to (0,0). + */ + SkPath& reversePathTo(const SkPath&); + + // called before we add points for lineTo, quadTo, cubicTo, checking to see + // if we need to inject a leading moveTo first + // + // SkPath path; path.lineTo(...); <--- need a leading moveTo(0, 0) + // SkPath path; ... path.close(); path.lineTo(...) <-- need a moveTo(previous moveTo) + // + inline void injectMoveToIfNeeded(); + + inline bool hasOnlyMoveTos() const; + + SkPathConvexity computeConvexity() const; + + bool isValidImpl() const; + /** Asserts if SkPath data is inconsistent. + Debugging check intended for internal use only. + */ +#ifdef SK_DEBUG + void validate() const; + void validateRef() const; +#endif + + // called by stroker to see if all points (in the last contour) are equal and worthy of a cap + bool isZeroLengthSincePoint(int startPtIndex) const; + + /** Returns if the path can return a bound at no cost (true) or will have to + perform some computation (false). + */ + bool hasComputedBounds() const; + + // 'rect' needs to be sorted + void setBounds(const SkRect& rect); + + void setPt(int index, SkScalar x, SkScalar y); + + SkPath& dirtyAfterEdit(); + + // Bottlenecks for working with fConvexity and fFirstDirection. + // Notice the setters are const... these are mutable atomic fields. + void setConvexity(SkPathConvexity) const; + + void setFirstDirection(SkPathFirstDirection) const; + SkPathFirstDirection getFirstDirection() const; + + /** Returns the comvexity type, computing if needed. Never returns kUnknown. + @return path's convexity type (convex or concave) + */ + SkPathConvexity getConvexity() const; + + SkPathConvexity getConvexityOrUnknown() const; + + // Compares the cached value with a freshly computed one (computeConvexity()) + bool isConvexityAccurate() const; + + /** Stores a convexity type for this path. This is what will be returned if + * getConvexityOrUnknown() is called. If you pass kUnknown, then if getContexityType() + * is called, the real convexity will be computed. + * + * example: https://fiddle.skia.org/c/@Path_setConvexity + */ + void setConvexity(SkPathConvexity convexity); + + /** Shrinks SkPath verb array and SkPoint array storage to discard unused capacity. + * May reduce the heap overhead for SkPath known to be fully constructed. + * + * NOTE: This may relocate the underlying buffers, and thus any Iterators referencing + * this path should be discarded after calling shrinkToFit(). + */ + void shrinkToFit(); + + // Creates a new Path after the supplied arguments have been validated by + // sk_path_analyze_verbs(). + static SkPath MakeInternal(const SkPathVerbAnalysis& analsis, + const SkPoint points[], + const uint8_t verbs[], + int verbCount, + const SkScalar conics[], + SkPathFillType fillType, + bool isVolatile); + + friend class SkAutoPathBoundsUpdate; + friend class SkAutoDisableOvalCheck; + friend class SkAutoDisableDirectionCheck; + friend class SkPathBuilder; + friend class SkPathEdgeIter; + friend class SkPathWriter; + friend class SkOpBuilder; + friend class SkBench_AddPathTest; // perf test reversePathTo + friend class PathTest_Private; // unit test reversePathTo + friend class ForceIsRRect_Private; // unit test isRRect + friend class FuzzPath; // for legacy access to validateRef +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPathBuilder.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPathBuilder.h new file mode 100644 index 0000000000..247c08624c --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPathBuilder.h @@ -0,0 +1,271 @@ +/* + * 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 SkPathBuilder_DEFINED +#define SkPathBuilder_DEFINED + +#include "include/core/SkPath.h" +#include "include/core/SkPathTypes.h" +#include "include/core/SkPoint.h" +#include "include/core/SkRect.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" +#include "include/private/SkPathRef.h" +#include "include/private/base/SkTo.h" + +#include + +class SkRRect; + +class SK_API SkPathBuilder { +public: + SkPathBuilder(); + SkPathBuilder(SkPathFillType); + SkPathBuilder(const SkPath&); + SkPathBuilder(const SkPathBuilder&) = default; + ~SkPathBuilder(); + + SkPathBuilder& operator=(const SkPath&); + SkPathBuilder& operator=(const SkPathBuilder&) = default; + + SkPathFillType fillType() const { return fFillType; } + SkRect computeBounds() const; + + SkPath snapshot() const; // the builder is unchanged after returning this path + SkPath detach(); // the builder is reset to empty after returning this path + + SkPathBuilder& setFillType(SkPathFillType ft) { fFillType = ft; return *this; } + SkPathBuilder& setIsVolatile(bool isVolatile) { fIsVolatile = isVolatile; return *this; } + + SkPathBuilder& reset(); + + SkPathBuilder& moveTo(SkPoint pt); + SkPathBuilder& moveTo(SkScalar x, SkScalar y) { return this->moveTo(SkPoint::Make(x, y)); } + + SkPathBuilder& lineTo(SkPoint pt); + SkPathBuilder& lineTo(SkScalar x, SkScalar y) { return this->lineTo(SkPoint::Make(x, y)); } + + SkPathBuilder& quadTo(SkPoint pt1, SkPoint pt2); + SkPathBuilder& quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) { + return this->quadTo(SkPoint::Make(x1, y1), SkPoint::Make(x2, y2)); + } + SkPathBuilder& quadTo(const SkPoint pts[2]) { return this->quadTo(pts[0], pts[1]); } + + SkPathBuilder& conicTo(SkPoint pt1, SkPoint pt2, SkScalar w); + SkPathBuilder& conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar w) { + return this->conicTo(SkPoint::Make(x1, y1), SkPoint::Make(x2, y2), w); + } + SkPathBuilder& conicTo(const SkPoint pts[2], SkScalar w) { + return this->conicTo(pts[0], pts[1], w); + } + + SkPathBuilder& cubicTo(SkPoint pt1, SkPoint pt2, SkPoint pt3); + SkPathBuilder& cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar x3, SkScalar y3) { + return this->cubicTo(SkPoint::Make(x1, y1), SkPoint::Make(x2, y2), SkPoint::Make(x3, y3)); + } + SkPathBuilder& cubicTo(const SkPoint pts[3]) { + return this->cubicTo(pts[0], pts[1], pts[2]); + } + + SkPathBuilder& close(); + + // Append a series of lineTo(...) + SkPathBuilder& polylineTo(const SkPoint pts[], int count); + SkPathBuilder& polylineTo(const std::initializer_list& list) { + return this->polylineTo(list.begin(), SkToInt(list.size())); + } + + // Relative versions of segments, relative to the previous position. + + SkPathBuilder& rLineTo(SkPoint pt); + SkPathBuilder& rLineTo(SkScalar x, SkScalar y) { return this->rLineTo({x, y}); } + SkPathBuilder& rQuadTo(SkPoint pt1, SkPoint pt2); + SkPathBuilder& rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) { + return this->rQuadTo({x1, y1}, {x2, y2}); + } + SkPathBuilder& rConicTo(SkPoint p1, SkPoint p2, SkScalar w); + SkPathBuilder& rConicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar w) { + return this->rConicTo({x1, y1}, {x2, y2}, w); + } + SkPathBuilder& rCubicTo(SkPoint pt1, SkPoint pt2, SkPoint pt3); + SkPathBuilder& rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar x3, SkScalar y3) { + return this->rCubicTo({x1, y1}, {x2, y2}, {x3, y3}); + } + + // Arcs + + /** Appends arc to the builder. Arc added is part of ellipse + bounded by oval, from startAngle through sweepAngle. Both startAngle and + sweepAngle are measured in degrees, where zero degrees is aligned with the + positive x-axis, and positive sweeps extends arc clockwise. + + arcTo() adds line connecting the builder's last point to initial arc point if forceMoveTo + is false and the builder is not empty. Otherwise, added contour begins with first point + of arc. Angles greater than -360 and less than 360 are treated modulo 360. + + @param oval bounds of ellipse containing arc + @param startAngleDeg starting angle of arc in degrees + @param sweepAngleDeg sweep, in degrees. Positive is clockwise; treated modulo 360 + @param forceMoveTo true to start a new contour with arc + @return reference to the builder + */ + SkPathBuilder& arcTo(const SkRect& oval, SkScalar startAngleDeg, SkScalar sweepAngleDeg, + bool forceMoveTo); + + /** Appends arc to SkPath, after appending line if needed. Arc is implemented by conic + weighted to describe part of circle. Arc is contained by tangent from + last SkPath point to p1, and tangent from p1 to p2. Arc + is part of circle sized to radius, positioned so it touches both tangent lines. + + If last SkPath SkPoint does not start arc, arcTo() appends connecting line to SkPath. + The length of vector from p1 to p2 does not affect arc. + + Arc sweep is always less than 180 degrees. If radius is zero, or if + tangents are nearly parallel, arcTo() appends line from last SkPath SkPoint to p1. + + arcTo() appends at most one line and one conic. + arcTo() implements the functionality of PostScript arct and HTML Canvas arcTo. + + @param p1 SkPoint common to pair of tangents + @param p2 end of second tangent + @param radius distance from arc to circle center + @return reference to SkPath + */ + SkPathBuilder& arcTo(SkPoint p1, SkPoint p2, SkScalar radius); + + enum ArcSize { + kSmall_ArcSize, //!< smaller of arc pair + kLarge_ArcSize, //!< larger of arc pair + }; + + /** Appends arc to SkPath. Arc is implemented by one or more conic weighted to describe + part of oval with radii (r.fX, r.fY) rotated by xAxisRotate degrees. Arc curves + from last SkPath SkPoint to (xy.fX, xy.fY), choosing one of four possible routes: + clockwise or counterclockwise, + and smaller or larger. + + Arc sweep is always less than 360 degrees. arcTo() appends line to xy if either + radii are zero, or if last SkPath SkPoint equals (xy.fX, xy.fY). arcTo() scales radii r to + fit last SkPath SkPoint and xy if both are greater than zero but too small to describe + an arc. + + arcTo() appends up to four conic curves. + arcTo() implements the functionality of SVG arc, although SVG sweep-flag value is + opposite the integer value of sweep; SVG sweep-flag uses 1 for clockwise, while + kCW_Direction cast to int is zero. + + @param r radii on axes before x-axis rotation + @param xAxisRotate x-axis rotation in degrees; positive values are clockwise + @param largeArc chooses smaller or larger arc + @param sweep chooses clockwise or counterclockwise arc + @param xy end of arc + @return reference to SkPath + */ + SkPathBuilder& arcTo(SkPoint r, SkScalar xAxisRotate, ArcSize largeArc, SkPathDirection sweep, + SkPoint xy); + + /** Appends arc to the builder, as the start of new contour. Arc added is part of ellipse + bounded by oval, from startAngle through sweepAngle. Both startAngle and + sweepAngle are measured in degrees, where zero degrees is aligned with the + positive x-axis, and positive sweeps extends arc clockwise. + + If sweepAngle <= -360, or sweepAngle >= 360; and startAngle modulo 90 is nearly + zero, append oval instead of arc. Otherwise, sweepAngle values are treated + modulo 360, and arc may or may not draw depending on numeric rounding. + + @param oval bounds of ellipse containing arc + @param startAngleDeg starting angle of arc in degrees + @param sweepAngleDeg sweep, in degrees. Positive is clockwise; treated modulo 360 + @return reference to this builder + */ + SkPathBuilder& addArc(const SkRect& oval, SkScalar startAngleDeg, SkScalar sweepAngleDeg); + + // Add a new contour + + SkPathBuilder& addRect(const SkRect&, SkPathDirection, unsigned startIndex); + SkPathBuilder& addOval(const SkRect&, SkPathDirection, unsigned startIndex); + SkPathBuilder& addRRect(const SkRRect&, SkPathDirection, unsigned startIndex); + + SkPathBuilder& addRect(const SkRect& rect, SkPathDirection dir = SkPathDirection::kCW) { + return this->addRect(rect, dir, 0); + } + SkPathBuilder& addOval(const SkRect& rect, SkPathDirection dir = SkPathDirection::kCW) { + // legacy start index: 1 + return this->addOval(rect, dir, 1); + } + SkPathBuilder& addRRect(const SkRRect& rrect, SkPathDirection dir = SkPathDirection::kCW) { + // legacy start indices: 6 (CW) and 7 (CCW) + return this->addRRect(rrect, dir, dir == SkPathDirection::kCW ? 6 : 7); + } + + SkPathBuilder& addCircle(SkScalar center_x, SkScalar center_y, SkScalar radius, + SkPathDirection dir = SkPathDirection::kCW); + + SkPathBuilder& addPolygon(const SkPoint pts[], int count, bool isClosed); + SkPathBuilder& addPolygon(const std::initializer_list& list, bool isClosed) { + return this->addPolygon(list.begin(), SkToInt(list.size()), isClosed); + } + + SkPathBuilder& addPath(const SkPath&); + + // Performance hint, to reserve extra storage for subsequent calls to lineTo, quadTo, etc. + + void incReserve(int extraPtCount, int extraVerbCount); + void incReserve(int extraPtCount) { + this->incReserve(extraPtCount, extraPtCount); + } + + SkPathBuilder& offset(SkScalar dx, SkScalar dy); + + SkPathBuilder& toggleInverseFillType() { + fFillType = (SkPathFillType)((unsigned)fFillType ^ 2); + return *this; + } + +private: + SkPathRef::PointsArray fPts; + SkPathRef::VerbsArray fVerbs; + SkPathRef::ConicWeightsArray fConicWeights; + + SkPathFillType fFillType; + bool fIsVolatile; + + unsigned fSegmentMask; + SkPoint fLastMovePoint; + int fLastMoveIndex; // only needed until SkPath is immutable + bool fNeedsMoveVerb; + + enum IsA { + kIsA_JustMoves, // we only have 0 or more moves + kIsA_MoreThanMoves, // we have verbs other than just move + kIsA_Oval, // we are 0 or more moves followed by an oval + kIsA_RRect, // we are 0 or more moves followed by a rrect + }; + IsA fIsA = kIsA_JustMoves; + int fIsAStart = -1; // tracks direction iff fIsA is not unknown + bool fIsACCW = false; // tracks direction iff fIsA is not unknown + + int countVerbs() const { return fVerbs.size(); } + + // called right before we add a (non-move) verb + void ensureMove() { + fIsA = kIsA_MoreThanMoves; + if (fNeedsMoveVerb) { + this->moveTo(fLastMovePoint); + } + } + + SkPath make(sk_sp) const; + + SkPathBuilder& privateReverseAddPath(const SkPath&); + + friend class SkPathPriv; +}; + +#endif + diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPathEffect.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPathEffect.h new file mode 100644 index 0000000000..0ebe39f293 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPathEffect.h @@ -0,0 +1,113 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkPathEffect_DEFINED +#define SkPathEffect_DEFINED + +#include "include/core/SkFlattenable.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkScalar.h" +#include "include/private/base/SkAPI.h" + +// TODO(kjlubick) update clients and remove this unnecessary #include +#include "include/core/SkPath.h" // IWYU pragma: keep + +#include +#include + +class SkMatrix; +class SkStrokeRec; +struct SkDeserialProcs; +struct SkRect; + +/** \class SkPathEffect + + SkPathEffect is the base class for objects in the SkPaint that affect + the geometry of a drawing primitive before it is transformed by the + canvas' matrix and drawn. + + Dashing is implemented as a subclass of SkPathEffect. +*/ +class SK_API SkPathEffect : public SkFlattenable { +public: + /** + * Returns a patheffect that apples each effect (first and second) to the original path, + * and returns a path with the sum of these. + * + * result = first(path) + second(path) + * + */ + static sk_sp MakeSum(sk_sp first, sk_sp second); + + /** + * Returns a patheffect that applies the inner effect to the path, and then applies the + * outer effect to the result of the inner's. + * + * result = outer(inner(path)) + */ + static sk_sp MakeCompose(sk_sp outer, sk_sp inner); + + static SkFlattenable::Type GetFlattenableType() { + return kSkPathEffect_Type; + } + + // move to base? + + enum DashType { + kNone_DashType, //!< ignores the info parameter + kDash_DashType, //!< fills in all of the info parameter + }; + + struct DashInfo { + DashInfo() : fIntervals(nullptr), fCount(0), fPhase(0) {} + DashInfo(SkScalar* intervals, int32_t count, SkScalar phase) + : fIntervals(intervals), fCount(count), fPhase(phase) {} + + SkScalar* fIntervals; //!< Length of on/off intervals for dashed lines + // Even values represent ons, and odds offs + int32_t fCount; //!< Number of intervals in the dash. Should be even number + SkScalar fPhase; //!< Offset into the dashed interval pattern + // mod the sum of all intervals + }; + + DashType asADash(DashInfo* info) const; + + /** + * Given a src path (input) and a stroke-rec (input and output), apply + * this effect to the src path, returning the new path in dst, and return + * true. If this effect cannot be applied, return false and ignore dst + * and stroke-rec. + * + * The stroke-rec specifies the initial request for stroking (if any). + * The effect can treat this as input only, or it can choose to change + * the rec as well. For example, the effect can decide to change the + * stroke's width or join, or the effect can change the rec from stroke + * to fill (or fill to stroke) in addition to returning a new (dst) path. + * + * If this method returns true, the caller will apply (as needed) the + * resulting stroke-rec to dst and then draw. + */ + bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect* cullR) const; + + /** Version of filterPath that can be called when the CTM is known. */ + bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect* cullR, + const SkMatrix& ctm) const; + + /** True if this path effect requires a valid CTM */ + bool needsCTM() const; + + static sk_sp Deserialize(const void* data, size_t size, + const SkDeserialProcs* procs = nullptr); + +private: + SkPathEffect() = default; + friend class SkPathEffectBase; + + using INHERITED = SkFlattenable; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPathMeasure.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPathMeasure.h new file mode 100644 index 0000000000..2c2c36007a --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPathMeasure.h @@ -0,0 +1,95 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkPathMeasure_DEFINED +#define SkPathMeasure_DEFINED + +#include "include/core/SkContourMeasure.h" +#include "include/core/SkPoint.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkScalar.h" +#include "include/private/base/SkAPI.h" +#include "include/private/base/SkDebug.h" + +class SkMatrix; +class SkPath; + +class SK_API SkPathMeasure { +public: + SkPathMeasure(); + /** Initialize the pathmeasure with the specified path. The parts of the path that are needed + * are copied, so the client is free to modify/delete the path after this call. + * + * resScale controls the precision of the measure. values > 1 increase the + * precision (and possibly slow down the computation). + */ + SkPathMeasure(const SkPath& path, bool forceClosed, SkScalar resScale = 1); + ~SkPathMeasure(); + + SkPathMeasure(SkPathMeasure&&) = default; + SkPathMeasure& operator=(SkPathMeasure&&) = default; + + /** Reset the pathmeasure with the specified path. The parts of the path that are needed + * are copied, so the client is free to modify/delete the path after this call.. + */ + void setPath(const SkPath*, bool forceClosed); + + /** Return the total length of the current contour, or 0 if no path + is associated (e.g. resetPath(null)) + */ + SkScalar getLength(); + + /** Pins distance to 0 <= distance <= getLength(), and then computes + the corresponding position and tangent. + Returns false if there is no path, or a zero-length path was specified, in which case + position and tangent are unchanged. + */ + [[nodiscard]] bool getPosTan(SkScalar distance, SkPoint* position, SkVector* tangent); + + enum MatrixFlags { + kGetPosition_MatrixFlag = 0x01, + kGetTangent_MatrixFlag = 0x02, + kGetPosAndTan_MatrixFlag = kGetPosition_MatrixFlag | kGetTangent_MatrixFlag + }; + + /** Pins distance to 0 <= distance <= getLength(), and then computes + the corresponding matrix (by calling getPosTan). + Returns false if there is no path, or a zero-length path was specified, in which case + matrix is unchanged. + */ + [[nodiscard]] bool getMatrix(SkScalar distance, SkMatrix* matrix, + MatrixFlags flags = kGetPosAndTan_MatrixFlag); + + /** Given a start and stop distance, return in dst the intervening segment(s). + If the segment is zero-length, return false, else return true. + startD and stopD are pinned to legal values (0..getLength()). If startD > stopD + then return false (and leave dst untouched). + Begin the segment with a moveTo if startWithMoveTo is true + */ + bool getSegment(SkScalar startD, SkScalar stopD, SkPath* dst, bool startWithMoveTo); + + /** Return true if the current contour is closed() + */ + bool isClosed(); + + /** Move to the next contour in the path. Return true if one exists, or false if + we're done with the path. + */ + bool nextContour(); + +#ifdef SK_DEBUG + void dump(); +#endif + + const SkContourMeasure* currentMeasure() const { return fContour.get(); } + +private: + SkContourMeasureIter fIter; + sk_sp fContour; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPathTypes.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPathTypes.h new file mode 100644 index 0000000000..963a6bda00 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPathTypes.h @@ -0,0 +1,57 @@ +/* + * Copyright 2019 Google LLC. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkPathTypes_DEFINED +#define SkPathTypes_DEFINED + +enum class SkPathFillType { + /** Specifies that "inside" is computed by a non-zero sum of signed edge crossings */ + kWinding, + /** Specifies that "inside" is computed by an odd number of edge crossings */ + kEvenOdd, + /** Same as Winding, but draws outside of the path, rather than inside */ + kInverseWinding, + /** Same as EvenOdd, but draws outside of the path, rather than inside */ + kInverseEvenOdd +}; + +static inline bool SkPathFillType_IsEvenOdd(SkPathFillType ft) { + return (static_cast(ft) & 1) != 0; +} + +static inline bool SkPathFillType_IsInverse(SkPathFillType ft) { + return (static_cast(ft) & 2) != 0; +} + +static inline SkPathFillType SkPathFillType_ConvertToNonInverse(SkPathFillType ft) { + return static_cast(static_cast(ft) & 1); +} + +enum class SkPathDirection { + /** clockwise direction for adding closed contours */ + kCW, + /** counter-clockwise direction for adding closed contours */ + kCCW, +}; + +enum SkPathSegmentMask { + kLine_SkPathSegmentMask = 1 << 0, + kQuad_SkPathSegmentMask = 1 << 1, + kConic_SkPathSegmentMask = 1 << 2, + kCubic_SkPathSegmentMask = 1 << 3, +}; + +enum class SkPathVerb { + kMove, //!< SkPath::RawIter returns 1 point + kLine, //!< SkPath::RawIter returns 2 points + kQuad, //!< SkPath::RawIter returns 3 points + kConic, //!< SkPath::RawIter returns 3 points + 1 weight + kCubic, //!< SkPath::RawIter returns 4 points + kClose //!< SkPath::RawIter returns 0 points +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPathUtils.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPathUtils.h new file mode 100644 index 0000000000..6285da7996 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPathUtils.h @@ -0,0 +1,42 @@ +/* + * 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 SkPathUtils_DEFINED +#define SkPathUtils_DEFINED + +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" + +class SkMatrix; +class SkPaint; +class SkPath; +struct SkRect; + +namespace skpathutils { + +/** Returns the filled equivalent of the stroked path. + + @param src SkPath read to create a filled version + @param paint SkPaint, from which attributes such as stroke cap, width, miter, and join, + as well as pathEffect will be used. + @param dst resulting SkPath; may be the same as src, but may not be nullptr + @param cullRect optional limit passed to SkPathEffect + @param resScale if > 1, increase precision, else if (0 < resScale < 1) reduce precision + to favor speed and size + @return true if the dst path was updated, false if it was not (e.g. if the path + represents hairline and cannot be filled). +*/ +SK_API bool FillPathWithPaint(const SkPath &src, const SkPaint &paint, SkPath *dst, + const SkRect *cullRect, SkScalar resScale = 1); + +SK_API bool FillPathWithPaint(const SkPath &src, const SkPaint &paint, SkPath *dst, + const SkRect *cullRect, const SkMatrix &ctm); + +SK_API bool FillPathWithPaint(const SkPath &src, const SkPaint &paint, SkPath *dst); + +} + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPicture.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPicture.h new file mode 100644 index 0000000000..f4ab3ed29d --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPicture.h @@ -0,0 +1,291 @@ +/* + * Copyright 2007 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkPicture_DEFINED +#define SkPicture_DEFINED + +#include "include/core/SkRect.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkShader.h" // IWYU pragma: keep +#include "include/core/SkTypes.h" + +#include +#include +#include + +class SkCanvas; +class SkData; +class SkMatrix; +class SkStream; +class SkWStream; +enum class SkFilterMode; +struct SkDeserialProcs; +struct SkSerialProcs; + +// TODO(kjlubick) Remove this after cleaning up clients +#include "include/core/SkTileMode.h" // IWYU pragma: keep + +/** \class SkPicture + SkPicture records drawing commands made to SkCanvas. The command stream may be + played in whole or in part at a later time. + + SkPicture is an abstract class. SkPicture may be generated by SkPictureRecorder + or SkDrawable, or from SkPicture previously saved to SkData or SkStream. + + SkPicture may contain any SkCanvas drawing command, as well as one or more + SkCanvas matrix or SkCanvas clip. SkPicture has a cull SkRect, which is used as + a bounding box hint. To limit SkPicture bounds, use SkCanvas clip when + recording or drawing SkPicture. +*/ +class SK_API SkPicture : public SkRefCnt { +public: + ~SkPicture() override; + + /** Recreates SkPicture that was serialized into a stream. Returns constructed SkPicture + if successful; otherwise, returns nullptr. Fails if data does not permit + constructing valid SkPicture. + + procs->fPictureProc permits supplying a custom function to decode SkPicture. + If procs->fPictureProc is nullptr, default decoding is used. procs->fPictureCtx + may be used to provide user context to procs->fPictureProc; procs->fPictureProc + is called with a pointer to data, data byte length, and user context. + + @param stream container for serial data + @param procs custom serial data decoders; may be nullptr + @return SkPicture constructed from stream data + */ + static sk_sp MakeFromStream(SkStream* stream, + const SkDeserialProcs* procs = nullptr); + + /** Recreates SkPicture that was serialized into data. Returns constructed SkPicture + if successful; otherwise, returns nullptr. Fails if data does not permit + constructing valid SkPicture. + + procs->fPictureProc permits supplying a custom function to decode SkPicture. + If procs->fPictureProc is nullptr, default decoding is used. procs->fPictureCtx + may be used to provide user context to procs->fPictureProc; procs->fPictureProc + is called with a pointer to data, data byte length, and user context. + + @param data container for serial data + @param procs custom serial data decoders; may be nullptr + @return SkPicture constructed from data + */ + static sk_sp MakeFromData(const SkData* data, + const SkDeserialProcs* procs = nullptr); + + /** + + @param data pointer to serial data + @param size size of data + @param procs custom serial data decoders; may be nullptr + @return SkPicture constructed from data + */ + static sk_sp MakeFromData(const void* data, size_t size, + const SkDeserialProcs* procs = nullptr); + + /** \class SkPicture::AbortCallback + AbortCallback is an abstract class. An implementation of AbortCallback may + passed as a parameter to SkPicture::playback, to stop it before all drawing + commands have been processed. + + If AbortCallback::abort returns true, SkPicture::playback is interrupted. + */ + class SK_API AbortCallback { + public: + /** Has no effect. + */ + virtual ~AbortCallback() = default; + + /** Stops SkPicture playback when some condition is met. A subclass of + AbortCallback provides an override for abort() that can stop SkPicture::playback. + + The part of SkPicture drawn when aborted is undefined. SkPicture instantiations are + free to stop drawing at different points during playback. + + If the abort happens inside one or more calls to SkCanvas::save(), stack + of SkCanvas matrix and SkCanvas clip values is restored to its state before + SkPicture::playback was called. + + @return true to stop playback + + example: https://fiddle.skia.org/c/@Picture_AbortCallback_abort + */ + virtual bool abort() = 0; + + protected: + AbortCallback() = default; + AbortCallback(const AbortCallback&) = delete; + AbortCallback& operator=(const AbortCallback&) = delete; + }; + + /** Replays the drawing commands on the specified canvas. In the case that the + commands are recorded, each command in the SkPicture is sent separately to canvas. + + To add a single command to draw SkPicture to recording canvas, call + SkCanvas::drawPicture instead. + + @param canvas receiver of drawing commands + @param callback allows interruption of playback + + example: https://fiddle.skia.org/c/@Picture_playback + */ + virtual void playback(SkCanvas* canvas, AbortCallback* callback = nullptr) const = 0; + + /** Returns cull SkRect for this picture, passed in when SkPicture was created. + Returned SkRect does not specify clipping SkRect for SkPicture; cull is hint + of SkPicture bounds. + + SkPicture is free to discard recorded drawing commands that fall outside + cull. + + @return bounds passed when SkPicture was created + + example: https://fiddle.skia.org/c/@Picture_cullRect + */ + virtual SkRect cullRect() const = 0; + + /** Returns a non-zero value unique among SkPicture in Skia process. + + @return identifier for SkPicture + */ + uint32_t uniqueID() const { return fUniqueID; } + + /** Returns storage containing SkData describing SkPicture, using optional custom + encoders. + + procs->fPictureProc permits supplying a custom function to encode SkPicture. + If procs->fPictureProc is nullptr, default encoding is used. procs->fPictureCtx + may be used to provide user context to procs->fPictureProc; procs->fPictureProc + is called with a pointer to SkPicture and user context. + + The default behavior for serializing SkImages is to encode a nullptr. Should + clients want to, for example, encode these SkImages as PNGs so they can be + deserialized, they must provide SkSerialProcs with the fImageProc set to do so. + + @param procs custom serial data encoders; may be nullptr + @return storage containing serialized SkPicture + + example: https://fiddle.skia.org/c/@Picture_serialize + */ + sk_sp serialize(const SkSerialProcs* procs = nullptr) const; + + /** Writes picture to stream, using optional custom encoders. + + procs->fPictureProc permits supplying a custom function to encode SkPicture. + If procs->fPictureProc is nullptr, default encoding is used. procs->fPictureCtx + may be used to provide user context to procs->fPictureProc; procs->fPictureProc + is called with a pointer to SkPicture and user context. + + The default behavior for serializing SkImages is to encode a nullptr. Should + clients want to, for example, encode these SkImages as PNGs so they can be + deserialized, they must provide SkSerialProcs with the fImageProc set to do so. + + @param stream writable serial data stream + @param procs custom serial data encoders; may be nullptr + + example: https://fiddle.skia.org/c/@Picture_serialize_2 + */ + void serialize(SkWStream* stream, const SkSerialProcs* procs = nullptr) const; + + /** Returns a placeholder SkPicture. Result does not draw, and contains only + cull SkRect, a hint of its bounds. Result is immutable; it cannot be changed + later. Result identifier is unique. + + Returned placeholder can be intercepted during playback to insert other + commands into SkCanvas draw stream. + + @param cull placeholder dimensions + @return placeholder with unique identifier + + example: https://fiddle.skia.org/c/@Picture_MakePlaceholder + */ + static sk_sp MakePlaceholder(SkRect cull); + + /** Returns the approximate number of operations in SkPicture. Returned value + may be greater or less than the number of SkCanvas calls + recorded: some calls may be recorded as more than one operation, other + calls may be optimized away. + + @param nested if true, include the op-counts of nested pictures as well, else + just return count the ops in the top-level picture. + @return approximate operation count + + example: https://fiddle.skia.org/c/@Picture_approximateOpCount + */ + virtual int approximateOpCount(bool nested = false) const = 0; + + /** Returns the approximate byte size of SkPicture. Does not include large objects + referenced by SkPicture. + + @return approximate size + + example: https://fiddle.skia.org/c/@Picture_approximateBytesUsed + */ + virtual size_t approximateBytesUsed() const = 0; + + /** Return a new shader that will draw with this picture. + * + * @param tmx The tiling mode to use when sampling in the x-direction. + * @param tmy The tiling mode to use when sampling in the y-direction. + * @param mode How to filter the tiles + * @param localMatrix Optional matrix used when sampling + * @param tileRect The tile rectangle in picture coordinates: this represents the subset + * (or superset) of the picture used when building a tile. It is not + * affected by localMatrix and does not imply scaling (only translation + * and cropping). If null, the tile rect is considered equal to the picture + * bounds. + * @return Returns a new shader object. Note: this function never returns null. + */ + sk_sp makeShader(SkTileMode tmx, SkTileMode tmy, SkFilterMode mode, + const SkMatrix* localMatrix, const SkRect* tileRect) const; + + sk_sp makeShader(SkTileMode tmx, SkTileMode tmy, SkFilterMode mode) const { + return this->makeShader(tmx, tmy, mode, nullptr, nullptr); + } + +private: + // Allowed subclasses. + SkPicture(); + friend class SkBigPicture; + friend class SkEmptyPicture; + friend class SkPicturePriv; + + void serialize(SkWStream*, const SkSerialProcs*, class SkRefCntSet* typefaces, + bool textBlobsOnly=false) const; + static sk_sp MakeFromStreamPriv(SkStream*, const SkDeserialProcs*, + class SkTypefacePlayback*, + int recursionLimit); + friend class SkPictureData; + + /** Return true if the SkStream/Buffer represents a serialized picture, and + fills out SkPictInfo. After this function returns, the data source is not + rewound so it will have to be manually reset before passing to + MakeFromStream or MakeFromBuffer. Note, MakeFromStream and + MakeFromBuffer perform this check internally so these entry points are + intended for stand alone tools. + If false is returned, SkPictInfo is unmodified. + */ + static bool StreamIsSKP(SkStream*, struct SkPictInfo*); + static bool BufferIsSKP(class SkReadBuffer*, struct SkPictInfo*); + friend bool SkPicture_StreamIsSKP(SkStream*, struct SkPictInfo*); + + // Returns NULL if this is not an SkBigPicture. + virtual const class SkBigPicture* asSkBigPicture() const { return nullptr; } + + static bool IsValidPictInfo(const struct SkPictInfo& info); + static sk_sp Forwardport(const struct SkPictInfo&, + const class SkPictureData*, + class SkReadBuffer* buffer); + + struct SkPictInfo createHeader() const; + class SkPictureData* backport() const; + + uint32_t fUniqueID; + mutable std::atomic fAddedToCache{false}; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPictureRecorder.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPictureRecorder.h new file mode 100644 index 0000000000..573a643f73 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPictureRecorder.h @@ -0,0 +1,115 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkPictureRecorder_DEFINED +#define SkPictureRecorder_DEFINED + +#include "include/core/SkRect.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkScalar.h" +#include "include/private/base/SkAPI.h" + +#include + +#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK +namespace android { + class Picture; +}; +#endif + +class SkBBHFactory; +class SkBBoxHierarchy; +class SkCanvas; +class SkDrawable; +class SkPicture; +class SkRecord; +class SkRecorder; + +class SK_API SkPictureRecorder { +public: + SkPictureRecorder(); + ~SkPictureRecorder(); + + /** Returns the canvas that records the drawing commands. + @param bounds the cull rect used when recording this picture. Any drawing the falls outside + of this rect is undefined, and may be drawn or it may not. + @param bbh optional acceleration structure + @param recordFlags optional flags that control recording. + @return the canvas. + */ + SkCanvas* beginRecording(const SkRect& bounds, sk_sp bbh); + + SkCanvas* beginRecording(const SkRect& bounds, SkBBHFactory* bbhFactory = nullptr); + + SkCanvas* beginRecording(SkScalar width, SkScalar height, + SkBBHFactory* bbhFactory = nullptr) { + return this->beginRecording(SkRect::MakeWH(width, height), bbhFactory); + } + + /** Returns the recording canvas if one is active, or NULL if recording is + not active. This does not alter the refcnt on the canvas (if present). + */ + SkCanvas* getRecordingCanvas(); + + /** + * Signal that the caller is done recording. This invalidates the canvas returned by + * beginRecording/getRecordingCanvas. Ownership of the object is passed to the caller, who + * must call unref() when they are done using it. + * + * The returned picture is immutable. If during recording drawables were added to the canvas, + * these will have been "drawn" into a recording canvas, so that this resulting picture will + * reflect their current state, but will not contain a live reference to the drawables + * themselves. + */ + sk_sp finishRecordingAsPicture(); + + /** + * Signal that the caller is done recording, and update the cull rect to use for bounding + * box hierarchy (BBH) generation. The behavior is the same as calling + * finishRecordingAsPicture(), except that this method updates the cull rect initially passed + * into beginRecording. + * @param cullRect the new culling rectangle to use as the overall bound for BBH generation + * and subsequent culling operations. + * @return the picture containing the recorded content. + */ + sk_sp finishRecordingAsPictureWithCull(const SkRect& cullRect); + + /** + * Signal that the caller is done recording. This invalidates the canvas returned by + * beginRecording/getRecordingCanvas. Ownership of the object is passed to the caller, who + * must call unref() when they are done using it. + * + * Unlike finishRecordingAsPicture(), which returns an immutable picture, the returned drawable + * may contain live references to other drawables (if they were added to the recording canvas) + * and therefore this drawable will reflect the current state of those nested drawables anytime + * it is drawn or a new picture is snapped from it (by calling drawable->makePictureSnapshot()). + */ + sk_sp finishRecordingAsDrawable(); + +private: + void reset(); + + /** Replay the current (partially recorded) operation stream into + canvas. This call doesn't close the current recording. + */ +#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK + friend class android::Picture; +#endif + friend class SkPictureRecorderReplayTester; // for unit testing + void partialReplay(SkCanvas* canvas) const; + + bool fActivelyRecording; + SkRect fCullRect; + sk_sp fBBH; + std::unique_ptr fRecorder; + sk_sp fRecord; + + SkPictureRecorder(SkPictureRecorder&&) = delete; + SkPictureRecorder& operator=(SkPictureRecorder&&) = delete; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPixelRef.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPixelRef.h new file mode 100644 index 0000000000..12779890f8 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPixelRef.h @@ -0,0 +1,119 @@ +/* + * Copyright 2008 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkPixelRef_DEFINED +#define SkPixelRef_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkSize.h" +#include "include/private/SkIDChangeListener.h" +#include "include/private/base/SkAPI.h" +#include "include/private/base/SkTo.h" + +#include +#include +#include + +class SkDiscardableMemory; + +/** \class SkPixelRef + + This class is the smart container for pixel memory, and is used with SkBitmap. + This class can be shared/accessed between multiple threads. +*/ +class SK_API SkPixelRef : public SkRefCnt { +public: + SkPixelRef(int width, int height, void* addr, size_t rowBytes); + ~SkPixelRef() override; + + SkISize dimensions() const { return {fWidth, fHeight}; } + int width() const { return fWidth; } + int height() const { return fHeight; } + void* pixels() const { return fPixels; } + size_t rowBytes() const { return fRowBytes; } + + /** Returns a non-zero, unique value corresponding to the pixels in this + pixelref. Each time the pixels are changed (and notifyPixelsChanged is + called), a different generation ID will be returned. + */ + uint32_t getGenerationID() const; + + /** + * Call this if you have changed the contents of the pixels. This will in- + * turn cause a different generation ID value to be returned from + * getGenerationID(). + */ + void notifyPixelsChanged(); + + /** Returns true if this pixelref is marked as immutable, meaning that the + contents of its pixels will not change for the lifetime of the pixelref. + */ + bool isImmutable() const { return fMutability != kMutable; } + + /** Marks this pixelref is immutable, meaning that the contents of its + pixels will not change for the lifetime of the pixelref. This state can + be set on a pixelref, but it cannot be cleared once it is set. + */ + void setImmutable(); + + // Register a listener that may be called the next time our generation ID changes. + // + // We'll only call the listener if we're confident that we are the only SkPixelRef with this + // generation ID. If our generation ID changes and we decide not to call the listener, we'll + // never call it: you must add a new listener for each generation ID change. We also won't call + // the listener when we're certain no one knows what our generation ID is. + // + // This can be used to invalidate caches keyed by SkPixelRef generation ID. + // Takes ownership of listener. Threadsafe. + void addGenIDChangeListener(sk_sp listener); + + // Call when this pixelref is part of the key to a resourcecache entry. This allows the cache + // to know automatically those entries can be purged when this pixelref is changed or deleted. + void notifyAddedToCache() { + fAddedToCache.store(true); + } + + virtual SkDiscardableMemory* diagnostic_only_getDiscardable() const { return nullptr; } + +protected: + void android_only_reset(int width, int height, size_t rowBytes); + +private: + int fWidth; + int fHeight; + void* fPixels; + size_t fRowBytes; + + // Bottom bit indicates the Gen ID is unique. + bool genIDIsUnique() const { return SkToBool(fTaggedGenID.load() & 1); } + mutable std::atomic fTaggedGenID; + + SkIDChangeListener::List fGenIDChangeListeners; + + // Set true by caches when they cache content that's derived from the current pixels. + std::atomic fAddedToCache; + + enum Mutability { + kMutable, // PixelRefs begin mutable. + kTemporarilyImmutable, // Considered immutable, but can revert to mutable. + kImmutable, // Once set to this state, it never leaves. + } fMutability : 8; // easily fits inside a byte + + void needsNewGenID(); + void callGenIDChangeListeners(); + + void setTemporarilyImmutable(); + void restoreMutability(); + friend class SkSurface_Raster; // For temporary immutable methods above. + + void setImmutableWithID(uint32_t genID); + friend void SkBitmapCache_setImmutableWithID(SkPixelRef*, uint32_t); + + using INHERITED = SkRefCnt; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPixmap.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPixmap.h new file mode 100644 index 0000000000..e3379cbcf9 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPixmap.h @@ -0,0 +1,731 @@ +/* + * 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 SkPixmap_DEFINED +#define SkPixmap_DEFINED + +#include "include/core/SkColor.h" +#include "include/core/SkColorType.h" +#include "include/core/SkImageInfo.h" +#include "include/core/SkRect.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkSamplingOptions.h" +#include "include/core/SkSize.h" +#include "include/private/base/SkAPI.h" +#include "include/private/base/SkAssert.h" + +#include +#include + +class SkColorSpace; +enum SkAlphaType : int; +struct SkMask; + +/** \class SkPixmap + SkPixmap provides a utility to pair SkImageInfo with pixels and row bytes. + SkPixmap is a low level class which provides convenience functions to access + raster destinations. SkCanvas can not draw SkPixmap, nor does SkPixmap provide + a direct drawing destination. + + Use SkBitmap to draw pixels referenced by SkPixmap; use SkSurface to draw into + pixels referenced by SkPixmap. + + SkPixmap does not try to manage the lifetime of the pixel memory. Use SkPixelRef + to manage pixel memory; SkPixelRef is safe across threads. +*/ +class SK_API SkPixmap { +public: + + /** Creates an empty SkPixmap without pixels, with kUnknown_SkColorType, with + kUnknown_SkAlphaType, and with a width and height of zero. Use + reset() to associate pixels, SkColorType, SkAlphaType, width, and height + after SkPixmap has been created. + + @return empty SkPixmap + */ + SkPixmap() + : fPixels(nullptr), fRowBytes(0), fInfo(SkImageInfo::MakeUnknown(0, 0)) + {} + + /** Creates SkPixmap from info width, height, SkAlphaType, and SkColorType. + addr points to pixels, or nullptr. rowBytes should be info.width() times + info.bytesPerPixel(), or larger. + + No parameter checking is performed; it is up to the caller to ensure that + addr and rowBytes agree with info. + + The memory lifetime of pixels is managed by the caller. When SkPixmap goes + out of scope, addr is unaffected. + + SkPixmap may be later modified by reset() to change its size, pixel type, or + storage. + + @param info width, height, SkAlphaType, SkColorType of SkImageInfo + @param addr pointer to pixels allocated by caller; may be nullptr + @param rowBytes size of one row of addr; width times pixel size, or larger + @return initialized SkPixmap + */ + SkPixmap(const SkImageInfo& info, const void* addr, size_t rowBytes) + : fPixels(addr), fRowBytes(rowBytes), fInfo(info) + {} + + /** Sets width, height, row bytes to zero; pixel address to nullptr; SkColorType to + kUnknown_SkColorType; and SkAlphaType to kUnknown_SkAlphaType. + + The prior pixels are unaffected; it is up to the caller to release pixels + memory if desired. + + example: https://fiddle.skia.org/c/@Pixmap_reset + */ + void reset(); + + /** Sets width, height, SkAlphaType, and SkColorType from info. + Sets pixel address from addr, which may be nullptr. + Sets row bytes from rowBytes, which should be info.width() times + info.bytesPerPixel(), or larger. + + Does not check addr. Asserts if built with SK_DEBUG defined and if rowBytes is + too small to hold one row of pixels. + + The memory lifetime pixels are managed by the caller. When SkPixmap goes + out of scope, addr is unaffected. + + @param info width, height, SkAlphaType, SkColorType of SkImageInfo + @param addr pointer to pixels allocated by caller; may be nullptr + @param rowBytes size of one row of addr; width times pixel size, or larger + + example: https://fiddle.skia.org/c/@Pixmap_reset_2 + */ + void reset(const SkImageInfo& info, const void* addr, size_t rowBytes); + + /** Changes SkColorSpace in SkImageInfo; preserves width, height, SkAlphaType, and + SkColorType in SkImage, and leaves pixel address and row bytes unchanged. + SkColorSpace reference count is incremented. + + @param colorSpace SkColorSpace moved to SkImageInfo + + example: https://fiddle.skia.org/c/@Pixmap_setColorSpace + */ + void setColorSpace(sk_sp colorSpace); + + /** Deprecated. + */ + [[nodiscard]] bool reset(const SkMask& mask); + + /** Sets subset width, height, pixel address to intersection of SkPixmap with area, + if intersection is not empty; and return true. Otherwise, leave subset unchanged + and return false. + + Failing to read the return value generates a compile time warning. + + @param subset storage for width, height, pixel address of intersection + @param area bounds to intersect with SkPixmap + @return true if intersection of SkPixmap and area is not empty + */ + [[nodiscard]] bool extractSubset(SkPixmap* subset, const SkIRect& area) const; + + /** Returns width, height, SkAlphaType, SkColorType, and SkColorSpace. + + @return reference to SkImageInfo + */ + const SkImageInfo& info() const { return fInfo; } + + /** Returns row bytes, the interval from one pixel row to the next. Row bytes + is at least as large as: width() * info().bytesPerPixel(). + + Returns zero if colorType() is kUnknown_SkColorType. + It is up to the SkBitmap creator to ensure that row bytes is a useful value. + + @return byte length of pixel row + */ + size_t rowBytes() const { return fRowBytes; } + + /** Returns pixel address, the base address corresponding to the pixel origin. + + It is up to the SkPixmap creator to ensure that pixel address is a useful value. + + @return pixel address + */ + const void* addr() const { return fPixels; } + + /** Returns pixel count in each pixel row. Should be equal or less than: + rowBytes() / info().bytesPerPixel(). + + @return pixel width in SkImageInfo + */ + int width() const { return fInfo.width(); } + + /** Returns pixel row count. + + @return pixel height in SkImageInfo + */ + int height() const { return fInfo.height(); } + + /** + * Return the dimensions of the pixmap (from its ImageInfo) + */ + SkISize dimensions() const { return fInfo.dimensions(); } + + SkColorType colorType() const { return fInfo.colorType(); } + + SkAlphaType alphaType() const { return fInfo.alphaType(); } + + /** Returns SkColorSpace, the range of colors, associated with SkImageInfo. The + reference count of SkColorSpace is unchanged. The returned SkColorSpace is + immutable. + + @return SkColorSpace in SkImageInfo, or nullptr + */ + SkColorSpace* colorSpace() const; + + /** Returns smart pointer to SkColorSpace, the range of colors, associated with + SkImageInfo. The smart pointer tracks the number of objects sharing this + SkColorSpace reference so the memory is released when the owners destruct. + + The returned SkColorSpace is immutable. + + @return SkColorSpace in SkImageInfo wrapped in a smart pointer + */ + sk_sp refColorSpace() const; + + /** Returns true if SkAlphaType is kOpaque_SkAlphaType. + Does not check if SkColorType allows alpha, or if any pixel value has + transparency. + + @return true if SkImageInfo has opaque SkAlphaType + */ + bool isOpaque() const { return fInfo.isOpaque(); } + + /** Returns SkIRect { 0, 0, width(), height() }. + + @return integral rectangle from origin to width() and height() + */ + SkIRect bounds() const { return SkIRect::MakeWH(this->width(), this->height()); } + + /** Returns number of pixels that fit on row. Should be greater than or equal to + width(). + + @return maximum pixels per row + */ + int rowBytesAsPixels() const { return int(fRowBytes >> this->shiftPerPixel()); } + + /** Returns bit shift converting row bytes to row pixels. + Returns zero for kUnknown_SkColorType. + + @return one of: 0, 1, 2, 3; left shift to convert pixels to bytes + */ + int shiftPerPixel() const { return fInfo.shiftPerPixel(); } + + /** Returns minimum memory required for pixel storage. + Does not include unused memory on last row when rowBytesAsPixels() exceeds width(). + Returns SIZE_MAX if result does not fit in size_t. + Returns zero if height() or width() is 0. + Returns height() times rowBytes() if colorType() is kUnknown_SkColorType. + + @return size in bytes of image buffer + */ + size_t computeByteSize() const { return fInfo.computeByteSize(fRowBytes); } + + /** Returns true if all pixels are opaque. SkColorType determines how pixels + are encoded, and whether pixel describes alpha. Returns true for SkColorType + without alpha in each pixel; for other SkColorType, returns true if all + pixels have alpha values equivalent to 1.0 or greater. + + For SkColorType kRGB_565_SkColorType or kGray_8_SkColorType: always + returns true. For SkColorType kAlpha_8_SkColorType, kBGRA_8888_SkColorType, + kRGBA_8888_SkColorType: returns true if all pixel alpha values are 255. + For SkColorType kARGB_4444_SkColorType: returns true if all pixel alpha values are 15. + For kRGBA_F16_SkColorType: returns true if all pixel alpha values are 1.0 or + greater. + + Returns false for kUnknown_SkColorType. + + @return true if all pixels have opaque values or SkColorType is opaque + + example: https://fiddle.skia.org/c/@Pixmap_computeIsOpaque + */ + bool computeIsOpaque() const; + + /** Returns pixel at (x, y) as unpremultiplied color. + Returns black with alpha if SkColorType is kAlpha_8_SkColorType. + + Input is not validated: out of bounds values of x or y trigger an assert() if + built with SK_DEBUG defined; and returns undefined values or may crash if + SK_RELEASE is defined. Fails if SkColorType is kUnknown_SkColorType or + pixel address is nullptr. + + SkColorSpace in SkImageInfo is ignored. Some color precision may be lost in the + conversion to unpremultiplied color; original pixel data may have additional + precision. + + @param x column index, zero or greater, and less than width() + @param y row index, zero or greater, and less than height() + @return pixel converted to unpremultiplied color + + example: https://fiddle.skia.org/c/@Pixmap_getColor + */ + SkColor getColor(int x, int y) const; + + /** Returns pixel at (x, y) as unpremultiplied color as an SkColor4f. + Returns black with alpha if SkColorType is kAlpha_8_SkColorType. + + Input is not validated: out of bounds values of x or y trigger an assert() if + built with SK_DEBUG defined; and returns undefined values or may crash if + SK_RELEASE is defined. Fails if SkColorType is kUnknown_SkColorType or + pixel address is nullptr. + + SkColorSpace in SkImageInfo is ignored. Some color precision may be lost in the + conversion to unpremultiplied color; original pixel data may have additional + precision, though this is less likely than for getColor(). Rounding errors may + occur if the underlying type has lower precision. + + @param x column index, zero or greater, and less than width() + @param y row index, zero or greater, and less than height() + @return pixel converted to unpremultiplied float color + */ + SkColor4f getColor4f(int x, int y) const; + + /** Look up the pixel at (x,y) and return its alpha component, normalized to [0..1]. + This is roughly equivalent to SkGetColorA(getColor()), but can be more efficent + (and more precise if the pixels store more than 8 bits per component). + + @param x column index, zero or greater, and less than width() + @param y row index, zero or greater, and less than height() + @return alpha converted to normalized float + */ + float getAlphaf(int x, int y) const; + + /** Returns readable pixel address at (x, y). Returns nullptr if SkPixelRef is nullptr. + + Input is not validated: out of bounds values of x or y trigger an assert() if + built with SK_DEBUG defined. Returns nullptr if SkColorType is kUnknown_SkColorType. + + Performs a lookup of pixel size; for better performance, call + one of: addr8, addr16, addr32, addr64, or addrF16(). + + @param x column index, zero or greater, and less than width() + @param y row index, zero or greater, and less than height() + @return readable generic pointer to pixel + */ + const void* addr(int x, int y) const { + return (const char*)fPixels + fInfo.computeOffset(x, y, fRowBytes); + } + + /** Returns readable base pixel address. Result is addressable as unsigned 8-bit bytes. + Will trigger an assert() if SkColorType is not kAlpha_8_SkColorType or + kGray_8_SkColorType, and is built with SK_DEBUG defined. + + One byte corresponds to one pixel. + + @return readable unsigned 8-bit pointer to pixels + */ + const uint8_t* addr8() const { + SkASSERT(1 == fInfo.bytesPerPixel()); + return reinterpret_cast(fPixels); + } + + /** Returns readable base pixel address. Result is addressable as unsigned 16-bit words. + Will trigger an assert() if SkColorType is not kRGB_565_SkColorType or + kARGB_4444_SkColorType, and is built with SK_DEBUG defined. + + One word corresponds to one pixel. + + @return readable unsigned 16-bit pointer to pixels + */ + const uint16_t* addr16() const { + SkASSERT(2 == fInfo.bytesPerPixel()); + return reinterpret_cast(fPixels); + } + + /** Returns readable base pixel address. Result is addressable as unsigned 32-bit words. + Will trigger an assert() if SkColorType is not kRGBA_8888_SkColorType or + kBGRA_8888_SkColorType, and is built with SK_DEBUG defined. + + One word corresponds to one pixel. + + @return readable unsigned 32-bit pointer to pixels + */ + const uint32_t* addr32() const { + SkASSERT(4 == fInfo.bytesPerPixel()); + return reinterpret_cast(fPixels); + } + + /** Returns readable base pixel address. Result is addressable as unsigned 64-bit words. + Will trigger an assert() if SkColorType is not kRGBA_F16_SkColorType and is built + with SK_DEBUG defined. + + One word corresponds to one pixel. + + @return readable unsigned 64-bit pointer to pixels + */ + const uint64_t* addr64() const { + SkASSERT(8 == fInfo.bytesPerPixel()); + return reinterpret_cast(fPixels); + } + + /** Returns readable base pixel address. Result is addressable as unsigned 16-bit words. + Will trigger an assert() if SkColorType is not kRGBA_F16_SkColorType and is built + with SK_DEBUG defined. + + Each word represents one color component encoded as a half float. + Four words correspond to one pixel. + + @return readable unsigned 16-bit pointer to first component of pixels + */ + const uint16_t* addrF16() const { + SkASSERT(8 == fInfo.bytesPerPixel()); + SkASSERT(kRGBA_F16_SkColorType == fInfo.colorType() || + kRGBA_F16Norm_SkColorType == fInfo.colorType()); + return reinterpret_cast(fPixels); + } + + /** Returns readable pixel address at (x, y). + + Input is not validated: out of bounds values of x or y trigger an assert() if + built with SK_DEBUG defined. + + Will trigger an assert() if SkColorType is not kAlpha_8_SkColorType or + kGray_8_SkColorType, and is built with SK_DEBUG defined. + + @param x column index, zero or greater, and less than width() + @param y row index, zero or greater, and less than height() + @return readable unsigned 8-bit pointer to pixel at (x, y) + */ + const uint8_t* addr8(int x, int y) const { + SkASSERT((unsigned)x < (unsigned)fInfo.width()); + SkASSERT((unsigned)y < (unsigned)fInfo.height()); + return (const uint8_t*)((const char*)this->addr8() + (size_t)y * fRowBytes + (x << 0)); + } + + /** Returns readable pixel address at (x, y). + + Input is not validated: out of bounds values of x or y trigger an assert() if + built with SK_DEBUG defined. + + Will trigger an assert() if SkColorType is not kRGB_565_SkColorType or + kARGB_4444_SkColorType, and is built with SK_DEBUG defined. + + @param x column index, zero or greater, and less than width() + @param y row index, zero or greater, and less than height() + @return readable unsigned 16-bit pointer to pixel at (x, y) + */ + const uint16_t* addr16(int x, int y) const { + SkASSERT((unsigned)x < (unsigned)fInfo.width()); + SkASSERT((unsigned)y < (unsigned)fInfo.height()); + return (const uint16_t*)((const char*)this->addr16() + (size_t)y * fRowBytes + (x << 1)); + } + + /** Returns readable pixel address at (x, y). + + Input is not validated: out of bounds values of x or y trigger an assert() if + built with SK_DEBUG defined. + + Will trigger an assert() if SkColorType is not kRGBA_8888_SkColorType or + kBGRA_8888_SkColorType, and is built with SK_DEBUG defined. + + @param x column index, zero or greater, and less than width() + @param y row index, zero or greater, and less than height() + @return readable unsigned 32-bit pointer to pixel at (x, y) + */ + const uint32_t* addr32(int x, int y) const { + SkASSERT((unsigned)x < (unsigned)fInfo.width()); + SkASSERT((unsigned)y < (unsigned)fInfo.height()); + return (const uint32_t*)((const char*)this->addr32() + (size_t)y * fRowBytes + (x << 2)); + } + + /** Returns readable pixel address at (x, y). + + Input is not validated: out of bounds values of x or y trigger an assert() if + built with SK_DEBUG defined. + + Will trigger an assert() if SkColorType is not kRGBA_F16_SkColorType and is built + with SK_DEBUG defined. + + @param x column index, zero or greater, and less than width() + @param y row index, zero or greater, and less than height() + @return readable unsigned 64-bit pointer to pixel at (x, y) + */ + const uint64_t* addr64(int x, int y) const { + SkASSERT((unsigned)x < (unsigned)fInfo.width()); + SkASSERT((unsigned)y < (unsigned)fInfo.height()); + return (const uint64_t*)((const char*)this->addr64() + (size_t)y * fRowBytes + (x << 3)); + } + + /** Returns readable pixel address at (x, y). + + Input is not validated: out of bounds values of x or y trigger an assert() if + built with SK_DEBUG defined. + + Will trigger an assert() if SkColorType is not kRGBA_F16_SkColorType and is built + with SK_DEBUG defined. + + Each unsigned 16-bit word represents one color component encoded as a half float. + Four words correspond to one pixel. + + @param x column index, zero or greater, and less than width() + @param y row index, zero or greater, and less than height() + @return readable unsigned 16-bit pointer to pixel component at (x, y) + */ + const uint16_t* addrF16(int x, int y) const { + SkASSERT(kRGBA_F16_SkColorType == fInfo.colorType() || + kRGBA_F16Norm_SkColorType == fInfo.colorType()); + return reinterpret_cast(this->addr64(x, y)); + } + + /** Returns writable base pixel address. + + @return writable generic base pointer to pixels + */ + void* writable_addr() const { return const_cast(fPixels); } + + /** Returns writable pixel address at (x, y). + + Input is not validated: out of bounds values of x or y trigger an assert() if + built with SK_DEBUG defined. Returns zero if SkColorType is kUnknown_SkColorType. + + @param x column index, zero or greater, and less than width() + @param y row index, zero or greater, and less than height() + @return writable generic pointer to pixel + */ + void* writable_addr(int x, int y) const { + return const_cast(this->addr(x, y)); + } + + /** Returns writable pixel address at (x, y). Result is addressable as unsigned + 8-bit bytes. Will trigger an assert() if SkColorType is not kAlpha_8_SkColorType + or kGray_8_SkColorType, and is built with SK_DEBUG defined. + + One byte corresponds to one pixel. + + @param x column index, zero or greater, and less than width() + @param y row index, zero or greater, and less than height() + @return writable unsigned 8-bit pointer to pixels + */ + uint8_t* writable_addr8(int x, int y) const { + return const_cast(this->addr8(x, y)); + } + + /** Returns writable_addr pixel address at (x, y). Result is addressable as unsigned + 16-bit words. Will trigger an assert() if SkColorType is not kRGB_565_SkColorType + or kARGB_4444_SkColorType, and is built with SK_DEBUG defined. + + One word corresponds to one pixel. + + @param x column index, zero or greater, and less than width() + @param y row index, zero or greater, and less than height() + @return writable unsigned 16-bit pointer to pixel + */ + uint16_t* writable_addr16(int x, int y) const { + return const_cast(this->addr16(x, y)); + } + + /** Returns writable pixel address at (x, y). Result is addressable as unsigned + 32-bit words. Will trigger an assert() if SkColorType is not + kRGBA_8888_SkColorType or kBGRA_8888_SkColorType, and is built with SK_DEBUG + defined. + + One word corresponds to one pixel. + + @param x column index, zero or greater, and less than width() + @param y row index, zero or greater, and less than height() + @return writable unsigned 32-bit pointer to pixel + */ + uint32_t* writable_addr32(int x, int y) const { + return const_cast(this->addr32(x, y)); + } + + /** Returns writable pixel address at (x, y). Result is addressable as unsigned + 64-bit words. Will trigger an assert() if SkColorType is not + kRGBA_F16_SkColorType and is built with SK_DEBUG defined. + + One word corresponds to one pixel. + + @param x column index, zero or greater, and less than width() + @param y row index, zero or greater, and less than height() + @return writable unsigned 64-bit pointer to pixel + */ + uint64_t* writable_addr64(int x, int y) const { + return const_cast(this->addr64(x, y)); + } + + /** Returns writable pixel address at (x, y). Result is addressable as unsigned + 16-bit words. Will trigger an assert() if SkColorType is not + kRGBA_F16_SkColorType and is built with SK_DEBUG defined. + + Each word represents one color component encoded as a half float. + Four words correspond to one pixel. + + @param x column index, zero or greater, and less than width() + @param y row index, zero or greater, and less than height() + @return writable unsigned 16-bit pointer to first component of pixel + */ + uint16_t* writable_addrF16(int x, int y) const { + return reinterpret_cast(writable_addr64(x, y)); + } + + /** Copies a SkRect of pixels to dstPixels. Copy starts at (0, 0), and does not + exceed SkPixmap (width(), height()). + + dstInfo specifies width, height, SkColorType, SkAlphaType, and + SkColorSpace of destination. dstRowBytes specifics the gap from one destination + row to the next. Returns true if pixels are copied. Returns false if + dstInfo address equals nullptr, or dstRowBytes is less than dstInfo.minRowBytes(). + + Pixels are copied only if pixel conversion is possible. If SkPixmap colorType() is + kGray_8_SkColorType, or kAlpha_8_SkColorType; dstInfo.colorType() must match. + If SkPixmap colorType() is kGray_8_SkColorType, dstInfo.colorSpace() must match. + If SkPixmap alphaType() is kOpaque_SkAlphaType, dstInfo.alphaType() must + match. If SkPixmap colorSpace() is nullptr, dstInfo.colorSpace() must match. Returns + false if pixel conversion is not possible. + + Returns false if SkPixmap width() or height() is zero or negative. + + @param dstInfo destination width, height, SkColorType, SkAlphaType, SkColorSpace + @param dstPixels destination pixel storage + @param dstRowBytes destination row length + @return true if pixels are copied to dstPixels + */ + bool readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes) const { + return this->readPixels(dstInfo, dstPixels, dstRowBytes, 0, 0); + } + + /** Copies a SkRect of pixels to dstPixels. Copy starts at (srcX, srcY), and does not + exceed SkPixmap (width(), height()). + + dstInfo specifies width, height, SkColorType, SkAlphaType, and + SkColorSpace of destination. dstRowBytes specifics the gap from one destination + row to the next. Returns true if pixels are copied. Returns false if + dstInfo address equals nullptr, or dstRowBytes is less than dstInfo.minRowBytes(). + + Pixels are copied only if pixel conversion is possible. If SkPixmap colorType() is + kGray_8_SkColorType, or kAlpha_8_SkColorType; dstInfo.colorType() must match. + If SkPixmap colorType() is kGray_8_SkColorType, dstInfo.colorSpace() must match. + If SkPixmap alphaType() is kOpaque_SkAlphaType, dstInfo.alphaType() must + match. If SkPixmap colorSpace() is nullptr, dstInfo.colorSpace() must match. Returns + false if pixel conversion is not possible. + + srcX and srcY may be negative to copy only top or left of source. Returns + false if SkPixmap width() or height() is zero or negative. Returns false if: + abs(srcX) >= Pixmap width(), or if abs(srcY) >= Pixmap height(). + + @param dstInfo destination width, height, SkColorType, SkAlphaType, SkColorSpace + @param dstPixels destination pixel storage + @param dstRowBytes destination row length + @param srcX column index whose absolute value is less than width() + @param srcY row index whose absolute value is less than height() + @return true if pixels are copied to dstPixels + */ + bool readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes, int srcX, + int srcY) const; + + /** Copies a SkRect of pixels to dst. Copy starts at (srcX, srcY), and does not + exceed SkPixmap (width(), height()). dst specifies width, height, SkColorType, + SkAlphaType, and SkColorSpace of destination. Returns true if pixels are copied. + Returns false if dst address equals nullptr, or dst.rowBytes() is less than + dst SkImageInfo::minRowBytes. + + Pixels are copied only if pixel conversion is possible. If SkPixmap colorType() is + kGray_8_SkColorType, or kAlpha_8_SkColorType; dst.info().colorType must match. + If SkPixmap colorType() is kGray_8_SkColorType, dst.info().colorSpace must match. + If SkPixmap alphaType() is kOpaque_SkAlphaType, dst.info().alphaType must + match. If SkPixmap colorSpace() is nullptr, dst.info().colorSpace must match. Returns + false if pixel conversion is not possible. + + srcX and srcY may be negative to copy only top or left of source. Returns + false SkPixmap width() or height() is zero or negative. Returns false if: + abs(srcX) >= Pixmap width(), or if abs(srcY) >= Pixmap height(). + + @param dst SkImageInfo and pixel address to write to + @param srcX column index whose absolute value is less than width() + @param srcY row index whose absolute value is less than height() + @return true if pixels are copied to dst + */ + bool readPixels(const SkPixmap& dst, int srcX, int srcY) const { + return this->readPixels(dst.info(), dst.writable_addr(), dst.rowBytes(), srcX, srcY); + } + + /** Copies pixels inside bounds() to dst. dst specifies width, height, SkColorType, + SkAlphaType, and SkColorSpace of destination. Returns true if pixels are copied. + Returns false if dst address equals nullptr, or dst.rowBytes() is less than + dst SkImageInfo::minRowBytes. + + Pixels are copied only if pixel conversion is possible. If SkPixmap colorType() is + kGray_8_SkColorType, or kAlpha_8_SkColorType; dst SkColorType must match. + If SkPixmap colorType() is kGray_8_SkColorType, dst SkColorSpace must match. + If SkPixmap alphaType() is kOpaque_SkAlphaType, dst SkAlphaType must + match. If SkPixmap colorSpace() is nullptr, dst SkColorSpace must match. Returns + false if pixel conversion is not possible. + + Returns false if SkPixmap width() or height() is zero or negative. + + @param dst SkImageInfo and pixel address to write to + @return true if pixels are copied to dst + */ + bool readPixels(const SkPixmap& dst) const { + return this->readPixels(dst.info(), dst.writable_addr(), dst.rowBytes(), 0, 0); + } + + /** Copies SkBitmap to dst, scaling pixels to fit dst.width() and dst.height(), and + converting pixels to match dst.colorType() and dst.alphaType(). Returns true if + pixels are copied. Returns false if dst address is nullptr, or dst.rowBytes() is + less than dst SkImageInfo::minRowBytes. + + Pixels are copied only if pixel conversion is possible. If SkPixmap colorType() is + kGray_8_SkColorType, or kAlpha_8_SkColorType; dst SkColorType must match. + If SkPixmap colorType() is kGray_8_SkColorType, dst SkColorSpace must match. + If SkPixmap alphaType() is kOpaque_SkAlphaType, dst SkAlphaType must + match. If SkPixmap colorSpace() is nullptr, dst SkColorSpace must match. Returns + false if pixel conversion is not possible. + + Returns false if SkBitmap width() or height() is zero or negative. + + @param dst SkImageInfo and pixel address to write to + @return true if pixels are scaled to fit dst + + example: https://fiddle.skia.org/c/@Pixmap_scalePixels + */ + bool scalePixels(const SkPixmap& dst, const SkSamplingOptions&) const; + + /** Writes color to pixels bounded by subset; returns true on success. + Returns false if colorType() is kUnknown_SkColorType, or if subset does + not intersect bounds(). + + @param color sRGB unpremultiplied color to write + @param subset bounding integer SkRect of written pixels + @return true if pixels are changed + + example: https://fiddle.skia.org/c/@Pixmap_erase + */ + bool erase(SkColor color, const SkIRect& subset) const; + + /** Writes color to pixels inside bounds(); returns true on success. + Returns false if colorType() is kUnknown_SkColorType, or if bounds() + is empty. + + @param color sRGB unpremultiplied color to write + @return true if pixels are changed + */ + bool erase(SkColor color) const { return this->erase(color, this->bounds()); } + + /** Writes color to pixels bounded by subset; returns true on success. + if subset is nullptr, writes colors pixels inside bounds(). Returns false if + colorType() is kUnknown_SkColorType, if subset is not nullptr and does + not intersect bounds(), or if subset is nullptr and bounds() is empty. + + @param color unpremultiplied color to write + @param subset bounding integer SkRect of pixels to write; may be nullptr + @return true if pixels are changed + */ + bool erase(const SkColor4f& color, const SkIRect* subset = nullptr) const; + +private: + const void* fPixels; + size_t fRowBytes; + SkImageInfo fInfo; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPoint.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPoint.h new file mode 100644 index 0000000000..4c7e0943cd --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPoint.h @@ -0,0 +1,10 @@ +/* + * Copyright 2023 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + */ + +// SkPoint is part of the public API, but is also required by code in base. The following include +// forwarding allows SkPoint to participate in the API and for use by code in base. + +#include "include/private/base/SkPoint_impl.h" // IWYU pragma: export diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPoint3.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPoint3.h new file mode 100644 index 0000000000..abf8dfd9c9 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkPoint3.h @@ -0,0 +1,149 @@ +/* + * 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 SkPoint3_DEFINED +#define SkPoint3_DEFINED + +#include "include/core/SkScalar.h" +#include "include/private/base/SkAPI.h" +#include "include/private/base/SkFloatingPoint.h" + +struct SK_API SkPoint3 { + SkScalar fX, fY, fZ; + + static SkPoint3 Make(SkScalar x, SkScalar y, SkScalar z) { + SkPoint3 pt; + pt.set(x, y, z); + return pt; + } + + SkScalar x() const { return fX; } + SkScalar y() const { return fY; } + SkScalar z() const { return fZ; } + + void set(SkScalar x, SkScalar y, SkScalar z) { fX = x; fY = y; fZ = z; } + + friend bool operator==(const SkPoint3& a, const SkPoint3& b) { + return a.fX == b.fX && a.fY == b.fY && a.fZ == b.fZ; + } + + friend bool operator!=(const SkPoint3& a, const SkPoint3& b) { + return !(a == b); + } + + /** Returns the Euclidian distance from (0,0,0) to (x,y,z) + */ + static SkScalar Length(SkScalar x, SkScalar y, SkScalar z); + + /** Return the Euclidian distance from (0,0,0) to the point + */ + SkScalar length() const { return SkPoint3::Length(fX, fY, fZ); } + + /** Set the point (vector) to be unit-length in the same direction as it + already points. If the point has a degenerate length (i.e., nearly 0) + then set it to (0,0,0) and return false; otherwise return true. + */ + bool normalize(); + + /** Return a new point whose X, Y and Z coordinates are scaled. + */ + SkPoint3 makeScale(SkScalar scale) const { + SkPoint3 p; + p.set(scale * fX, scale * fY, scale * fZ); + return p; + } + + /** Scale the point's coordinates by scale. + */ + void scale(SkScalar value) { + fX *= value; + fY *= value; + fZ *= value; + } + + /** Return a new point whose X, Y and Z coordinates are the negative of the + original point's + */ + SkPoint3 operator-() const { + SkPoint3 neg; + neg.fX = -fX; + neg.fY = -fY; + neg.fZ = -fZ; + return neg; + } + + /** Returns a new point whose coordinates are the difference between + a and b (i.e., a - b) + */ + friend SkPoint3 operator-(const SkPoint3& a, const SkPoint3& b) { + return { a.fX - b.fX, a.fY - b.fY, a.fZ - b.fZ }; + } + + /** Returns a new point whose coordinates are the sum of a and b (a + b) + */ + friend SkPoint3 operator+(const SkPoint3& a, const SkPoint3& b) { + return { a.fX + b.fX, a.fY + b.fY, a.fZ + b.fZ }; + } + + /** Add v's coordinates to the point's + */ + void operator+=(const SkPoint3& v) { + fX += v.fX; + fY += v.fY; + fZ += v.fZ; + } + + /** Subtract v's coordinates from the point's + */ + void operator-=(const SkPoint3& v) { + fX -= v.fX; + fY -= v.fY; + fZ -= v.fZ; + } + + friend SkPoint3 operator*(SkScalar t, SkPoint3 p) { + return { t * p.fX, t * p.fY, t * p.fZ }; + } + + /** Returns true if fX, fY, and fZ are measurable values. + + @return true for values other than infinities and NaN + */ + bool isFinite() const { + return SkIsFinite(fX, fY, fZ); + } + + /** Returns the dot product of a and b, treating them as 3D vectors + */ + static SkScalar DotProduct(const SkPoint3& a, const SkPoint3& b) { + return a.fX * b.fX + a.fY * b.fY + a.fZ * b.fZ; + } + + SkScalar dot(const SkPoint3& vec) const { + return DotProduct(*this, vec); + } + + /** Returns the cross product of a and b, treating them as 3D vectors + */ + static SkPoint3 CrossProduct(const SkPoint3& a, const SkPoint3& b) { + SkPoint3 result; + result.fX = a.fY*b.fZ - a.fZ*b.fY; + result.fY = a.fZ*b.fX - a.fX*b.fZ; + result.fZ = a.fX*b.fY - a.fY*b.fX; + + return result; + } + + SkPoint3 cross(const SkPoint3& vec) const { + return CrossProduct(*this, vec); + } +}; + +typedef SkPoint3 SkVector3; +typedef SkPoint3 SkColor3f; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkRRect.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkRRect.h new file mode 100644 index 0000000000..b6dc32c5b7 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkRRect.h @@ -0,0 +1,516 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkRRect_DEFINED +#define SkRRect_DEFINED + +#include "include/core/SkPoint.h" +#include "include/core/SkRect.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" + +#include +#include + +class SkMatrix; +class SkString; + +/** \class SkRRect + SkRRect describes a rounded rectangle with a bounds and a pair of radii for each corner. + The bounds and radii can be set so that SkRRect describes: a rectangle with sharp corners; + a circle; an oval; or a rectangle with one or more rounded corners. + + SkRRect allows implementing CSS properties that describe rounded corners. + SkRRect may have up to eight different radii, one for each axis on each of its four + corners. + + SkRRect may modify the provided parameters when initializing bounds and radii. + 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. +*/ +class SK_API SkRRect { +public: + + /** Initializes bounds at (0, 0), the origin, with zero width and height. + Initializes corner radii to (0, 0), and sets type of kEmpty_Type. + + @return empty SkRRect + */ + SkRRect() = default; + + /** Initializes to copy of rrect bounds and corner radii. + + @param rrect bounds and corner to copy + @return copy of rrect + */ + SkRRect(const SkRRect& rrect) = default; + + /** Copies rrect bounds and corner radii. + + @param rrect bounds and corner to copy + @return copy of rrect + */ + SkRRect& operator=(const SkRRect& rrect) = default; + + /** \enum SkRRect::Type + Type describes possible specializations of SkRRect. Each Type is + exclusive; a SkRRect may only have one type. + + Type members become progressively less restrictive; larger values of + Type have more degrees of freedom than smaller values. + */ + enum Type { + kEmpty_Type, //!< zero width or height + kRect_Type, //!< non-zero width and height, and zeroed radii + kOval_Type, //!< non-zero width and height filled with radii + kSimple_Type, //!< non-zero width and height with equal radii + kNinePatch_Type, //!< non-zero width and height with axis-aligned radii + kComplex_Type, //!< non-zero width and height with arbitrary radii + kLastType = kComplex_Type, //!< largest Type value + }; + + Type getType() const { + SkASSERT(this->isValid()); + return static_cast(fType); + } + + Type type() const { return this->getType(); } + + inline bool isEmpty() const { return kEmpty_Type == this->getType(); } + inline bool isRect() const { return kRect_Type == this->getType(); } + inline bool isOval() const { return kOval_Type == this->getType(); } + inline bool isSimple() const { return kSimple_Type == this->getType(); } + inline bool isNinePatch() const { return kNinePatch_Type == this->getType(); } + inline bool isComplex() const { return kComplex_Type == this->getType(); } + + /** Returns span on the x-axis. This does not check if result fits in 32-bit float; + result may be infinity. + + @return rect().fRight minus rect().fLeft + */ + SkScalar width() const { return fRect.width(); } + + /** Returns span on the y-axis. This does not check if result fits in 32-bit float; + result may be infinity. + + @return rect().fBottom minus rect().fTop + */ + SkScalar height() const { return fRect.height(); } + + /** Returns top-left corner radii. If type() returns kEmpty_Type, kRect_Type, + kOval_Type, or kSimple_Type, returns a value representative of all corner radii. + If type() returns kNinePatch_Type or kComplex_Type, at least one of the + remaining three corners has a different value. + + @return corner radii for simple types + */ + SkVector getSimpleRadii() const { + return fRadii[0]; + } + + /** Sets bounds to zero width and height at (0, 0), the origin. Sets + corner radii to zero and sets type to kEmpty_Type. + */ + void setEmpty() { *this = SkRRect(); } + + /** Sets bounds to sorted rect, and sets corner radii to zero. + If set bounds has width and height, and sets type to kRect_Type; + otherwise, sets type to kEmpty_Type. + + @param rect bounds to set + */ + void setRect(const SkRect& rect) { + if (!this->initializeRect(rect)) { + return; + } + + memset(fRadii, 0, sizeof(fRadii)); + fType = kRect_Type; + + SkASSERT(this->isValid()); + } + + /** Initializes bounds at (0, 0), the origin, with zero width and height. + Initializes corner radii to (0, 0), and sets type of kEmpty_Type. + + @return empty SkRRect + */ + static SkRRect MakeEmpty() { return SkRRect(); } + + /** Initializes to copy of r bounds and zeroes corner radii. + + @param r bounds to copy + @return copy of r + */ + static SkRRect MakeRect(const SkRect& r) { + SkRRect rr; + rr.setRect(r); + return rr; + } + + /** Sets bounds to oval, x-axis radii to half oval.width(), and all y-axis radii + to half oval.height(). If oval bounds is empty, sets to kEmpty_Type. + Otherwise, sets to kOval_Type. + + @param oval bounds of oval + @return oval + */ + static SkRRect MakeOval(const SkRect& oval) { + SkRRect rr; + rr.setOval(oval); + return rr; + } + + /** Sets to rounded rectangle with the same radii for all four corners. + If rect is empty, sets to kEmpty_Type. + Otherwise, if xRad and yRad are zero, sets to kRect_Type. + Otherwise, if xRad is at least half rect.width() and yRad is at least half + rect.height(), sets to kOval_Type. + Otherwise, sets to kSimple_Type. + + @param rect bounds of rounded rectangle + @param xRad x-axis radius of corners + @param yRad y-axis radius of corners + @return rounded rectangle + */ + static SkRRect MakeRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) { + SkRRect rr; + rr.setRectXY(rect, xRad, yRad); + return rr; + } + + /** Sets bounds to oval, x-axis radii to half oval.width(), and all y-axis radii + to half oval.height(). If oval bounds is empty, sets to kEmpty_Type. + Otherwise, sets to kOval_Type. + + @param oval bounds of oval + */ + void setOval(const SkRect& oval); + + /** Sets to rounded rectangle with the same radii for all four corners. + If rect is empty, sets to kEmpty_Type. + Otherwise, if xRad or yRad is zero, sets to kRect_Type. + Otherwise, if xRad is at least half rect.width() and yRad is at least half + rect.height(), sets to kOval_Type. + Otherwise, sets to kSimple_Type. + + @param rect bounds of rounded rectangle + @param xRad x-axis radius of corners + @param yRad y-axis radius of corners + + example: https://fiddle.skia.org/c/@RRect_setRectXY + */ + void setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad); + + /** Sets bounds to rect. Sets radii to (leftRad, topRad), (rightRad, topRad), + (rightRad, bottomRad), (leftRad, bottomRad). + + If rect is empty, sets to kEmpty_Type. + Otherwise, if leftRad and rightRad are zero, sets to kRect_Type. + Otherwise, if topRad and bottomRad are zero, sets to kRect_Type. + Otherwise, if leftRad and rightRad are equal and at least half rect.width(), and + topRad and bottomRad are equal at least half rect.height(), sets to kOval_Type. + Otherwise, if leftRad and rightRad are equal, and topRad and bottomRad are equal, + sets to kSimple_Type. Otherwise, sets to kNinePatch_Type. + + Nine patch refers to the nine parts defined by the radii: one center rectangle, + four edge patches, and four corner patches. + + @param rect bounds of rounded rectangle + @param leftRad left-top and left-bottom x-axis radius + @param topRad left-top and right-top y-axis radius + @param rightRad right-top and right-bottom x-axis radius + @param bottomRad left-bottom and right-bottom y-axis radius + */ + void setNinePatch(const SkRect& rect, SkScalar leftRad, SkScalar topRad, + SkScalar rightRad, SkScalar bottomRad); + + /** Sets bounds to rect. Sets radii array for individual control of all for corners. + + If rect is empty, sets to kEmpty_Type. + Otherwise, if one of each corner radii are zero, sets to kRect_Type. + Otherwise, if all x-axis radii are equal and at least half rect.width(), and + all y-axis radii are equal at least half rect.height(), sets to kOval_Type. + Otherwise, if all x-axis radii are equal, and all y-axis radii are equal, + sets to kSimple_Type. Otherwise, sets to kNinePatch_Type. + + @param rect bounds of rounded rectangle + @param radii corner x-axis and y-axis radii + + example: https://fiddle.skia.org/c/@RRect_setRectRadii + */ + void setRectRadii(const SkRect& rect, const SkVector radii[4]); + + /** \enum SkRRect::Corner + The radii are stored: top-left, top-right, bottom-right, bottom-left. + */ + enum Corner { + kUpperLeft_Corner, //!< index of top-left corner radii + kUpperRight_Corner, //!< index of top-right corner radii + kLowerRight_Corner, //!< index of bottom-right corner radii + kLowerLeft_Corner, //!< index of bottom-left corner radii + }; + + /** Returns bounds. Bounds may have zero width or zero height. Bounds right is + greater than or equal to left; bounds bottom is greater than or equal to top. + Result is identical to getBounds(). + + @return bounding box + */ + const SkRect& rect() const { return fRect; } + + /** Returns scalar pair for radius of curve on x-axis and y-axis for one corner. + Both radii may be zero. If not zero, both are positive and finite. + + @return x-axis and y-axis radii for one corner + */ + SkVector radii(Corner corner) const { return fRadii[corner]; } + + /** Returns bounds. Bounds may have zero width or zero height. Bounds right is + greater than or equal to left; bounds bottom is greater than or equal to top. + Result is identical to rect(). + + @return bounding box + */ + const SkRect& getBounds() const { return fRect; } + + /** Returns true if bounds and radii in a are equal to bounds and radii in b. + + a and b are not equal if either contain NaN. a and b are equal if members + contain zeroes with different signs. + + @param a SkRect bounds and radii to compare + @param b SkRect bounds and radii to compare + @return true if members are equal + */ + friend bool operator==(const SkRRect& a, const SkRRect& b) { + return a.fRect == b.fRect && SkScalarsEqual(&a.fRadii[0].fX, &b.fRadii[0].fX, 8); + } + + /** Returns true if bounds and radii in a are not equal to bounds and radii in b. + + a and b are not equal if either contain NaN. a and b are equal if members + contain zeroes with different signs. + + @param a SkRect bounds and radii to compare + @param b SkRect bounds and radii to compare + @return true if members are not equal + */ + friend bool operator!=(const SkRRect& a, const SkRRect& b) { + return a.fRect != b.fRect || !SkScalarsEqual(&a.fRadii[0].fX, &b.fRadii[0].fX, 8); + } + + /** Copies SkRRect to dst, then insets dst bounds by dx and dy, and adjusts dst + radii by dx and dy. dx and dy may be positive, negative, or zero. dst may be + SkRRect. + + If either corner radius is zero, the corner has no curvature and is unchanged. + Otherwise, if adjusted radius becomes negative, pins radius to zero. + If dx exceeds half dst bounds width, dst bounds left and right are set to + bounds x-axis center. If dy exceeds half dst bounds height, dst bounds top and + bottom are set to bounds y-axis center. + + If dx or dy cause the bounds to become infinite, dst bounds is zeroed. + + @param dx added to rect().fLeft, and subtracted from rect().fRight + @param dy added to rect().fTop, and subtracted from rect().fBottom + @param dst insets bounds and radii + + example: https://fiddle.skia.org/c/@RRect_inset + */ + void inset(SkScalar dx, SkScalar dy, SkRRect* dst) const; + + /** Insets bounds by dx and dy, and adjusts radii by dx and dy. dx and dy may be + positive, negative, or zero. + + If either corner radius is zero, the corner has no curvature and is unchanged. + Otherwise, if adjusted radius becomes negative, pins radius to zero. + If dx exceeds half bounds width, bounds left and right are set to + bounds x-axis center. If dy exceeds half bounds height, bounds top and + bottom are set to bounds y-axis center. + + If dx or dy cause the bounds to become infinite, bounds is zeroed. + + @param dx added to rect().fLeft, and subtracted from rect().fRight + @param dy added to rect().fTop, and subtracted from rect().fBottom + */ + void inset(SkScalar dx, SkScalar dy) { + this->inset(dx, dy, this); + } + + /** Outsets dst bounds by dx and dy, and adjusts radii by dx and dy. dx and dy may be + positive, negative, or zero. + + If either corner radius is zero, the corner has no curvature and is unchanged. + Otherwise, if adjusted radius becomes negative, pins radius to zero. + If dx exceeds half dst bounds width, dst bounds left and right are set to + bounds x-axis center. If dy exceeds half dst bounds height, dst bounds top and + bottom are set to bounds y-axis center. + + If dx or dy cause the bounds to become infinite, dst bounds is zeroed. + + @param dx subtracted from rect().fLeft, and added to rect().fRight + @param dy subtracted from rect().fTop, and added to rect().fBottom + @param dst outset bounds and radii + */ + void outset(SkScalar dx, SkScalar dy, SkRRect* dst) const { + this->inset(-dx, -dy, dst); + } + + /** Outsets bounds by dx and dy, and adjusts radii by dx and dy. dx and dy may be + positive, negative, or zero. + + If either corner radius is zero, the corner has no curvature and is unchanged. + Otherwise, if adjusted radius becomes negative, pins radius to zero. + If dx exceeds half bounds width, bounds left and right are set to + bounds x-axis center. If dy exceeds half bounds height, bounds top and + bottom are set to bounds y-axis center. + + If dx or dy cause the bounds to become infinite, bounds is zeroed. + + @param dx subtracted from rect().fLeft, and added to rect().fRight + @param dy subtracted from rect().fTop, and added to rect().fBottom + */ + void outset(SkScalar dx, SkScalar dy) { + this->inset(-dx, -dy, this); + } + + /** Translates SkRRect by (dx, dy). + + @param dx offset added to rect().fLeft and rect().fRight + @param dy offset added to rect().fTop and rect().fBottom + */ + void offset(SkScalar dx, SkScalar dy) { + fRect.offset(dx, dy); + } + + /** Returns SkRRect translated by (dx, dy). + + @param dx offset added to rect().fLeft and rect().fRight + @param dy offset added to rect().fTop and rect().fBottom + @return SkRRect bounds offset by (dx, dy), with unchanged corner radii + */ + [[nodiscard]] SkRRect makeOffset(SkScalar dx, SkScalar dy) const { + return SkRRect(fRect.makeOffset(dx, dy), fRadii, fType); + } + + /** Returns true if rect is inside the bounds and corner radii, and if + SkRRect and rect are not empty. + + @param rect area tested for containment + @return true if SkRRect contains rect + + example: https://fiddle.skia.org/c/@RRect_contains + */ + bool contains(const SkRect& rect) const; + + /** Returns true if bounds and radii values are finite and describe a SkRRect + SkRRect::Type that matches getType(). All SkRRect methods construct valid types, + even if the input values are not valid. Invalid SkRRect data can only + be generated by corrupting memory. + + @return true if bounds and radii match type() + + example: https://fiddle.skia.org/c/@RRect_isValid + */ + bool isValid() const; + + static constexpr size_t kSizeInMemory = 12 * sizeof(SkScalar); + + /** Writes SkRRect to buffer. Writes kSizeInMemory bytes, and returns + kSizeInMemory, the number of bytes written. + + @param buffer storage for SkRRect + @return bytes written, kSizeInMemory + + example: https://fiddle.skia.org/c/@RRect_writeToMemory + */ + size_t writeToMemory(void* buffer) const; + + /** Reads SkRRect from buffer, reading kSizeInMemory bytes. + Returns kSizeInMemory, bytes read if length is at least kSizeInMemory. + Otherwise, returns zero. + + @param buffer memory to read from + @param length size of buffer + @return bytes read, or 0 if length is less than kSizeInMemory + + example: https://fiddle.skia.org/c/@RRect_readFromMemory + */ + size_t readFromMemory(const void* buffer, size_t length); + + /** Transforms by SkRRect by matrix, storing result in dst. + Returns true if SkRRect transformed can be represented by another SkRRect. + Returns false if matrix contains transformations that are not axis aligned. + + Asserts in debug builds if SkRRect equals dst. + + @param matrix SkMatrix specifying the transform + @param dst SkRRect to store the result + @return true if transformation succeeded. + + example: https://fiddle.skia.org/c/@RRect_transform + */ + bool transform(const SkMatrix& matrix, SkRRect* dst) const; + + /** Writes text representation of SkRRect to standard output. + Set asHex true to generate exact binary representations + of floating point numbers. + + @param asHex true if SkScalar values are written as hexadecimal + + example: https://fiddle.skia.org/c/@RRect_dump + */ + void dump(bool asHex) const; + SkString dumpToString(bool asHex) const; + + /** Writes text representation of SkRRect to standard output. The representation + may be directly compiled as C++ code. Floating point values are written + with limited precision; it may not be possible to reconstruct original + SkRRect from output. + */ + void dump() const { this->dump(false); } + + /** Writes text representation of SkRRect to standard output. The representation + may be directly compiled as C++ code. Floating point values are written + in hexadecimal to preserve their exact bit pattern. The output reconstructs the + original SkRRect. + */ + void dumpHex() const { this->dump(true); } + +private: + static bool AreRectAndRadiiValid(const SkRect&, const SkVector[4]); + + SkRRect(const SkRect& rect, const SkVector radii[4], int32_t type) + : fRect(rect) + , fRadii{radii[0], radii[1], radii[2], radii[3]} + , fType(type) {} + + /** + * Initializes fRect. If the passed in rect is not finite or empty the rrect will be fully + * initialized and false is returned. Otherwise, just fRect is initialized and true is returned. + */ + bool initializeRect(const SkRect&); + + void computeType(); + bool checkCornerContainment(SkScalar x, SkScalar y) const; + // Returns true if the radii had to be scaled to fit rect + bool scaleRadii(); + + SkRect fRect = SkRect::MakeEmpty(); + // Radii order is UL, UR, LR, LL. Use Corner enum to index into fRadii[] + 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; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkRSXform.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkRSXform.h new file mode 100644 index 0000000000..de6bafd358 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkRSXform.h @@ -0,0 +1,71 @@ +/* + * 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 SkRSXform_DEFINED +#define SkRSXform_DEFINED + +#include "include/core/SkPoint.h" +#include "include/core/SkScalar.h" +#include "include/core/SkSize.h" +#include "include/private/base/SkAPI.h" + +/** + * A compressed form of a rotation+scale matrix. + * + * [ fSCos -fSSin fTx ] + * [ fSSin fSCos fTy ] + * [ 0 0 1 ] + */ +struct SK_API SkRSXform { + static SkRSXform Make(SkScalar scos, SkScalar ssin, SkScalar tx, SkScalar ty) { + SkRSXform xform = { scos, ssin, tx, ty }; + return xform; + } + + /* + * Initialize a new xform based on the scale, rotation (in radians), final tx,ty location + * and anchor-point ax,ay within the src quad. + * + * Note: the anchor point is not normalized (e.g. 0...1) but is in pixels of the src image. + */ + static SkRSXform MakeFromRadians(SkScalar scale, SkScalar radians, SkScalar tx, SkScalar ty, + SkScalar ax, SkScalar ay) { + const SkScalar s = SkScalarSin(radians) * scale; + const SkScalar c = SkScalarCos(radians) * scale; + return Make(c, s, tx + -c * ax + s * ay, ty + -s * ax - c * ay); + } + + SkScalar fSCos; + SkScalar fSSin; + SkScalar fTx; + SkScalar fTy; + + bool rectStaysRect() const { + return 0 == fSCos || 0 == fSSin; + } + + void setIdentity() { + fSCos = 1; + fSSin = fTx = fTy = 0; + } + + void set(SkScalar scos, SkScalar ssin, SkScalar tx, SkScalar ty) { + fSCos = scos; + fSSin = ssin; + fTx = tx; + fTy = ty; + } + + void toQuad(SkScalar width, SkScalar height, SkPoint quad[4]) const; + void toQuad(const SkSize& size, SkPoint quad[4]) const { + this->toQuad(size.width(), size.height(), quad); + } + void toTriStrip(SkScalar width, SkScalar height, SkPoint strip[4]) const; +}; + +#endif + diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkRasterHandleAllocator.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkRasterHandleAllocator.h new file mode 100644 index 0000000000..6fe121a6de --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkRasterHandleAllocator.h @@ -0,0 +1,94 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkRasterHandleAllocator_DEFINED +#define SkRasterHandleAllocator_DEFINED + +#include "include/core/SkImageInfo.h" + +class SkBitmap; +class SkCanvas; +class SkMatrix; +class SkSurfaceProps; + +/** + * If a client wants to control the allocation of raster layers in a canvas, it should subclass + * SkRasterHandleAllocator. This allocator performs two tasks: + * 1. controls how the memory for the pixels is allocated + * 2. associates a "handle" to a private object that can track the matrix/clip of the SkCanvas + * + * This example allocates a canvas, and defers to the allocator to create the base layer. + * + * std::unique_ptr canvas = SkRasterHandleAllocator::MakeCanvas( + * SkImageInfo::Make(...), + * std::make_unique(...), + * nullptr); + * + * If you have already allocated the base layer (and its handle, release-proc etc.) then you + * can pass those in using the last parameter to MakeCanvas(). + * + * Regardless of how the base layer is allocated, each time canvas->saveLayer() is called, + * your allocator's allocHandle() will be called. + */ +class SK_API SkRasterHandleAllocator { +public: + virtual ~SkRasterHandleAllocator() = default; + + // The value that is returned to clients of the canvas that has this allocator installed. + typedef void* Handle; + + struct Rec { + // When the allocation goes out of scope, this proc is called to free everything associated + // with it: the pixels, the "handle", etc. This is passed the pixel address and fReleaseCtx. + void (*fReleaseProc)(void* pixels, void* ctx); + void* fReleaseCtx; // context passed to fReleaseProc + void* fPixels; // pixels for this allocation + size_t fRowBytes; // rowbytes for these pixels + Handle fHandle; // public handle returned by SkCanvas::accessTopRasterHandle() + }; + + /** + * Given a requested info, allocate the corresponding pixels/rowbytes, and whatever handle + * is desired to give clients access to those pixels. The rec also contains a proc and context + * which will be called when this allocation goes out of scope. + * + * e.g. + * when canvas->saveLayer() is called, the allocator will be called to allocate the pixels + * for the layer. When canvas->restore() is called, the fReleaseProc will be called. + */ + virtual bool allocHandle(const SkImageInfo&, Rec*) = 0; + + /** + * Clients access the handle for a given layer by calling SkCanvas::accessTopRasterHandle(). + * To allow the handle to reflect the current matrix/clip in the canvs, updateHandle() is + * is called. The subclass is responsible to update the handle as it sees fit. + */ + virtual void updateHandle(Handle, const SkMatrix&, const SkIRect&) = 0; + + /** + * This creates a canvas which will use the allocator to manage pixel allocations, including + * all calls to saveLayer(). + * + * If rec is non-null, then it will be used as the base-layer of pixels/handle. + * If rec is null, then the allocator will be called for the base-layer as well. + */ + static std::unique_ptr MakeCanvas(std::unique_ptr, + const SkImageInfo&, const Rec* rec = nullptr, + const SkSurfaceProps* props = nullptr); + +protected: + SkRasterHandleAllocator() = default; + SkRasterHandleAllocator(const SkRasterHandleAllocator&) = delete; + SkRasterHandleAllocator& operator=(const SkRasterHandleAllocator&) = delete; + +private: + friend class SkBitmapDevice; + + Handle allocBitmap(const SkImageInfo&, SkBitmap*); +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkRect.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkRect.h new file mode 100644 index 0000000000..65c053a5e1 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkRect.h @@ -0,0 +1,1374 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkRect_DEFINED +#define SkRect_DEFINED + +#include "include/core/SkPoint.h" +#include "include/core/SkSize.h" +#include "include/core/SkTypes.h" +#include "include/private/base/SkFloatingPoint.h" +#include "include/private/base/SkSafe32.h" +#include "include/private/base/SkTFitsIn.h" + +#include +#include +#include +#include + +struct SkRect; + +/** \struct SkIRect + SkIRect holds four 32-bit integer coordinates describing the upper and + lower bounds of a rectangle. SkIRect may be created from outer bounds or + from position, width, and height. SkIRect describes an area; if its right + is less than or equal to its left, or if its bottom is less than or equal to + its top, it is considered empty. +*/ +struct SK_API SkIRect { + int32_t fLeft = 0; //!< smaller x-axis bounds + int32_t fTop = 0; //!< smaller y-axis bounds + int32_t fRight = 0; //!< larger x-axis bounds + int32_t fBottom = 0; //!< larger y-axis bounds + + /** Returns constructed SkIRect set to (0, 0, 0, 0). + Many other rectangles are empty; if left is equal to or greater than right, + or if top is equal to or greater than bottom. Setting all members to zero + is a convenience, but does not designate a special empty rectangle. + + @return bounds (0, 0, 0, 0) + */ + [[nodiscard]] static constexpr SkIRect MakeEmpty() { + return SkIRect{0, 0, 0, 0}; + } + + /** Returns constructed SkIRect set to (0, 0, w, h). Does not validate input; w or h + may be negative. + + @param w width of constructed SkIRect + @param h height of constructed SkIRect + @return bounds (0, 0, w, h) + */ + [[nodiscard]] static constexpr SkIRect MakeWH(int32_t w, int32_t h) { + return SkIRect{0, 0, w, h}; + } + + /** Returns constructed SkIRect set to (0, 0, size.width(), size.height()). + Does not validate input; size.width() or size.height() may be negative. + + @param size values for SkIRect width and height + @return bounds (0, 0, size.width(), size.height()) + */ + [[nodiscard]] static constexpr SkIRect MakeSize(const SkISize& size) { + return SkIRect{0, 0, size.fWidth, size.fHeight}; + } + + /** Returns constructed SkIRect set to (pt.x(), pt.y(), pt.x() + size.width(), + pt.y() + size.height()). Does not validate input; size.width() or size.height() may be + negative. + + @param pt values for SkIRect fLeft and fTop + @param size values for SkIRect width and height + @return bounds at pt with width and height of size + */ + [[nodiscard]] static constexpr SkIRect MakePtSize(SkIPoint pt, SkISize size) { + return MakeXYWH(pt.x(), pt.y(), size.width(), size.height()); + } + + /** Returns constructed SkIRect set to (l, t, r, b). Does not sort input; SkIRect may + result in fLeft greater than fRight, or fTop greater than fBottom. + + @param l integer stored in fLeft + @param t integer stored in fTop + @param r integer stored in fRight + @param b integer stored in fBottom + @return bounds (l, t, r, b) + */ + [[nodiscard]] static constexpr SkIRect MakeLTRB(int32_t l, int32_t t, int32_t r, int32_t b) { + return SkIRect{l, t, r, b}; + } + + /** Returns constructed SkIRect set to: (x, y, x + w, y + h). + Does not validate input; w or h may be negative. + + @param x stored in fLeft + @param y stored in fTop + @param w added to x and stored in fRight + @param h added to y and stored in fBottom + @return bounds at (x, y) with width w and height h + */ + [[nodiscard]] static constexpr SkIRect MakeXYWH(int32_t x, int32_t y, int32_t w, int32_t h) { + return { x, y, Sk32_sat_add(x, w), Sk32_sat_add(y, h) }; + } + + /** Returns left edge of SkIRect, if sorted. + Call sort() to reverse fLeft and fRight if needed. + + @return fLeft + */ + constexpr int32_t left() const { return fLeft; } + + /** Returns top edge of SkIRect, if sorted. Call isEmpty() to see if SkIRect may be invalid, + and sort() to reverse fTop and fBottom if needed. + + @return fTop + */ + constexpr int32_t top() const { return fTop; } + + /** Returns right edge of SkIRect, if sorted. + Call sort() to reverse fLeft and fRight if needed. + + @return fRight + */ + constexpr int32_t right() const { return fRight; } + + /** Returns bottom edge of SkIRect, if sorted. Call isEmpty() to see if SkIRect may be invalid, + and sort() to reverse fTop and fBottom if needed. + + @return fBottom + */ + constexpr int32_t bottom() const { return fBottom; } + + /** Returns left edge of SkIRect, if sorted. Call isEmpty() to see if SkIRect may be invalid, + and sort() to reverse fLeft and fRight if needed. + + @return fLeft + */ + constexpr int32_t x() const { return fLeft; } + + /** Returns top edge of SkIRect, if sorted. Call isEmpty() to see if SkIRect may be invalid, + and sort() to reverse fTop and fBottom if needed. + + @return fTop + */ + constexpr int32_t y() const { return fTop; } + + // Experimental + constexpr SkIPoint topLeft() const { return {fLeft, fTop}; } + + /** Returns span on the x-axis. This does not check if SkIRect is sorted, or if + result fits in 32-bit signed integer; result may be negative. + + @return fRight minus fLeft + */ + constexpr int32_t width() const { return Sk32_can_overflow_sub(fRight, fLeft); } + + /** Returns span on the y-axis. This does not check if SkIRect is sorted, or if + result fits in 32-bit signed integer; result may be negative. + + @return fBottom minus fTop + */ + constexpr int32_t height() const { return Sk32_can_overflow_sub(fBottom, fTop); } + + /** Returns spans on the x-axis and y-axis. This does not check if SkIRect is sorted, + or if result fits in 32-bit signed integer; result may be negative. + + @return SkISize (width, height) + */ + constexpr SkISize size() const { return SkISize::Make(this->width(), this->height()); } + + /** Returns span on the x-axis. This does not check if SkIRect is sorted, so the + result may be negative. This is safer than calling width() since width() might + overflow in its calculation. + + @return fRight minus fLeft cast to int64_t + */ + constexpr int64_t width64() const { return (int64_t)fRight - (int64_t)fLeft; } + + /** Returns span on the y-axis. This does not check if SkIRect is sorted, so the + result may be negative. This is safer than calling height() since height() might + overflow in its calculation. + + @return fBottom minus fTop cast to int64_t + */ + constexpr int64_t height64() const { return (int64_t)fBottom - (int64_t)fTop; } + + /** Returns true if fLeft is equal to or greater than fRight, or if fTop is equal + to or greater than fBottom. Call sort() to reverse rectangles with negative + width64() or height64(). + + @return true if width64() or height64() are zero or negative + */ + bool isEmpty64() const { return fRight <= fLeft || fBottom <= fTop; } + + /** Returns true if width() or height() are zero or negative. + + @return true if width() or height() are zero or negative + */ + bool isEmpty() const { + int64_t w = this->width64(); + int64_t h = this->height64(); + if (w <= 0 || h <= 0) { + return true; + } + // Return true if either exceeds int32_t + return !SkTFitsIn(w | h); + } + + /** Returns true if all members in a: fLeft, fTop, fRight, and fBottom; are + identical to corresponding members in b. + + @param a SkIRect to compare + @param b SkIRect to compare + @return true if members are equal + */ + friend bool operator==(const SkIRect& a, const SkIRect& b) { + return a.fLeft == b.fLeft && a.fTop == b.fTop && + a.fRight == b.fRight && a.fBottom == b.fBottom; + } + + /** Returns true if any member in a: fLeft, fTop, fRight, and fBottom; is not + identical to the corresponding member in b. + + @param a SkIRect to compare + @param b SkIRect to compare + @return true if members are not equal + */ + friend bool operator!=(const SkIRect& a, const SkIRect& b) { + return a.fLeft != b.fLeft || a.fTop != b.fTop || + a.fRight != b.fRight || a.fBottom != b.fBottom; + } + + /** Sets SkIRect to (0, 0, 0, 0). + + Many other rectangles are empty; if left is equal to or greater than right, + or if top is equal to or greater than bottom. Setting all members to zero + is a convenience, but does not designate a special empty rectangle. + */ + void setEmpty() { memset(this, 0, sizeof(*this)); } + + /** Sets SkIRect to (left, top, right, bottom). + left and right are not sorted; left is not necessarily less than right. + top and bottom are not sorted; top is not necessarily less than bottom. + + @param left stored in fLeft + @param top stored in fTop + @param right stored in fRight + @param bottom stored in fBottom + */ + void setLTRB(int32_t left, int32_t top, int32_t right, int32_t bottom) { + fLeft = left; + fTop = top; + fRight = right; + fBottom = bottom; + } + + /** Sets SkIRect to: (x, y, x + width, y + height). + Does not validate input; width or height may be negative. + + @param x stored in fLeft + @param y stored in fTop + @param width added to x and stored in fRight + @param height added to y and stored in fBottom + */ + void setXYWH(int32_t x, int32_t y, int32_t width, int32_t height) { + fLeft = x; + fTop = y; + fRight = Sk32_sat_add(x, width); + fBottom = Sk32_sat_add(y, height); + } + + void setWH(int32_t width, int32_t height) { + fLeft = 0; + fTop = 0; + fRight = width; + fBottom = height; + } + + void setSize(SkISize size) { + fLeft = 0; + fTop = 0; + fRight = size.width(); + fBottom = size.height(); + } + + /** Returns SkIRect offset by (dx, dy). + + If dx is negative, SkIRect returned is moved to the left. + If dx is positive, SkIRect returned is moved to the right. + If dy is negative, SkIRect returned is moved upward. + If dy is positive, SkIRect returned is moved downward. + + @param dx offset added to fLeft and fRight + @param dy offset added to fTop and fBottom + @return SkIRect offset by dx and dy, with original width and height + */ + constexpr SkIRect makeOffset(int32_t dx, int32_t dy) const { + return { + Sk32_sat_add(fLeft, dx), Sk32_sat_add(fTop, dy), + Sk32_sat_add(fRight, dx), Sk32_sat_add(fBottom, dy), + }; + } + + /** Returns SkIRect offset by (offset.x(), offset.y()). + + If offset.x() is negative, SkIRect returned is moved to the left. + If offset.x() is positive, SkIRect returned is moved to the right. + If offset.y() is negative, SkIRect returned is moved upward. + If offset.y() is positive, SkIRect returned is moved downward. + + @param offset translation vector + @return SkIRect translated by offset, with original width and height + */ + constexpr SkIRect makeOffset(SkIVector offset) const { + return this->makeOffset(offset.x(), offset.y()); + } + + /** Returns SkIRect, inset by (dx, dy). + + If dx is negative, SkIRect returned is wider. + If dx is positive, SkIRect returned is narrower. + If dy is negative, SkIRect returned is taller. + If dy is positive, SkIRect returned is shorter. + + @param dx offset added to fLeft and subtracted from fRight + @param dy offset added to fTop and subtracted from fBottom + @return SkIRect inset symmetrically left and right, top and bottom + */ + SkIRect makeInset(int32_t dx, int32_t dy) const { + return { + Sk32_sat_add(fLeft, dx), Sk32_sat_add(fTop, dy), + Sk32_sat_sub(fRight, dx), Sk32_sat_sub(fBottom, dy), + }; + } + + /** Returns SkIRect, outset by (dx, dy). + + If dx is negative, SkIRect returned is narrower. + If dx is positive, SkIRect returned is wider. + If dy is negative, SkIRect returned is shorter. + If dy is positive, SkIRect returned is taller. + + @param dx offset subtracted to fLeft and added from fRight + @param dy offset subtracted to fTop and added from fBottom + @return SkIRect outset symmetrically left and right, top and bottom + */ + SkIRect makeOutset(int32_t dx, int32_t dy) const { + return { + Sk32_sat_sub(fLeft, dx), Sk32_sat_sub(fTop, dy), + Sk32_sat_add(fRight, dx), Sk32_sat_add(fBottom, dy), + }; + } + + /** Offsets SkIRect by adding dx to fLeft, fRight; and by adding dy to fTop, fBottom. + + If dx is negative, moves SkIRect returned to the left. + If dx is positive, moves SkIRect returned to the right. + If dy is negative, moves SkIRect returned upward. + If dy is positive, moves SkIRect returned downward. + + @param dx offset added to fLeft and fRight + @param dy offset added to fTop and fBottom + */ + void offset(int32_t dx, int32_t dy) { + fLeft = Sk32_sat_add(fLeft, dx); + fTop = Sk32_sat_add(fTop, dy); + fRight = Sk32_sat_add(fRight, dx); + fBottom = Sk32_sat_add(fBottom, dy); + } + + /** Offsets SkIRect by adding delta.fX to fLeft, fRight; and by adding delta.fY to + fTop, fBottom. + + If delta.fX is negative, moves SkIRect returned to the left. + If delta.fX is positive, moves SkIRect returned to the right. + If delta.fY is negative, moves SkIRect returned upward. + If delta.fY is positive, moves SkIRect returned downward. + + @param delta offset added to SkIRect + */ + void offset(const SkIPoint& delta) { + this->offset(delta.fX, delta.fY); + } + + /** Offsets SkIRect so that fLeft equals newX, and fTop equals newY. width and height + are unchanged. + + @param newX stored in fLeft, preserving width() + @param newY stored in fTop, preserving height() + */ + void offsetTo(int32_t newX, int32_t newY) { + fRight = Sk64_pin_to_s32((int64_t)fRight + newX - fLeft); + fBottom = Sk64_pin_to_s32((int64_t)fBottom + newY - fTop); + fLeft = newX; + fTop = newY; + } + + /** Insets SkIRect by (dx,dy). + + If dx is positive, makes SkIRect narrower. + If dx is negative, makes SkIRect wider. + If dy is positive, makes SkIRect shorter. + If dy is negative, makes SkIRect taller. + + @param dx offset added to fLeft and subtracted from fRight + @param dy offset added to fTop and subtracted from fBottom + */ + void inset(int32_t dx, int32_t dy) { + fLeft = Sk32_sat_add(fLeft, dx); + fTop = Sk32_sat_add(fTop, dy); + fRight = Sk32_sat_sub(fRight, dx); + fBottom = Sk32_sat_sub(fBottom, dy); + } + + /** Outsets SkIRect by (dx, dy). + + If dx is positive, makes SkIRect wider. + If dx is negative, makes SkIRect narrower. + If dy is positive, makes SkIRect taller. + If dy is negative, makes SkIRect shorter. + + @param dx subtracted to fLeft and added from fRight + @param dy subtracted to fTop and added from fBottom + */ + void outset(int32_t dx, int32_t dy) { this->inset(-dx, -dy); } + + /** Adjusts SkIRect by adding dL to fLeft, dT to fTop, dR to fRight, and dB to fBottom. + + If dL is positive, narrows SkIRect on the left. If negative, widens it on the left. + If dT is positive, shrinks SkIRect on the top. If negative, lengthens it on the top. + If dR is positive, narrows SkIRect on the right. If negative, widens it on the right. + If dB is positive, shrinks SkIRect on the bottom. If negative, lengthens it on the bottom. + + The resulting SkIRect is not checked for validity. Thus, if the resulting SkIRect left is + greater than right, the SkIRect will be considered empty. Call sort() after this call + if that is not the desired behavior. + + @param dL offset added to fLeft + @param dT offset added to fTop + @param dR offset added to fRight + @param dB offset added to fBottom + */ + void adjust(int32_t dL, int32_t dT, int32_t dR, int32_t dB) { + fLeft = Sk32_sat_add(fLeft, dL); + fTop = Sk32_sat_add(fTop, dT); + fRight = Sk32_sat_add(fRight, dR); + fBottom = Sk32_sat_add(fBottom, dB); + } + + /** Returns true if: fLeft <= x < fRight && fTop <= y < fBottom. + Returns false if SkIRect is empty. + + Considers input to describe constructed SkIRect: (x, y, x + 1, y + 1) and + returns true if constructed area is completely enclosed by SkIRect area. + + @param x test SkIPoint x-coordinate + @param y test SkIPoint y-coordinate + @return true if (x, y) is inside SkIRect + */ + bool contains(int32_t x, int32_t y) const { + return x >= fLeft && x < fRight && y >= fTop && y < fBottom; + } + + /** Returns true if SkIRect contains r. + Returns false if SkIRect is empty or r is empty. + + SkIRect contains r when SkIRect area completely includes r area. + + @param r SkIRect contained + @return true if all sides of SkIRect are outside r + */ + bool contains(const SkIRect& r) const { + return !r.isEmpty() && !this->isEmpty() && // check for empties + fLeft <= r.fLeft && fTop <= r.fTop && + fRight >= r.fRight && fBottom >= r.fBottom; + } + + /** Returns true if SkIRect contains r. + Returns false if SkIRect is empty or r is empty. + + SkIRect contains r when SkIRect area completely includes r area. + + @param r SkRect contained + @return true if all sides of SkIRect are outside r + */ + inline bool contains(const SkRect& r) const; + + /** Returns true if SkIRect contains construction. + Asserts if SkIRect is empty or construction is empty, and if SK_DEBUG is defined. + + Return is undefined if SkIRect is empty or construction is empty. + + @param r SkIRect contained + @return true if all sides of SkIRect are outside r + */ + bool containsNoEmptyCheck(const SkIRect& r) const { + SkASSERT(fLeft < fRight && fTop < fBottom); + SkASSERT(r.fLeft < r.fRight && r.fTop < r.fBottom); + return fLeft <= r.fLeft && fTop <= r.fTop && fRight >= r.fRight && fBottom >= r.fBottom; + } + + /** Returns true if SkIRect intersects r, and sets SkIRect to intersection. + Returns false if SkIRect does not intersect r, and leaves SkIRect unchanged. + + Returns false if either r or SkIRect is empty, leaving SkIRect unchanged. + + @param r limit of result + @return true if r and SkIRect have area in common + */ + bool intersect(const SkIRect& r) { + return this->intersect(*this, r); + } + + /** Returns true if a intersects b, and sets SkIRect to intersection. + Returns false if a does not intersect b, and leaves SkIRect unchanged. + + Returns false if either a or b is empty, leaving SkIRect unchanged. + + @param a SkIRect to intersect + @param b SkIRect to intersect + @return true if a and b have area in common + */ + [[nodiscard]] bool intersect(const SkIRect& a, const SkIRect& b); + + /** Returns true if a intersects b. + Returns false if either a or b is empty, or do not intersect. + + @param a SkIRect to intersect + @param b SkIRect to intersect + @return true if a and b have area in common + */ + static bool Intersects(const SkIRect& a, const SkIRect& b) { + return SkIRect{}.intersect(a, b); + } + + /** Sets SkIRect to the union of itself and r. + + Has no effect if r is empty. Otherwise, if SkIRect is empty, sets SkIRect to r. + + @param r expansion SkIRect + + example: https://fiddle.skia.org/c/@IRect_join_2 + */ + void join(const SkIRect& r); + + /** Swaps fLeft and fRight if fLeft is greater than fRight; and swaps + fTop and fBottom if fTop is greater than fBottom. Result may be empty, + and width() and height() will be zero or positive. + */ + void sort() { + using std::swap; + if (fLeft > fRight) { + swap(fLeft, fRight); + } + if (fTop > fBottom) { + swap(fTop, fBottom); + } + } + + /** Returns SkIRect with fLeft and fRight swapped if fLeft is greater than fRight; and + with fTop and fBottom swapped if fTop is greater than fBottom. Result may be empty; + and width() and height() will be zero or positive. + + @return sorted SkIRect + */ + SkIRect makeSorted() const { + return MakeLTRB(std::min(fLeft, fRight), std::min(fTop, fBottom), + std::max(fLeft, fRight), std::max(fTop, fBottom)); + } +}; + +/** \struct SkRect + SkRect holds four float coordinates describing the upper and + lower bounds of a rectangle. SkRect may be created from outer bounds or + from position, width, and height. SkRect describes an area; if its right + is less than or equal to its left, or if its bottom is less than or equal to + its top, it is considered empty. +*/ +struct SK_API SkRect { + float fLeft = 0; //!< smaller x-axis bounds + float fTop = 0; //!< smaller y-axis bounds + float fRight = 0; //!< larger x-axis bounds + float fBottom = 0; //!< larger y-axis bounds + + /** Returns constructed SkRect set to (0, 0, 0, 0). + Many other rectangles are empty; if left is equal to or greater than right, + or if top is equal to or greater than bottom. Setting all members to zero + is a convenience, but does not designate a special empty rectangle. + + @return bounds (0, 0, 0, 0) + */ + [[nodiscard]] static constexpr SkRect MakeEmpty() { + return SkRect{0, 0, 0, 0}; + } + + /** Returns constructed SkRect set to float values (0, 0, w, h). Does not + validate input; w or h may be negative. + + Passing integer values may generate a compiler warning since SkRect cannot + represent 32-bit integers exactly. Use SkIRect for an exact integer rectangle. + + @param w float width of constructed SkRect + @param h float height of constructed SkRect + @return bounds (0, 0, w, h) + */ + [[nodiscard]] static constexpr SkRect MakeWH(float w, float h) { + return SkRect{0, 0, w, h}; + } + + /** Returns constructed SkRect set to integer values (0, 0, w, h). Does not validate + input; w or h may be negative. + + Use to avoid a compiler warning that input may lose precision when stored. + Use SkIRect for an exact integer rectangle. + + @param w integer width of constructed SkRect + @param h integer height of constructed SkRect + @return bounds (0, 0, w, h) + */ + [[nodiscard]] static SkRect MakeIWH(int w, int h) { + return {0, 0, static_cast(w), static_cast(h)}; + } + + /** Returns constructed SkRect set to (0, 0, size.width(), size.height()). Does not + validate input; size.width() or size.height() may be negative. + + @param size float values for SkRect width and height + @return bounds (0, 0, size.width(), size.height()) + */ + [[nodiscard]] static constexpr SkRect MakeSize(const SkSize& size) { + return SkRect{0, 0, size.fWidth, size.fHeight}; + } + + /** Returns constructed SkRect set to (l, t, r, b). Does not sort input; SkRect may + result in fLeft greater than fRight, or fTop greater than fBottom. + + @param l float stored in fLeft + @param t float stored in fTop + @param r float stored in fRight + @param b float stored in fBottom + @return bounds (l, t, r, b) + */ + [[nodiscard]] static constexpr SkRect MakeLTRB(float l, float t, float r, float b) { + return SkRect {l, t, r, b}; + } + + /** Returns constructed SkRect set to (x, y, x + w, y + h). + Does not validate input; w or h may be negative. + + @param x stored in fLeft + @param y stored in fTop + @param w added to x and stored in fRight + @param h added to y and stored in fBottom + @return bounds at (x, y) with width w and height h + */ + [[nodiscard]] static constexpr SkRect MakeXYWH(float x, float y, float w, float h) { + return SkRect {x, y, x + w, y + h}; + } + + /** Returns constructed SkIRect set to (0, 0, size.width(), size.height()). + Does not validate input; size.width() or size.height() may be negative. + + @param size integer values for SkRect width and height + @return bounds (0, 0, size.width(), size.height()) + */ + static SkRect Make(const SkISize& size) { + return MakeIWH(size.width(), size.height()); + } + + /** Returns constructed SkIRect set to irect, promoting integers to float. + Does not validate input; fLeft may be greater than fRight, fTop may be greater + than fBottom. + + @param irect integer unsorted bounds + @return irect members converted to float + */ + [[nodiscard]] static SkRect Make(const SkIRect& irect) { + return { + static_cast(irect.fLeft), static_cast(irect.fTop), + static_cast(irect.fRight), static_cast(irect.fBottom) + }; + } + + /** Returns true if fLeft is equal to or greater than fRight, or if fTop is equal + to or greater than fBottom. Call sort() to reverse rectangles with negative + width() or height(). + + @return true if width() or height() are zero or negative + */ + bool isEmpty() const { + // We write it as the NOT of a non-empty rect, so we will return true if any values + // are NaN. + return !(fLeft < fRight && fTop < fBottom); + } + + /** Returns true if fLeft is equal to or less than fRight, or if fTop is equal + to or less than fBottom. Call sort() to reverse rectangles with negative + width() or height(). + + @return true if width() or height() are zero or positive + */ + bool isSorted() const { return fLeft <= fRight && fTop <= fBottom; } + + /** Returns true if all values in the rectangle are finite. + + @return true if no member is infinite or NaN + */ + bool isFinite() const { + return SkIsFinite(fLeft, fTop, fRight, fBottom); + } + + /** Returns left edge of SkRect, if sorted. Call isSorted() to see if SkRect is valid. + Call sort() to reverse fLeft and fRight if needed. + + @return fLeft + */ + constexpr float x() const { return fLeft; } + + /** Returns top edge of SkRect, if sorted. Call isEmpty() to see if SkRect may be invalid, + and sort() to reverse fTop and fBottom if needed. + + @return fTop + */ + constexpr float y() const { return fTop; } + + /** Returns left edge of SkRect, if sorted. Call isSorted() to see if SkRect is valid. + Call sort() to reverse fLeft and fRight if needed. + + @return fLeft + */ + constexpr float left() const { return fLeft; } + + /** Returns top edge of SkRect, if sorted. Call isEmpty() to see if SkRect may be invalid, + and sort() to reverse fTop and fBottom if needed. + + @return fTop + */ + constexpr float top() const { return fTop; } + + /** Returns right edge of SkRect, if sorted. Call isSorted() to see if SkRect is valid. + Call sort() to reverse fLeft and fRight if needed. + + @return fRight + */ + constexpr float right() const { return fRight; } + + /** Returns bottom edge of SkRect, if sorted. Call isEmpty() to see if SkRect may be invalid, + and sort() to reverse fTop and fBottom if needed. + + @return fBottom + */ + constexpr float bottom() const { return fBottom; } + + /** Returns span on the x-axis. This does not check if SkRect is sorted, or if + result fits in 32-bit float; result may be negative or infinity. + + @return fRight minus fLeft + */ + constexpr float width() const { return fRight - fLeft; } + + /** Returns span on the y-axis. This does not check if SkRect is sorted, or if + result fits in 32-bit float; result may be negative or infinity. + + @return fBottom minus fTop + */ + constexpr float height() const { return fBottom - fTop; } + + /** Returns average of left edge and right edge. Result does not change if SkRect + is sorted. Result may overflow to infinity if SkRect is far from the origin. + + @return midpoint on x-axis + */ + constexpr float centerX() const { + return sk_float_midpoint(fLeft, fRight); + } + + /** Returns average of top edge and bottom edge. Result does not change if SkRect + is sorted. + + @return midpoint on y-axis + */ + constexpr float centerY() const { + return sk_float_midpoint(fTop, fBottom); + } + + /** Returns the point this->centerX(), this->centerY(). + @return rectangle center + */ + constexpr SkPoint center() const { return {this->centerX(), this->centerY()}; } + + /** Returns true if all members in a: fLeft, fTop, fRight, and fBottom; are + equal to the corresponding members in b. + + a and b are not equal if either contain NaN. a and b are equal if members + contain zeroes with different signs. + + @param a SkRect to compare + @param b SkRect to compare + @return true if members are equal + */ + friend bool operator==(const SkRect& a, const SkRect& b) { + return a.fLeft == b.fLeft && + a.fTop == b.fTop && + a.fRight == b.fRight && + a.fBottom == b.fBottom; + } + + /** Returns true if any in a: fLeft, fTop, fRight, and fBottom; does not + equal the corresponding members in b. + + a and b are not equal if either contain NaN. a and b are equal if members + contain zeroes with different signs. + + @param a SkRect to compare + @param b SkRect to compare + @return true if members are not equal + */ + friend bool operator!=(const SkRect& a, const SkRect& b) { + return !(a == b); + } + + /** Returns four points in quad that enclose SkRect ordered as: top-left, top-right, + bottom-right, bottom-left. + + TODO: Consider adding parameter to control whether quad is clockwise or counterclockwise. + + @param quad storage for corners of SkRect + + example: https://fiddle.skia.org/c/@Rect_toQuad + */ + void toQuad(SkPoint quad[4]) const; + + /** Sets SkRect to (0, 0, 0, 0). + + Many other rectangles are empty; if left is equal to or greater than right, + or if top is equal to or greater than bottom. Setting all members to zero + is a convenience, but does not designate a special empty rectangle. + */ + void setEmpty() { *this = MakeEmpty(); } + + /** Sets SkRect to src, promoting src members from integer to float. + Very large values in src may lose precision. + + @param src integer SkRect + */ + void set(const SkIRect& src) { + fLeft = src.fLeft; + fTop = src.fTop; + fRight = src.fRight; + fBottom = src.fBottom; + } + + /** Sets SkRect to (left, top, right, bottom). + left and right are not sorted; left is not necessarily less than right. + top and bottom are not sorted; top is not necessarily less than bottom. + + @param left stored in fLeft + @param top stored in fTop + @param right stored in fRight + @param bottom stored in fBottom + */ + void setLTRB(float left, float top, float right, float bottom) { + fLeft = left; + fTop = top; + fRight = right; + fBottom = bottom; + } + + /** Sets to bounds of SkPoint array with count entries. If count is zero or smaller, + or if SkPoint array contains an infinity or NaN, sets to (0, 0, 0, 0). + + Result is either empty or sorted: fLeft is less than or equal to fRight, and + fTop is less than or equal to fBottom. + + @param pts SkPoint array + @param count entries in array + */ + void setBounds(const SkPoint pts[], int count) { + (void)this->setBoundsCheck(pts, count); + } + + /** Sets to bounds of SkPoint array with count entries. Returns false if count is + zero or smaller, or if SkPoint array contains an infinity or NaN; in these cases + sets SkRect to (0, 0, 0, 0). + + Result is either empty or sorted: fLeft is less than or equal to fRight, and + fTop is less than or equal to fBottom. + + @param pts SkPoint array + @param count entries in array + @return true if all SkPoint values are finite + + example: https://fiddle.skia.org/c/@Rect_setBoundsCheck + */ + bool setBoundsCheck(const SkPoint pts[], int count); + + /** Sets to bounds of SkPoint pts array with count entries. If any SkPoint in pts + contains infinity or NaN, all SkRect dimensions are set to NaN. + + @param pts SkPoint array + @param count entries in array + + example: https://fiddle.skia.org/c/@Rect_setBoundsNoCheck + */ + void setBoundsNoCheck(const SkPoint pts[], int count); + + /** Sets bounds to the smallest SkRect enclosing SkPoint p0 and p1. The result is + sorted and may be empty. Does not check to see if values are finite. + + @param p0 corner to include + @param p1 corner to include + */ + void set(const SkPoint& p0, const SkPoint& p1) { + fLeft = std::min(p0.fX, p1.fX); + fRight = std::max(p0.fX, p1.fX); + fTop = std::min(p0.fY, p1.fY); + fBottom = std::max(p0.fY, p1.fY); + } + + /** Sets SkRect to (x, y, x + width, y + height). + Does not validate input; width or height may be negative. + + @param x stored in fLeft + @param y stored in fTop + @param width added to x and stored in fRight + @param height added to y and stored in fBottom + */ + void setXYWH(float x, float y, float width, float height) { + fLeft = x; + fTop = y; + fRight = x + width; + fBottom = y + height; + } + + /** Sets SkRect to (0, 0, width, height). Does not validate input; + width or height may be negative. + + @param width stored in fRight + @param height stored in fBottom + */ + void setWH(float width, float height) { + fLeft = 0; + fTop = 0; + fRight = width; + fBottom = height; + } + void setIWH(int32_t width, int32_t height) { + this->setWH(width, height); + } + + /** Returns SkRect offset by (dx, dy). + + If dx is negative, SkRect returned is moved to the left. + If dx is positive, SkRect returned is moved to the right. + If dy is negative, SkRect returned is moved upward. + If dy is positive, SkRect returned is moved downward. + + @param dx added to fLeft and fRight + @param dy added to fTop and fBottom + @return SkRect offset on axes, with original width and height + */ + constexpr SkRect makeOffset(float dx, float dy) const { + return MakeLTRB(fLeft + dx, fTop + dy, fRight + dx, fBottom + dy); + } + + /** Returns SkRect offset by v. + + @param v added to rect + @return SkRect offset on axes, with original width and height + */ + constexpr SkRect makeOffset(SkVector v) const { return this->makeOffset(v.x(), v.y()); } + + /** Returns SkRect, inset by (dx, dy). + + If dx is negative, SkRect returned is wider. + If dx is positive, SkRect returned is narrower. + If dy is negative, SkRect returned is taller. + If dy is positive, SkRect returned is shorter. + + @param dx added to fLeft and subtracted from fRight + @param dy added to fTop and subtracted from fBottom + @return SkRect inset symmetrically left and right, top and bottom + */ + SkRect makeInset(float dx, float dy) const { + return MakeLTRB(fLeft + dx, fTop + dy, fRight - dx, fBottom - dy); + } + + /** Returns SkRect, outset by (dx, dy). + + If dx is negative, SkRect returned is narrower. + If dx is positive, SkRect returned is wider. + If dy is negative, SkRect returned is shorter. + If dy is positive, SkRect returned is taller. + + @param dx subtracted to fLeft and added from fRight + @param dy subtracted to fTop and added from fBottom + @return SkRect outset symmetrically left and right, top and bottom + */ + SkRect makeOutset(float dx, float dy) const { + return MakeLTRB(fLeft - dx, fTop - dy, fRight + dx, fBottom + dy); + } + + /** Offsets SkRect by adding dx to fLeft, fRight; and by adding dy to fTop, fBottom. + + If dx is negative, moves SkRect to the left. + If dx is positive, moves SkRect to the right. + If dy is negative, moves SkRect upward. + If dy is positive, moves SkRect downward. + + @param dx offset added to fLeft and fRight + @param dy offset added to fTop and fBottom + */ + void offset(float dx, float dy) { + fLeft += dx; + fTop += dy; + fRight += dx; + fBottom += dy; + } + + /** Offsets SkRect by adding delta.fX to fLeft, fRight; and by adding delta.fY to + fTop, fBottom. + + If delta.fX is negative, moves SkRect to the left. + If delta.fX is positive, moves SkRect to the right. + If delta.fY is negative, moves SkRect upward. + If delta.fY is positive, moves SkRect downward. + + @param delta added to SkRect + */ + void offset(const SkPoint& delta) { + this->offset(delta.fX, delta.fY); + } + + /** Offsets SkRect so that fLeft equals newX, and fTop equals newY. width and height + are unchanged. + + @param newX stored in fLeft, preserving width() + @param newY stored in fTop, preserving height() + */ + void offsetTo(float newX, float newY) { + fRight += newX - fLeft; + fBottom += newY - fTop; + fLeft = newX; + fTop = newY; + } + + /** Insets SkRect by (dx, dy). + + If dx is positive, makes SkRect narrower. + If dx is negative, makes SkRect wider. + If dy is positive, makes SkRect shorter. + If dy is negative, makes SkRect taller. + + @param dx added to fLeft and subtracted from fRight + @param dy added to fTop and subtracted from fBottom + */ + void inset(float dx, float dy) { + fLeft += dx; + fTop += dy; + fRight -= dx; + fBottom -= dy; + } + + /** Outsets SkRect by (dx, dy). + + If dx is positive, makes SkRect wider. + If dx is negative, makes SkRect narrower. + If dy is positive, makes SkRect taller. + If dy is negative, makes SkRect shorter. + + @param dx subtracted to fLeft and added from fRight + @param dy subtracted to fTop and added from fBottom + */ + void outset(float dx, float dy) { this->inset(-dx, -dy); } + + /** Returns true if SkRect intersects r, and sets SkRect to intersection. + Returns false if SkRect does not intersect r, and leaves SkRect unchanged. + + Returns false if either r or SkRect is empty, leaving SkRect unchanged. + + @param r limit of result + @return true if r and SkRect have area in common + + example: https://fiddle.skia.org/c/@Rect_intersect + */ + bool intersect(const SkRect& r); + + /** Returns true if a intersects b, and sets SkRect to intersection. + Returns false if a does not intersect b, and leaves SkRect unchanged. + + Returns false if either a or b is empty, leaving SkRect unchanged. + + @param a SkRect to intersect + @param b SkRect to intersect + @return true if a and b have area in common + */ + [[nodiscard]] bool intersect(const SkRect& a, const SkRect& b); + + +private: + static bool Intersects(float al, float at, float ar, float ab, + float bl, float bt, float br, float bb) { + float L = std::max(al, bl); + float R = std::min(ar, br); + float T = std::max(at, bt); + float B = std::min(ab, bb); + return L < R && T < B; + } + +public: + + /** Returns true if SkRect intersects r. + Returns false if either r or SkRect is empty, or do not intersect. + + @param r SkRect to intersect + @return true if r and SkRect have area in common + */ + bool intersects(const SkRect& r) const { + return Intersects(fLeft, fTop, fRight, fBottom, + r.fLeft, r.fTop, r.fRight, r.fBottom); + } + + /** Returns true if a intersects b. + Returns false if either a or b is empty, or do not intersect. + + @param a SkRect to intersect + @param b SkRect to intersect + @return true if a and b have area in common + */ + static bool Intersects(const SkRect& a, const SkRect& b) { + return Intersects(a.fLeft, a.fTop, a.fRight, a.fBottom, + b.fLeft, b.fTop, b.fRight, b.fBottom); + } + + /** Sets SkRect to the union of itself and r. + + Has no effect if r is empty. Otherwise, if SkRect is empty, sets + SkRect to r. + + @param r expansion SkRect + + example: https://fiddle.skia.org/c/@Rect_join_2 + */ + void join(const SkRect& r); + + /** Sets SkRect to the union of itself and r. + + Asserts if r is empty and SK_DEBUG is defined. + If SkRect is empty, sets SkRect to r. + + May produce incorrect results if r is empty. + + @param r expansion SkRect + */ + void joinNonEmptyArg(const SkRect& r) { + SkASSERT(!r.isEmpty()); + // if we are empty, just assign + if (fLeft >= fRight || fTop >= fBottom) { + *this = r; + } else { + this->joinPossiblyEmptyRect(r); + } + } + + /** Sets SkRect to the union of itself and the construction. + + May produce incorrect results if SkRect or r is empty. + + @param r expansion SkRect + */ + void joinPossiblyEmptyRect(const SkRect& r) { + fLeft = std::min(fLeft, r.left()); + fTop = std::min(fTop, r.top()); + fRight = std::max(fRight, r.right()); + fBottom = std::max(fBottom, r.bottom()); + } + + /** Returns true if: fLeft <= x < fRight && fTop <= y < fBottom. + Returns false if SkRect is empty. + + @param x test SkPoint x-coordinate + @param y test SkPoint y-coordinate + @return true if (x, y) is inside SkRect + */ + bool contains(float x, float y) const { + return x >= fLeft && x < fRight && y >= fTop && y < fBottom; + } + + /** Returns true if SkRect contains r. + Returns false if SkRect is empty or r is empty. + + SkRect contains r when SkRect area completely includes r area. + + @param r SkRect contained + @return true if all sides of SkRect are outside r + */ + bool contains(const SkRect& r) const { + // todo: can we eliminate the this->isEmpty check? + return !r.isEmpty() && !this->isEmpty() && + fLeft <= r.fLeft && fTop <= r.fTop && + fRight >= r.fRight && fBottom >= r.fBottom; + } + + /** Returns true if SkRect contains r. + Returns false if SkRect is empty or r is empty. + + SkRect contains r when SkRect area completely includes r area. + + @param r SkIRect contained + @return true if all sides of SkRect are outside r + */ + bool contains(const SkIRect& r) const { + // todo: can we eliminate the this->isEmpty check? + return !r.isEmpty() && !this->isEmpty() && + fLeft <= r.fLeft && fTop <= r.fTop && + fRight >= r.fRight && fBottom >= r.fBottom; + } + + /** Sets SkIRect by adding 0.5 and discarding the fractional portion of SkRect + members, using (sk_float_round2int(fLeft), sk_float_round2int(fTop), + sk_float_round2int(fRight), sk_float_round2int(fBottom)). + + @param dst storage for SkIRect + */ + void round(SkIRect* dst) const { + SkASSERT(dst); + dst->setLTRB(sk_float_round2int(fLeft), sk_float_round2int(fTop), + sk_float_round2int(fRight), sk_float_round2int(fBottom)); + } + + /** Sets SkIRect by discarding the fractional portion of fLeft and fTop; and rounding + up fRight and fBottom, using + (sk_float_floor2int(fLeft), sk_float_floor2int(fTop), + sk_float_ceil2int(fRight), sk_float_ceil2int(fBottom)). + + @param dst storage for SkIRect + */ + void roundOut(SkIRect* dst) const { + SkASSERT(dst); + dst->setLTRB(sk_float_floor2int(fLeft), sk_float_floor2int(fTop), + sk_float_ceil2int(fRight), sk_float_ceil2int(fBottom)); + } + + /** Sets SkRect by discarding the fractional portion of fLeft and fTop; and rounding + up fRight and fBottom, using + (std::floor(fLeft), std::floor(fTop), + std::ceil(fRight), std::ceil(fBottom)). + + @param dst storage for SkRect + */ + void roundOut(SkRect* dst) const { + dst->setLTRB(std::floor(fLeft), std::floor(fTop), + std::ceil(fRight), std::ceil(fBottom)); + } + + /** Sets SkRect by rounding up fLeft and fTop; and discarding the fractional portion + of fRight and fBottom, using + (sk_float_ceil2int(fLeft), sk_float_ceil2int(fTop), + sk_float_floor2int(fRight), sk_float_floor2int(fBottom)). + + @param dst storage for SkIRect + */ + void roundIn(SkIRect* dst) const { + SkASSERT(dst); + dst->setLTRB(sk_float_ceil2int(fLeft), sk_float_ceil2int(fTop), + sk_float_floor2int(fRight), sk_float_floor2int(fBottom)); + } + + /** Returns SkIRect by adding 0.5 and discarding the fractional portion of SkRect + members, using (sk_float_round2int(fLeft), sk_float_round2int(fTop), + sk_float_round2int(fRight), sk_float_round2int(fBottom)). + + @return rounded SkIRect + */ + SkIRect round() const { + SkIRect ir; + this->round(&ir); + return ir; + } + + /** Sets SkIRect by discarding the fractional portion of fLeft and fTop; and rounding + up fRight and fBottom, using + (sk_float_floor2int(fLeft), sk_float_floor2int(fTop), + sk_float_ceil2int(fRight), sk_float_ceil2int(fBottom)). + + @return rounded SkIRect + */ + SkIRect roundOut() const { + SkIRect ir; + this->roundOut(&ir); + return ir; + } + /** Sets SkIRect by rounding up fLeft and fTop; and discarding the fractional portion + of fRight and fBottom, using + (sk_float_ceil2int(fLeft), sk_float_ceil2int(fTop), + sk_float_floor2int(fRight), sk_float_floor2int(fBottom)). + + @return rounded SkIRect + */ + SkIRect roundIn() const { + SkIRect ir; + this->roundIn(&ir); + return ir; + } + + /** Swaps fLeft and fRight if fLeft is greater than fRight; and swaps + fTop and fBottom if fTop is greater than fBottom. Result may be empty; + and width() and height() will be zero or positive. + */ + void sort() { + using std::swap; + if (fLeft > fRight) { + swap(fLeft, fRight); + } + + if (fTop > fBottom) { + swap(fTop, fBottom); + } + } + + /** Returns SkRect with fLeft and fRight swapped if fLeft is greater than fRight; and + with fTop and fBottom swapped if fTop is greater than fBottom. Result may be empty; + and width() and height() will be zero or positive. + + @return sorted SkRect + */ + SkRect makeSorted() const { + return MakeLTRB(std::min(fLeft, fRight), std::min(fTop, fBottom), + std::max(fLeft, fRight), std::max(fTop, fBottom)); + } + + /** Returns pointer to first float in SkRect, to treat it as an array with four + entries. + + @return pointer to fLeft + */ + const float* asScalars() const { return &fLeft; } + + /** Writes text representation of SkRect to standard output. Set asHex to true to + generate exact binary representations of floating point numbers. + + @param asHex true if SkScalar values are written as hexadecimal + + example: https://fiddle.skia.org/c/@Rect_dump + */ + void dump(bool asHex) const; + + /** Writes text representation of SkRect to standard output. The representation may be + directly compiled as C++ code. Floating point values are written + with limited precision; it may not be possible to reconstruct original SkRect + from output. + */ + void dump() const { this->dump(false); } + + /** Writes text representation of SkRect to standard output. The representation may be + directly compiled as C++ code. Floating point values are written + in hexadecimal to preserve their exact bit pattern. The output reconstructs the + original SkRect. + + Use instead of dump() when submitting + */ + void dumpHex() const { this->dump(true); } +}; + +inline bool SkIRect::contains(const SkRect& r) const { + return !r.isEmpty() && !this->isEmpty() && // check for empties + fLeft <= r.fLeft && fTop <= r.fTop && + fRight >= r.fRight && fBottom >= r.fBottom; +} + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkRefCnt.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkRefCnt.h new file mode 100644 index 0000000000..34274a461d --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkRefCnt.h @@ -0,0 +1,389 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkRefCnt_DEFINED +#define SkRefCnt_DEFINED + +#include "include/core/SkTypes.h" +#include "include/private/base/SkDebug.h" + +#include +#include +#include +#include +#include +#include + +/** \class SkRefCntBase + + SkRefCntBase is the base class for objects that may be shared by multiple + objects. When an existing owner wants to share a reference, it calls ref(). + When an owner wants to release its reference, it calls unref(). When the + shared object's reference count goes to zero as the result of an unref() + call, its (virtual) destructor is called. It is an error for the + destructor to be called explicitly (or via the object going out of scope on + the stack or calling delete) if getRefCnt() > 1. +*/ +class SK_API SkRefCntBase { +public: + /** Default construct, initializing the reference count to 1. + */ + SkRefCntBase() : fRefCnt(1) {} + + /** Destruct, asserting that the reference count is 1. + */ + virtual ~SkRefCntBase() { + #ifdef SK_DEBUG + SkASSERTF(this->getRefCnt() == 1, "fRefCnt was %d", this->getRefCnt()); + // illegal value, to catch us if we reuse after delete + fRefCnt.store(0, std::memory_order_relaxed); + #endif + } + + /** May return true if the caller is the only owner. + * Ensures that all previous owner's actions are complete. + */ + bool unique() const { + if (1 == fRefCnt.load(std::memory_order_acquire)) { + // The acquire barrier is only really needed if we return true. It + // prevents code conditioned on the result of unique() from running + // until previous owners are all totally done calling unref(). + return true; + } + return false; + } + + /** Increment the reference count. Must be balanced by a call to unref(). + */ + void ref() const { + SkASSERT(this->getRefCnt() > 0); + // No barrier required. + (void)fRefCnt.fetch_add(+1, std::memory_order_relaxed); + } + + /** Decrement the reference count. If the reference count is 1 before the + decrement, then delete the object. Note that if this is the case, then + the object needs to have been allocated via new, and not on the stack. + */ + void unref() const { + SkASSERT(this->getRefCnt() > 0); + // A release here acts in place of all releases we "should" have been doing in ref(). + if (1 == fRefCnt.fetch_add(-1, std::memory_order_acq_rel)) { + // Like unique(), the acquire is only needed on success, to make sure + // code in internal_dispose() doesn't happen before the decrement. + this->internal_dispose(); + } + } + +private: + +#ifdef SK_DEBUG + /** Return the reference count. Use only for debugging. */ + int32_t getRefCnt() const { + return fRefCnt.load(std::memory_order_relaxed); + } +#endif + + /** + * Called when the ref count goes to 0. + */ + virtual void internal_dispose() const { + #ifdef SK_DEBUG + SkASSERT(0 == this->getRefCnt()); + fRefCnt.store(1, std::memory_order_relaxed); + #endif + delete this; + } + + // The following friends are those which override internal_dispose() + // and conditionally call SkRefCnt::internal_dispose(). + friend class SkWeakRefCnt; + + mutable std::atomic fRefCnt; + + SkRefCntBase(SkRefCntBase&&) = delete; + SkRefCntBase(const SkRefCntBase&) = delete; + SkRefCntBase& operator=(SkRefCntBase&&) = delete; + SkRefCntBase& operator=(const SkRefCntBase&) = delete; +}; + +#ifdef SK_REF_CNT_MIXIN_INCLUDE +// It is the responsibility of the following include to define the type SkRefCnt. +// This SkRefCnt should normally derive from SkRefCntBase. +#include SK_REF_CNT_MIXIN_INCLUDE +#else +class SK_API SkRefCnt : public SkRefCntBase { + // "#include SK_REF_CNT_MIXIN_INCLUDE" doesn't work with this build system. + #if defined(SK_BUILD_FOR_GOOGLE3) + public: + void deref() const { this->unref(); } + #endif +}; +#endif + +/////////////////////////////////////////////////////////////////////////////// + +/** Call obj->ref() and return obj. The obj must not be nullptr. + */ +template static inline T* SkRef(T* obj) { + SkASSERT(obj); + obj->ref(); + return obj; +} + +/** Check if the argument is non-null, and if so, call obj->ref() and return obj. + */ +template static inline T* SkSafeRef(T* obj) { + if (obj) { + obj->ref(); + } + return obj; +} + +/** Check if the argument is non-null, and if so, call obj->unref() + */ +template static inline void SkSafeUnref(T* obj) { + if (obj) { + obj->unref(); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +// This is a variant of SkRefCnt that's Not Virtual, so weighs 4 bytes instead of 8 or 16. +// There's only benefit to using this if the deriving class does not otherwise need a vtable. +template +class SkNVRefCnt { +public: + SkNVRefCnt() : fRefCnt(1) {} + ~SkNVRefCnt() { + #ifdef SK_DEBUG + int rc = fRefCnt.load(std::memory_order_relaxed); + SkASSERTF(rc == 1, "NVRefCnt was %d", rc); + #endif + } + + // Implementation is pretty much the same as SkRefCntBase. All required barriers are the same: + // - unique() needs acquire when it returns true, and no barrier if it returns false; + // - ref() doesn't need any barrier; + // - unref() needs a release barrier, and an acquire if it's going to call delete. + + bool unique() const { return 1 == fRefCnt.load(std::memory_order_acquire); } + void ref() const { (void)fRefCnt.fetch_add(+1, std::memory_order_relaxed); } + void unref() const { + if (1 == fRefCnt.fetch_add(-1, std::memory_order_acq_rel)) { + // restore the 1 for our destructor's assert + SkDEBUGCODE(fRefCnt.store(1, std::memory_order_relaxed)); + delete (const Derived*)this; + } + } + void deref() const { this->unref(); } + + // This must be used with caution. It is only valid to call this when 'threadIsolatedTestCnt' + // refs are known to be isolated to the current thread. That is, it is known that there are at + // least 'threadIsolatedTestCnt' refs for which no other thread may make a balancing unref() + // call. Assuming the contract is followed, if this returns false then no other thread has + // ownership of this. If it returns true then another thread *may* have ownership. + bool refCntGreaterThan(int32_t threadIsolatedTestCnt) const { + int cnt = fRefCnt.load(std::memory_order_acquire); + // If this fails then the above contract has been violated. + SkASSERT(cnt >= threadIsolatedTestCnt); + return cnt > threadIsolatedTestCnt; + } + +private: + mutable std::atomic fRefCnt; + + SkNVRefCnt(SkNVRefCnt&&) = delete; + SkNVRefCnt(const SkNVRefCnt&) = delete; + SkNVRefCnt& operator=(SkNVRefCnt&&) = delete; + SkNVRefCnt& operator=(const SkNVRefCnt&) = delete; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * Shared pointer class to wrap classes that support a ref()/unref() interface. + * + * This can be used for classes inheriting from SkRefCnt, but it also works for other + * classes that match the interface, but have different internal choices: e.g. the hosted class + * may have its ref/unref be thread-safe, but that is not assumed/imposed by sk_sp. + * + * Declared with the trivial_abi attribute where supported so that sk_sp and types containing it + * may be considered as trivially relocatable by the compiler so that destroying-move operations + * i.e. move constructor followed by destructor can be optimized to memcpy. + */ +template class SK_TRIVIAL_ABI sk_sp { +public: + using element_type = T; + + constexpr sk_sp() : fPtr(nullptr) {} + constexpr sk_sp(std::nullptr_t) : fPtr(nullptr) {} + + /** + * Shares the underlying object by calling ref(), so that both the argument and the newly + * created sk_sp both have a reference to it. + */ + sk_sp(const sk_sp& that) : fPtr(SkSafeRef(that.get())) {} + template ::value>::type> + sk_sp(const sk_sp& that) : fPtr(SkSafeRef(that.get())) {} + + /** + * Move the underlying object from the argument to the newly created sk_sp. Afterwards only + * the new sk_sp will have a reference to the object, and the argument will point to null. + * No call to ref() or unref() will be made. + */ + sk_sp(sk_sp&& that) : fPtr(that.release()) {} + template ::value>::type> + sk_sp(sk_sp&& that) : fPtr(that.release()) {} + + /** + * Adopt the bare pointer into the newly created sk_sp. + * No call to ref() or unref() will be made. + */ + explicit sk_sp(T* obj) : fPtr(obj) {} + + /** + * Calls unref() on the underlying object pointer. + */ + ~sk_sp() { + SkSafeUnref(fPtr); + SkDEBUGCODE(fPtr = nullptr); + } + + sk_sp& operator=(std::nullptr_t) { this->reset(); return *this; } + + /** + * Shares the underlying object referenced by the argument by calling ref() on it. If this + * sk_sp previously had a reference to an object (i.e. not null) it will call unref() on that + * object. + */ + sk_sp& operator=(const sk_sp& that) { + if (this != &that) { + this->reset(SkSafeRef(that.get())); + } + return *this; + } + template ::value>::type> + sk_sp& operator=(const sk_sp& that) { + this->reset(SkSafeRef(that.get())); + return *this; + } + + /** + * Move the underlying object from the argument to the sk_sp. If the sk_sp previously held + * a reference to another object, unref() will be called on that object. No call to ref() + * will be made. + */ + sk_sp& operator=(sk_sp&& that) { + this->reset(that.release()); + return *this; + } + template ::value>::type> + sk_sp& operator=(sk_sp&& that) { + this->reset(that.release()); + return *this; + } + + T& operator*() const { + SkASSERT(this->get() != nullptr); + return *this->get(); + } + + explicit operator bool() const { return this->get() != nullptr; } + + T* get() const { return fPtr; } + T* operator->() const { return fPtr; } + + /** + * Adopt the new bare pointer, and call unref() on any previously held object (if not null). + * No call to ref() will be made. + */ + void reset(T* ptr = nullptr) { + // Calling fPtr->unref() may call this->~() or this->reset(T*). + // http://wg21.cmeerw.net/lwg/issue998 + // http://wg21.cmeerw.net/lwg/issue2262 + T* oldPtr = fPtr; + fPtr = ptr; + SkSafeUnref(oldPtr); + } + + /** + * Return the bare pointer, and set the internal object pointer to nullptr. + * The caller must assume ownership of the object, and manage its reference count directly. + * No call to unref() will be made. + */ + [[nodiscard]] T* release() { + T* ptr = fPtr; + fPtr = nullptr; + return ptr; + } + + void swap(sk_sp& that) /*noexcept*/ { + using std::swap; + swap(fPtr, that.fPtr); + } + + using sk_is_trivially_relocatable = std::true_type; + +private: + T* fPtr; +}; + +template inline void swap(sk_sp& a, sk_sp& b) /*noexcept*/ { + a.swap(b); +} + +template inline bool operator==(const sk_sp& a, const sk_sp& b) { + return a.get() == b.get(); +} +template inline bool operator==(const sk_sp& a, std::nullptr_t) /*noexcept*/ { + return !a; +} +template inline bool operator==(std::nullptr_t, const sk_sp& b) /*noexcept*/ { + return !b; +} + +template inline bool operator!=(const sk_sp& a, const sk_sp& b) { + return a.get() != b.get(); +} +template inline bool operator!=(const sk_sp& a, std::nullptr_t) /*noexcept*/ { + return static_cast(a); +} +template inline bool operator!=(std::nullptr_t, const sk_sp& b) /*noexcept*/ { + return static_cast(b); +} + +template +auto operator<<(std::basic_ostream& os, const sk_sp& sp) -> decltype(os << sp.get()) { + return os << sp.get(); +} + +template +sk_sp sk_make_sp(Args&&... args) { + return sk_sp(new T(std::forward(args)...)); +} + +/* + * Returns a sk_sp wrapping the provided ptr AND calls ref on it (if not null). + * + * This is different than the semantics of the constructor for sk_sp, which just wraps the ptr, + * effectively "adopting" it. + */ +template sk_sp sk_ref_sp(T* obj) { + return sk_sp(SkSafeRef(obj)); +} + +template sk_sp sk_ref_sp(const T* obj) { + return sk_sp(const_cast(SkSafeRef(obj))); +} + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkRegion.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkRegion.h new file mode 100644 index 0000000000..d72cd2ab7d --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkRegion.h @@ -0,0 +1,684 @@ +/* + * Copyright 2005 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkRegion_DEFINED +#define SkRegion_DEFINED + +#include "include/core/SkRect.h" +#include "include/private/base/SkAPI.h" +#include "include/private/base/SkAssert.h" +#include "include/private/base/SkDebug.h" +#include "include/private/base/SkTypeTraits.h" + +#include +#include +#include + +class SkPath; + +/** \class SkRegion + SkRegion describes the set of pixels used to clip SkCanvas. SkRegion is compact, + efficiently storing a single integer rectangle, or a run length encoded array + of rectangles. SkRegion may reduce the current SkCanvas clip, or may be drawn as + one or more integer rectangles. SkRegion iterator returns the scan lines or + rectangles contained by it, optionally intersecting a bounding rectangle. +*/ +class SK_API SkRegion { + typedef int32_t RunType; +public: + + /** Constructs an empty SkRegion. SkRegion is set to empty bounds + at (0, 0) with zero width and height. + + @return empty SkRegion + + example: https://fiddle.skia.org/c/@Region_empty_constructor + */ + SkRegion(); + + /** Constructs a copy of an existing region. + Copy constructor makes two regions identical by value. Internally, region and + the returned result share pointer values. The underlying SkRect array is + copied when modified. + + Creating a SkRegion copy is very efficient and never allocates memory. + SkRegion are always copied by value from the interface; the underlying shared + pointers are not exposed. + + @param region SkRegion to copy by value + @return copy of SkRegion + + example: https://fiddle.skia.org/c/@Region_copy_const_SkRegion + */ + SkRegion(const SkRegion& region); + + /** Constructs a rectangular SkRegion matching the bounds of rect. + + @param rect bounds of constructed SkRegion + @return rectangular SkRegion + + example: https://fiddle.skia.org/c/@Region_copy_const_SkIRect + */ + explicit SkRegion(const SkIRect& rect); + + /** Releases ownership of any shared data and deletes data if SkRegion is sole owner. + + example: https://fiddle.skia.org/c/@Region_destructor + */ + ~SkRegion(); + + /** Constructs a copy of an existing region. + Makes two regions identical by value. Internally, region and + the returned result share pointer values. The underlying SkRect array is + copied when modified. + + Creating a SkRegion copy is very efficient and never allocates memory. + SkRegion are always copied by value from the interface; the underlying shared + pointers are not exposed. + + @param region SkRegion to copy by value + @return SkRegion to copy by value + + example: https://fiddle.skia.org/c/@Region_copy_operator + */ + SkRegion& operator=(const SkRegion& region); + + /** Compares SkRegion and other; returns true if they enclose exactly + the same area. + + @param other SkRegion to compare + @return true if SkRegion pair are equivalent + + example: https://fiddle.skia.org/c/@Region_equal1_operator + */ + bool operator==(const SkRegion& other) const; + + /** Compares SkRegion and other; returns true if they do not enclose the same area. + + @param other SkRegion to compare + @return true if SkRegion pair are not equivalent + */ + bool operator!=(const SkRegion& other) const { + return !(*this == other); + } + + /** Sets SkRegion to src, and returns true if src bounds is not empty. + This makes SkRegion and src identical by value. Internally, + SkRegion and src share pointer values. The underlying SkRect array is + copied when modified. + + Creating a SkRegion copy is very efficient and never allocates memory. + SkRegion are always copied by value from the interface; the underlying shared + pointers are not exposed. + + @param src SkRegion to copy + @return copy of src + */ + bool set(const SkRegion& src) { + *this = src; + return !this->isEmpty(); + } + + /** Exchanges SkIRect array of SkRegion and other. swap() internally exchanges pointers, + so it is lightweight and does not allocate memory. + + swap() usage has largely been replaced by operator=(const SkRegion& region). + SkPath do not copy their content on assignment until they are written to, + making assignment as efficient as swap(). + + @param other operator=(const SkRegion& region) set + + example: https://fiddle.skia.org/c/@Region_swap + */ + void swap(SkRegion& other); + + /** Returns true if SkRegion is empty. + Empty SkRegion has bounds width or height less than or equal to zero. + SkRegion() constructs empty SkRegion; setEmpty() + and setRect() with dimensionless data make SkRegion empty. + + @return true if bounds has no width or height + */ + bool isEmpty() const { return fRunHead == emptyRunHeadPtr(); } + + /** Returns true if SkRegion is one SkIRect with positive dimensions. + + @return true if SkRegion contains one SkIRect + */ + bool isRect() const { return fRunHead == kRectRunHeadPtr; } + + /** Returns true if SkRegion is described by more than one rectangle. + + @return true if SkRegion contains more than one SkIRect + */ + bool isComplex() const { return !this->isEmpty() && !this->isRect(); } + + /** Returns minimum and maximum axes values of SkIRect array. + Returns (0, 0, 0, 0) if SkRegion is empty. + + @return combined bounds of all SkIRect elements + */ + const SkIRect& getBounds() const { return fBounds; } + + /** Returns a value that increases with the number of + elements in SkRegion. Returns zero if SkRegion is empty. + Returns one if SkRegion equals SkIRect; otherwise, returns + value greater than one indicating that SkRegion is complex. + + Call to compare SkRegion for relative complexity. + + @return relative complexity + + example: https://fiddle.skia.org/c/@Region_computeRegionComplexity + */ + int computeRegionComplexity() const; + + /** Appends outline of SkRegion to path. + Returns true if SkRegion is not empty; otherwise, returns false, and leaves path + unmodified. + + @param path SkPath to append to + @return true if path changed + + example: https://fiddle.skia.org/c/@Region_getBoundaryPath + */ + bool getBoundaryPath(SkPath* path) const; + + /** Constructs an empty SkRegion. SkRegion is set to empty bounds + at (0, 0) with zero width and height. Always returns false. + + @return false + + example: https://fiddle.skia.org/c/@Region_setEmpty + */ + bool setEmpty(); + + /** Constructs a rectangular SkRegion matching the bounds of rect. + If rect is empty, constructs empty and returns false. + + @param rect bounds of constructed SkRegion + @return true if rect is not empty + + example: https://fiddle.skia.org/c/@Region_setRect + */ + bool setRect(const SkIRect& rect); + + /** Constructs SkRegion as the union of SkIRect in rects array. If count is + zero, constructs empty SkRegion. Returns false if constructed SkRegion is empty. + + May be faster than repeated calls to op(). + + @param rects array of SkIRect + @param count array size + @return true if constructed SkRegion is not empty + + example: https://fiddle.skia.org/c/@Region_setRects + */ + bool setRects(const SkIRect rects[], int count); + + /** Constructs a copy of an existing region. + Makes two regions identical by value. Internally, region and + the returned result share pointer values. The underlying SkRect array is + copied when modified. + + Creating a SkRegion copy is very efficient and never allocates memory. + SkRegion are always copied by value from the interface; the underlying shared + pointers are not exposed. + + @param region SkRegion to copy by value + @return SkRegion to copy by value + + example: https://fiddle.skia.org/c/@Region_setRegion + */ + bool setRegion(const SkRegion& region); + + /** Constructs SkRegion to match outline of path within clip. + Returns false if constructed SkRegion is empty. + + Constructed SkRegion draws the same pixels as path through clip when + anti-aliasing is disabled. + + @param path SkPath providing outline + @param clip SkRegion containing path + @return true if constructed SkRegion is not empty + + example: https://fiddle.skia.org/c/@Region_setPath + */ + bool setPath(const SkPath& path, const SkRegion& clip); + + /** Returns true if SkRegion intersects rect. + Returns false if either rect or SkRegion is empty, or do not intersect. + + @param rect SkIRect to intersect + @return true if rect and SkRegion have area in common + + example: https://fiddle.skia.org/c/@Region_intersects + */ + bool intersects(const SkIRect& rect) const; + + /** Returns true if SkRegion intersects other. + Returns false if either other or SkRegion is empty, or do not intersect. + + @param other SkRegion to intersect + @return true if other and SkRegion have area in common + + example: https://fiddle.skia.org/c/@Region_intersects_2 + */ + bool intersects(const SkRegion& other) const; + + /** Returns true if SkIPoint (x, y) is inside SkRegion. + Returns false if SkRegion is empty. + + @param x test SkIPoint x-coordinate + @param y test SkIPoint y-coordinate + @return true if (x, y) is inside SkRegion + + example: https://fiddle.skia.org/c/@Region_contains + */ + bool contains(int32_t x, int32_t y) const; + + /** Returns true if other is completely inside SkRegion. + Returns false if SkRegion or other is empty. + + @param other SkIRect to contain + @return true if other is inside SkRegion + + example: https://fiddle.skia.org/c/@Region_contains_2 + */ + bool contains(const SkIRect& other) const; + + /** Returns true if other is completely inside SkRegion. + Returns false if SkRegion or other is empty. + + @param other SkRegion to contain + @return true if other is inside SkRegion + + example: https://fiddle.skia.org/c/@Region_contains_3 + */ + bool contains(const SkRegion& other) const; + + /** Returns true if SkRegion is a single rectangle and contains r. + May return false even though SkRegion contains r. + + @param r SkIRect to contain + @return true quickly if r points are equal or inside + */ + bool quickContains(const SkIRect& r) const { + SkASSERT(this->isEmpty() == fBounds.isEmpty()); // valid region + + return r.fLeft < r.fRight && r.fTop < r.fBottom && + fRunHead == kRectRunHeadPtr && // this->isRect() + /* fBounds.contains(left, top, right, bottom); */ + fBounds.fLeft <= r.fLeft && fBounds.fTop <= r.fTop && + fBounds.fRight >= r.fRight && fBounds.fBottom >= r.fBottom; + } + + /** Returns true if SkRegion does not intersect rect. + Returns true if rect is empty or SkRegion is empty. + May return false even though SkRegion does not intersect rect. + + @param rect SkIRect to intersect + @return true if rect does not intersect + */ + bool quickReject(const SkIRect& rect) const { + return this->isEmpty() || rect.isEmpty() || + !SkIRect::Intersects(fBounds, rect); + } + + /** Returns true if SkRegion does not intersect rgn. + Returns true if rgn is empty or SkRegion is empty. + May return false even though SkRegion does not intersect rgn. + + @param rgn SkRegion to intersect + @return true if rgn does not intersect + */ + bool quickReject(const SkRegion& rgn) const { + return this->isEmpty() || rgn.isEmpty() || + !SkIRect::Intersects(fBounds, rgn.fBounds); + } + + /** Offsets SkRegion by ivector (dx, dy). Has no effect if SkRegion is empty. + + @param dx x-axis offset + @param dy y-axis offset + */ + void translate(int dx, int dy) { this->translate(dx, dy, this); } + + /** Offsets SkRegion by ivector (dx, dy), writing result to dst. SkRegion may be passed + as dst parameter, translating SkRegion in place. Has no effect if dst is nullptr. + If SkRegion is empty, sets dst to empty. + + @param dx x-axis offset + @param dy y-axis offset + @param dst translated result + + example: https://fiddle.skia.org/c/@Region_translate_2 + */ + void translate(int dx, int dy, SkRegion* dst) const; + + /** \enum SkRegion::Op + The logical operations that can be performed when combining two SkRegion. + */ + enum Op { + kDifference_Op, //!< target minus operand + kIntersect_Op, //!< target intersected with operand + kUnion_Op, //!< target unioned with operand + kXOR_Op, //!< target exclusive or with operand + kReverseDifference_Op, //!< operand minus target + kReplace_Op, //!< replace target with operand + kLastOp = kReplace_Op, //!< last operator + }; + + static const int kOpCnt = kLastOp + 1; + + /** Replaces SkRegion with the result of SkRegion op rect. + Returns true if replaced SkRegion is not empty. + + @param rect SkIRect operand + @return false if result is empty + */ + bool op(const SkIRect& rect, Op op) { + if (this->isRect() && kIntersect_Op == op) { + if (!fBounds.intersect(rect)) { + return this->setEmpty(); + } + return true; + } + return this->op(*this, rect, op); + } + + /** Replaces SkRegion with the result of SkRegion op rgn. + Returns true if replaced SkRegion is not empty. + + @param rgn SkRegion operand + @return false if result is empty + */ + bool op(const SkRegion& rgn, Op op) { return this->op(*this, rgn, op); } + + /** Replaces SkRegion with the result of rect op rgn. + Returns true if replaced SkRegion is not empty. + + @param rect SkIRect operand + @param rgn SkRegion operand + @return false if result is empty + + example: https://fiddle.skia.org/c/@Region_op_4 + */ + bool op(const SkIRect& rect, const SkRegion& rgn, Op op); + + /** Replaces SkRegion with the result of rgn op rect. + Returns true if replaced SkRegion is not empty. + + @param rgn SkRegion operand + @param rect SkIRect operand + @return false if result is empty + + example: https://fiddle.skia.org/c/@Region_op_5 + */ + bool op(const SkRegion& rgn, const SkIRect& rect, Op op); + + /** Replaces SkRegion with the result of rgna op rgnb. + Returns true if replaced SkRegion is not empty. + + @param rgna SkRegion operand + @param rgnb SkRegion operand + @return false if result is empty + + example: https://fiddle.skia.org/c/@Region_op_6 + */ + bool op(const SkRegion& rgna, const SkRegion& rgnb, Op op); + +#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK + /** Private. Android framework only. + + @return string representation of SkRegion + */ + char* toString(); +#endif + + /** \class SkRegion::Iterator + Returns sequence of rectangles, sorted along y-axis, then x-axis, that make + up SkRegion. + */ + class SK_API Iterator { + public: + + /** Initializes SkRegion::Iterator with an empty SkRegion. done() on SkRegion::Iterator + returns true. + Call reset() to initialized SkRegion::Iterator at a later time. + + @return empty SkRegion iterator + */ + Iterator() : fRgn(nullptr), fDone(true) {} + + /** Sets SkRegion::Iterator to return elements of SkIRect array in region. + + @param region SkRegion to iterate + @return SkRegion iterator + + example: https://fiddle.skia.org/c/@Region_Iterator_copy_const_SkRegion + */ + Iterator(const SkRegion& region); + + /** SkPoint SkRegion::Iterator to start of SkRegion. + Returns true if SkRegion was set; otherwise, returns false. + + @return true if SkRegion was set + + example: https://fiddle.skia.org/c/@Region_Iterator_rewind + */ + bool rewind(); + + /** Resets iterator, using the new SkRegion. + + @param region SkRegion to iterate + + example: https://fiddle.skia.org/c/@Region_Iterator_reset + */ + void reset(const SkRegion& region); + + /** Returns true if SkRegion::Iterator is pointing to final SkIRect in SkRegion. + + @return true if data parsing is complete + */ + bool done() const { return fDone; } + + /** Advances SkRegion::Iterator to next SkIRect in SkRegion if it is not done. + + example: https://fiddle.skia.org/c/@Region_Iterator_next + */ + void next(); + + /** Returns SkIRect element in SkRegion. Does not return predictable results if SkRegion + is empty. + + @return part of SkRegion as SkIRect + */ + const SkIRect& rect() const { return fRect; } + + /** Returns SkRegion if set; otherwise, returns nullptr. + + @return iterated SkRegion + */ + const SkRegion* rgn() const { return fRgn; } + + private: + const SkRegion* fRgn; + const SkRegion::RunType* fRuns; + SkIRect fRect = {0, 0, 0, 0}; + bool fDone; + }; + + /** \class SkRegion::Cliperator + Returns the sequence of rectangles, sorted along y-axis, then x-axis, that make + up SkRegion intersected with the specified clip rectangle. + */ + class SK_API Cliperator { + public: + + /** Sets SkRegion::Cliperator to return elements of SkIRect array in SkRegion within clip. + + @param region SkRegion to iterate + @param clip bounds of iteration + @return SkRegion iterator + + example: https://fiddle.skia.org/c/@Region_Cliperator_const_SkRegion_const_SkIRect + */ + Cliperator(const SkRegion& region, const SkIRect& clip); + + /** Returns true if SkRegion::Cliperator is pointing to final SkIRect in SkRegion. + + @return true if data parsing is complete + */ + bool done() { return fDone; } + + /** Advances iterator to next SkIRect in SkRegion contained by clip. + + example: https://fiddle.skia.org/c/@Region_Cliperator_next + */ + void next(); + + /** Returns SkIRect element in SkRegion, intersected with clip passed to + SkRegion::Cliperator constructor. Does not return predictable results if SkRegion + is empty. + + @return part of SkRegion inside clip as SkIRect + */ + const SkIRect& rect() const { return fRect; } + + private: + Iterator fIter; + SkIRect fClip; + SkIRect fRect = {0, 0, 0, 0}; + bool fDone; + }; + + /** \class SkRegion::Spanerator + Returns the line segment ends within SkRegion that intersect a horizontal line. + */ + class Spanerator { + public: + + /** Sets SkRegion::Spanerator to return line segments in SkRegion on scan line. + + @param region SkRegion to iterate + @param y horizontal line to intersect + @param left bounds of iteration + @param right bounds of iteration + @return SkRegion iterator + + example: https://fiddle.skia.org/c/@Region_Spanerator_const_SkRegion_int_int_int + */ + Spanerator(const SkRegion& region, int y, int left, int right); + + /** Advances iterator to next span intersecting SkRegion within line segment provided + in constructor. Returns true if interval was found. + + @param left pointer to span start; may be nullptr + @param right pointer to span end; may be nullptr + @return true if interval was found + + example: https://fiddle.skia.org/c/@Region_Spanerator_next + */ + bool next(int* left, int* right); + + private: + const SkRegion::RunType* fRuns; + int fLeft, fRight; + bool fDone; + }; + + /** Writes SkRegion to buffer, and returns number of bytes written. + If buffer is nullptr, returns number number of bytes that would be written. + + @param buffer storage for binary data + @return size of SkRegion + + example: https://fiddle.skia.org/c/@Region_writeToMemory + */ + size_t writeToMemory(void* buffer) const; + + /** Constructs SkRegion from buffer of size length. Returns bytes read. + Returned value will be multiple of four or zero if length was too small. + + @param buffer storage for binary data + @param length size of buffer + @return bytes read + + example: https://fiddle.skia.org/c/@Region_readFromMemory + */ + size_t readFromMemory(const void* buffer, size_t length); + + using sk_is_trivially_relocatable = std::true_type; + +private: + static constexpr int kOpCount = kReplace_Op + 1; + + // T + // [B N L R S] + // S + static constexpr int kRectRegionRuns = 7; + + struct RunHead; + + static RunHead* emptyRunHeadPtr() { return (SkRegion::RunHead*) -1; } + static constexpr RunHead* kRectRunHeadPtr = nullptr; + + // allocate space for count runs + void allocateRuns(int count); + void allocateRuns(int count, int ySpanCount, int intervalCount); + void allocateRuns(const RunHead& src); + + SkDEBUGCODE(void dump() const;) + + SkIRect fBounds; + RunHead* fRunHead; + + static_assert(::sk_is_trivially_relocatable::value); + static_assert(::sk_is_trivially_relocatable::value); + + void freeRuns(); + + /** + * Return the runs from this region, consing up fake runs if the region + * is empty or a rect. In those 2 cases, we use tmpStorage to hold the + * run data. + */ + const RunType* getRuns(RunType tmpStorage[], int* intervals) const; + + // This is called with runs[] that do not yet have their interval-count + // field set on each scanline. That is computed as part of this call + // (inside ComputeRunBounds). + bool setRuns(RunType runs[], int count); + + int count_runtype_values(int* itop, int* ibot) const; + + bool isValid() const; + + static void BuildRectRuns(const SkIRect& bounds, + RunType runs[kRectRegionRuns]); + + // If the runs define a simple rect, return true and set bounds to that + // rect. If not, return false and ignore bounds. + static bool RunsAreARect(const SkRegion::RunType runs[], int count, + SkIRect* bounds); + + /** + * If the last arg is null, just return if the result is non-empty, + * else store the result in the last arg. + */ + static bool Oper(const SkRegion&, const SkRegion&, SkRegion::Op, SkRegion*); + + friend struct RunHead; + friend class Iterator; + friend class Spanerator; + friend class SkRegionPriv; + friend class SkRgnBuilder; + friend class SkFlatRegion; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkSamplingOptions.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkSamplingOptions.h new file mode 100644 index 0000000000..4531a8c949 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkSamplingOptions.h @@ -0,0 +1,107 @@ +/* + * Copyright 2020 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkImageSampling_DEFINED +#define SkImageSampling_DEFINED + +#include "include/core/SkTypes.h" + +#include +#include + +enum class SkFilterMode { + kNearest, // single sample point (nearest neighbor) + kLinear, // interporate between 2x2 sample points (bilinear interpolation) + + kLast = kLinear, +}; +static constexpr int kSkFilterModeCount = static_cast(SkFilterMode::kLast) + 1; + +enum class SkMipmapMode { + kNone, // ignore mipmap levels, sample from the "base" + kNearest, // sample from the nearest level + kLinear, // interpolate between the two nearest levels + + kLast = kLinear, +}; +static constexpr int kSkMipmapModeCount = static_cast(SkMipmapMode::kLast) + 1; + +/* + * Specify B and C (each between 0...1) to create a shader that applies the corresponding + * cubic reconstruction filter to the image. + * + * Example values: + * B = 1/3, C = 1/3 "Mitchell" filter + * B = 0, C = 1/2 "Catmull-Rom" filter + * + * See "Reconstruction Filters in Computer Graphics" + * Don P. Mitchell + * Arun N. Netravali + * 1988 + * https://www.cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/mitchell/Mitchell.pdf + * + * Desmos worksheet https://www.desmos.com/calculator/aghdpicrvr + * Nice overview https://entropymine.com/imageworsener/bicubic/ + */ +struct SkCubicResampler { + float B, C; + + // Historic default for kHigh_SkFilterQuality + static constexpr SkCubicResampler Mitchell() { return {1/3.0f, 1/3.0f}; } + static constexpr SkCubicResampler CatmullRom() { return {0.0f, 1/2.0f}; } +}; + +struct SK_API SkSamplingOptions { + const int maxAniso = 0; + const bool useCubic = false; + const SkCubicResampler cubic = {0, 0}; + const SkFilterMode filter = SkFilterMode::kNearest; + const SkMipmapMode mipmap = SkMipmapMode::kNone; + + constexpr SkSamplingOptions() = default; + SkSamplingOptions(const SkSamplingOptions&) = default; + SkSamplingOptions& operator=(const SkSamplingOptions& that) { + this->~SkSamplingOptions(); // A pedantic no-op. + new (this) SkSamplingOptions(that); + return *this; + } + + constexpr SkSamplingOptions(SkFilterMode fm, SkMipmapMode mm) + : filter(fm) + , mipmap(mm) {} + + // These are intentionally implicit because the single parameter clearly conveys what the + // implicitly created SkSamplingOptions will be. + constexpr SkSamplingOptions(SkFilterMode fm) + : filter(fm) + , mipmap(SkMipmapMode::kNone) {} + + constexpr SkSamplingOptions(const SkCubicResampler& c) + : useCubic(true) + , cubic(c) {} + + static constexpr SkSamplingOptions Aniso(int maxAniso) { + return SkSamplingOptions{std::max(maxAniso, 1)}; + } + + bool operator==(const SkSamplingOptions& other) const { + return maxAniso == other.maxAniso + && useCubic == other.useCubic + && cubic.B == other.cubic.B + && cubic.C == other.cubic.C + && filter == other.filter + && mipmap == other.mipmap; + } + bool operator!=(const SkSamplingOptions& other) const { return !(*this == other); } + + bool isAniso() const { return maxAniso != 0; } + +private: + constexpr SkSamplingOptions(int maxAniso) : maxAniso(maxAniso) {} +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkScalar.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkScalar.h new file mode 100644 index 0000000000..d4150f6fb0 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkScalar.h @@ -0,0 +1,161 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkScalar_DEFINED +#define SkScalar_DEFINED + +#include "include/private/base/SkAssert.h" +#include "include/private/base/SkFloatingPoint.h" + +#include + +typedef float SkScalar; + +#define SK_Scalar1 1.0f +#define SK_ScalarHalf 0.5f +#define SK_ScalarSqrt2 SK_FloatSqrt2 +#define SK_ScalarPI SK_FloatPI +#define SK_ScalarTanPIOver8 0.414213562f +#define SK_ScalarRoot2Over2 0.707106781f +#define SK_ScalarMax 3.402823466e+38f +#define SK_ScalarMin (-SK_ScalarMax) +#define SK_ScalarInfinity SK_FloatInfinity +#define SK_ScalarNegativeInfinity SK_FloatNegativeInfinity +#define SK_ScalarNaN SK_FloatNaN + +#define SkScalarFloorToScalar(x) std::floor(x) +#define SkScalarCeilToScalar(x) std::ceil(x) +#define SkScalarRoundToScalar(x) sk_float_round(x) +#define SkScalarTruncToScalar(x) std::trunc(x) + +#define SkScalarFloorToInt(x) sk_float_floor2int(x) +#define SkScalarCeilToInt(x) sk_float_ceil2int(x) +#define SkScalarRoundToInt(x) sk_float_round2int(x) + +#define SkScalarAbs(x) std::fabs(x) +#define SkScalarCopySign(x, y) std::copysign(x, y) +#define SkScalarMod(x, y) std::fmod(x,y) +#define SkScalarSqrt(x) std::sqrt(x) +#define SkScalarPow(b, e) std::pow(b, e) + +#define SkScalarSin(radians) ((float)std::sin(radians)) +#define SkScalarCos(radians) ((float)std::cos(radians)) +#define SkScalarTan(radians) ((float)std::tan(radians)) +#define SkScalarASin(val) ((float)std::asin(val)) +#define SkScalarACos(val) ((float)std::acos(val)) +#define SkScalarATan2(y, x) ((float)std::atan2(y,x)) +#define SkScalarExp(x) ((float)std::exp(x)) +#define SkScalarLog(x) ((float)std::log(x)) +#define SkScalarLog2(x) ((float)std::log2(x)) + +////////////////////////////////////////////////////////////////////////////////////////////////// + +#define SkIntToScalar(x) static_cast(x) +#define SkIntToFloat(x) static_cast(x) +#define SkScalarTruncToInt(x) sk_float_saturate2int(x) + +#define SkScalarToFloat(x) static_cast(x) +#define SkFloatToScalar(x) static_cast(x) +#define SkScalarToDouble(x) static_cast(x) +#define SkDoubleToScalar(x) sk_double_to_float(x) + +/** Returns the fractional part of the scalar. */ +static inline SkScalar SkScalarFraction(SkScalar x) { + return x - SkScalarTruncToScalar(x); +} + +static inline SkScalar SkScalarSquare(SkScalar x) { return x * x; } + +#define SkScalarInvert(x) (SK_Scalar1 / (x)) +#define SkScalarAve(a, b) (((a) + (b)) * SK_ScalarHalf) +#define SkScalarHalf(a) ((a) * SK_ScalarHalf) + +#define SkDegreesToRadians(degrees) ((degrees) * (SK_ScalarPI / 180)) +#define SkRadiansToDegrees(radians) ((radians) * (180 / SK_ScalarPI)) + +static inline bool SkScalarIsInt(SkScalar x) { + return x == SkScalarFloorToScalar(x); +} + +/** + * Returns -1 || 0 || 1 depending on the sign of value: + * -1 if x < 0 + * 0 if x == 0 + * 1 if x > 0 + */ +static inline int SkScalarSignAsInt(SkScalar x) { + return x < 0 ? -1 : (x > 0); +} + +// Scalar result version of above +static inline SkScalar SkScalarSignAsScalar(SkScalar x) { + return x < 0 ? -SK_Scalar1 : ((x > 0) ? SK_Scalar1 : 0); +} + +#define SK_ScalarNearlyZero (SK_Scalar1 / (1 << 12)) + +static inline bool SkScalarNearlyZero(SkScalar x, + SkScalar tolerance = SK_ScalarNearlyZero) { + SkASSERT(tolerance >= 0); + return SkScalarAbs(x) <= tolerance; +} + +static inline bool SkScalarNearlyEqual(SkScalar x, SkScalar y, + SkScalar tolerance = SK_ScalarNearlyZero) { + SkASSERT(tolerance >= 0); + return SkScalarAbs(x-y) <= tolerance; +} + +#define SK_ScalarSinCosNearlyZero (SK_Scalar1 / (1 << 16)) + +static inline float SkScalarSinSnapToZero(SkScalar radians) { + float v = SkScalarSin(radians); + return SkScalarNearlyZero(v, SK_ScalarSinCosNearlyZero) ? 0.0f : v; +} + +static inline float SkScalarCosSnapToZero(SkScalar radians) { + float v = SkScalarCos(radians); + return SkScalarNearlyZero(v, SK_ScalarSinCosNearlyZero) ? 0.0f : v; +} + +/** Linearly interpolate between A and B, based on t. + If t is 0, return A + If t is 1, return B + else interpolate. + t must be [0..SK_Scalar1] +*/ +static inline SkScalar SkScalarInterp(SkScalar A, SkScalar B, SkScalar t) { + SkASSERT(t >= 0 && t <= SK_Scalar1); + return A + (B - A) * t; +} + +/** Interpolate along the function described by (keys[length], values[length]) + for the passed searchKey. SearchKeys outside the range keys[0]-keys[Length] + clamp to the min or max value. This function assumes the number of pairs + (length) will be small and a linear search is used. + + Repeated keys are allowed for discontinuous functions (so long as keys is + monotonically increasing). If key is the value of a repeated scalar in + keys the first one will be used. +*/ +SkScalar SkScalarInterpFunc(SkScalar searchKey, const SkScalar keys[], + const SkScalar values[], int length); + +/* + * Helper to compare an array of scalars. + */ +static inline bool SkScalarsEqual(const SkScalar a[], const SkScalar b[], int n) { + SkASSERT(n >= 0); + for (int i = 0; i < n; ++i) { + if (a[i] != b[i]) { + return false; + } + } + return true; +} + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkSerialProcs.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkSerialProcs.h new file mode 100644 index 0000000000..813488d0a7 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkSerialProcs.h @@ -0,0 +1,117 @@ +/* + * 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 SkSerialProcs_DEFINED +#define SkSerialProcs_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/private/base/SkAPI.h" + +#include +#include + +class SkData; +class SkImage; +class SkPicture; +class SkTypeface; +class SkReadBuffer; +enum SkAlphaType : int; +namespace sktext::gpu { + class Slug; +} + +/** + * A serial-proc is asked to serialize the specified object (e.g. picture or image). + * If a data object is returned, it will be used (even if it is zero-length). + * If null is returned, then Skia will take its default action. + * + * The default action for pictures is to use Skia's internal format. + * The default action for images is to encode either in its native format or PNG. + * The default action for typefaces is to use Skia's internal format. + */ + +using SkSerialPictureProc = sk_sp (*)(SkPicture*, void* ctx); +using SkSerialImageProc = sk_sp (*)(SkImage*, void* ctx); +using SkSerialTypefaceProc = sk_sp (*)(SkTypeface*, void* ctx); + +/** + * Called with the encoded form of a picture (previously written with a custom + * SkSerialPictureProc proc). Return a picture object, or nullptr indicating failure. + */ +using SkDeserialPictureProc = sk_sp (*)(const void* data, size_t length, void* ctx); + +/** + * Called with the encoded form of an image. The proc can return an image object, or if it + * returns nullptr, then Skia will take its default action to try to create an image from the data. + * + * This will also be used to decode the internal mipmap layers that are saved on some images. + * + * An explicit SkAlphaType may have been encoded in the bytestream; if not, then the passed in + * optional will be not present. + * + * Clients should set at least SkDeserialImageProc; SkDeserialImageFromDataProc may be called + * if the internal implementation has a SkData copy already. Implementations of SkDeserialImageProc + * must make a copy of any data they needed after the proc finishes, since the data will go away + * after serialization ends. + */ +#if !defined(SK_LEGACY_DESERIAL_IMAGE_PROC) +using SkDeserialImageProc = sk_sp (*)(const void* data, size_t length, void* ctx); +#else +using SkDeserialImageProc = sk_sp (*)(const void* data, + size_t length, + std::optional, + void* ctx); +#endif +using SkDeserialImageFromDataProc = sk_sp (*)(sk_sp, + std::optional, + void* ctx); + +/** + * Slugs are currently only deserializable with a GPU backend. Clients will not be able to + * provide a custom mechanism here, but can enable Slug deserialization by calling + * sktext::gpu::AddDeserialProcs to add Skia's implementation. + */ +using SkSlugProc = sk_sp (*)(SkReadBuffer&, void* ctx); + +/** + * Called with the encoded form of a typeface (previously written with a custom + * SkSerialTypefaceProc proc). Return a typeface object, or nullptr indicating failure. + */ +using SkDeserialTypefaceProc = sk_sp (*)(const void* data, size_t length, void* ctx); + +struct SK_API SkSerialProcs { + SkSerialPictureProc fPictureProc = nullptr; + void* fPictureCtx = nullptr; + + SkSerialImageProc fImageProc = nullptr; + void* fImageCtx = nullptr; + + SkSerialTypefaceProc fTypefaceProc = nullptr; + void* fTypefaceCtx = nullptr; +}; + +struct SK_API SkDeserialProcs { + SkDeserialPictureProc fPictureProc = nullptr; + void* fPictureCtx = nullptr; + + SkDeserialImageProc fImageProc = nullptr; + SkDeserialImageFromDataProc fImageDataProc = nullptr; + void* fImageCtx = nullptr; + + SkSlugProc fSlugProc = nullptr; + void* fSlugCtx = nullptr; + + SkDeserialTypefaceProc fTypefaceProc = nullptr; + void* fTypefaceCtx = nullptr; + + // This looks like a flag, but it could be considered a proc as well (one that takes no + // parameters and returns a bool). Given that there are only two valid implementations of that + // proc, we just insert the bool directly. + bool fAllowSkSL = true; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkShader.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkShader.h new file mode 100644 index 0000000000..b650a03d7b --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkShader.h @@ -0,0 +1,112 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkShader_DEFINED +#define SkShader_DEFINED + +#include "include/core/SkColor.h" +#include "include/core/SkFlattenable.h" +#include "include/core/SkRefCnt.h" +#include "include/private/base/SkAPI.h" + +class SkBlender; +class SkColorFilter; +class SkColorSpace; +class SkImage; +class SkMatrix; +enum class SkBlendMode; +enum class SkTileMode; +struct SkRect; +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 + * 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 + * to be modified. + */ +class SK_API SkShader : public SkFlattenable { +public: + /** + * Returns true if the shader is guaranteed to produce only opaque + * colors, subject to the SkPaint using the shader to apply an opaque + * alpha value. Subclasses should override this to allow some + * optimizations. + */ + virtual bool isOpaque() const { return false; } + + /** + * Iff this shader is backed by a single SkImage, return its ptr (the caller must ref this + * if they want to keep it longer than the lifetime of the shader). If not, return nullptr. + */ + SkImage* isAImage(SkMatrix* localMatrix, SkTileMode xy[2]) const; + + bool isAImage() const { + return this->isAImage(nullptr, (SkTileMode*)nullptr) != nullptr; + } + + ////////////////////////////////////////////////////////////////////////// + // Methods to create combinations or variants of shaders + + /** + * Return a shader that will apply the specified localMatrix to this shader. + * The specified matrix will be applied before any matrix associated with this shader. + */ + sk_sp makeWithLocalMatrix(const SkMatrix&) const; + + /** + * Create a new shader that produces the same colors as invoking this shader and then applying + * the colorfilter. + */ + sk_sp makeWithColorFilter(sk_sp) const; + + /** + * Return a shader that will compute this shader in a specific color space. + * By default, all shaders operate in the destination (surface) color space. + * The results of a shader are still always converted to the destination - this + * API has no impact on simple shaders or images. Primarily, it impacts shaders + * that perform mathematical operations, like Blend shaders, or runtime shaders. + */ + sk_sp makeWithWorkingColorSpace(sk_sp) const; + +private: + SkShader() = default; + friend class SkShaderBase; + + using INHERITED = SkFlattenable; +}; + +namespace SkShaders { +SK_API sk_sp Empty(); +SK_API sk_sp Color(SkColor); +SK_API sk_sp Color(const SkColor4f&, sk_sp); +SK_API sk_sp Blend(SkBlendMode mode, sk_sp dst, sk_sp src); +SK_API sk_sp Blend(sk_sp, sk_sp dst, sk_sp src); +SK_API sk_sp CoordClamp(sk_sp, const SkRect& subset); + +/* + * Create an SkShader that will sample the 'image'. This is equivalent to SkImage::makeShader. + */ +SK_API sk_sp Image(sk_sp image, + SkTileMode tmx, SkTileMode tmy, + const SkSamplingOptions& options, + const SkMatrix* localMatrix = nullptr); +/* + * Create an SkShader that will sample 'image' with minimal processing. This is equivalent to + * SkImage::makeRawShader. + */ +SK_API sk_sp RawImage(sk_sp image, + SkTileMode tmx, SkTileMode tmy, + const SkSamplingOptions& options, + const SkMatrix* localMatrix = nullptr); +} + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkSize.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkSize.h new file mode 100644 index 0000000000..3be747d274 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkSize.h @@ -0,0 +1,93 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkSize_DEFINED +#define SkSize_DEFINED + +#include "include/core/SkScalar.h" +#include "include/private/base/SkTo.h" + +#include + +struct SkISize { + int32_t fWidth; + int32_t fHeight; + + static constexpr SkISize Make(int32_t w, int32_t h) { return {w, h}; } + + static constexpr SkISize MakeEmpty() { return {0, 0}; } + + void set(int32_t w, int32_t h) { *this = SkISize{w, h}; } + + /** Returns true iff fWidth == 0 && fHeight == 0 + */ + bool isZero() const { return 0 == fWidth && 0 == fHeight; } + + /** Returns true if either width or height are <= 0 */ + bool isEmpty() const { return fWidth <= 0 || fHeight <= 0; } + + /** Set the width and height to 0 */ + void setEmpty() { fWidth = fHeight = 0; } + + constexpr int32_t width() const { return fWidth; } + constexpr int32_t height() const { return fHeight; } + + constexpr int64_t area() const { return SkToS64(fWidth) * SkToS64(fHeight); } + + bool equals(int32_t w, int32_t h) const { return fWidth == w && fHeight == h; } +}; + +static inline bool operator==(const SkISize& a, const SkISize& b) { + return a.fWidth == b.fWidth && a.fHeight == b.fHeight; +} + +static inline bool operator!=(const SkISize& a, const SkISize& b) { return !(a == b); } + +/////////////////////////////////////////////////////////////////////////////// + +struct SkSize { + SkScalar fWidth; + SkScalar fHeight; + + static constexpr SkSize Make(SkScalar w, SkScalar h) { return {w, h}; } + + static constexpr SkSize Make(const SkISize& src) { + return {SkIntToScalar(src.width()), SkIntToScalar(src.height())}; + } + + static constexpr SkSize MakeEmpty() { return {0, 0}; } + + void set(SkScalar w, SkScalar h) { *this = SkSize{w, h}; } + + /** Returns true iff fWidth == 0 && fHeight == 0 + */ + bool isZero() const { return 0 == fWidth && 0 == fHeight; } + + /** Returns true if either width or height are <= 0 */ + bool isEmpty() const { return fWidth <= 0 || fHeight <= 0; } + + /** Set the width and height to 0 */ + void setEmpty() { *this = SkSize{0, 0}; } + + SkScalar width() const { return fWidth; } + SkScalar height() const { return fHeight; } + + bool equals(SkScalar w, SkScalar h) const { return fWidth == w && fHeight == h; } + + SkISize toRound() const { return {SkScalarRoundToInt(fWidth), SkScalarRoundToInt(fHeight)}; } + + SkISize toCeil() const { return {SkScalarCeilToInt(fWidth), SkScalarCeilToInt(fHeight)}; } + + SkISize toFloor() const { return {SkScalarFloorToInt(fWidth), SkScalarFloorToInt(fHeight)}; } +}; + +static inline bool operator==(const SkSize& a, const SkSize& b) { + return a.fWidth == b.fWidth && a.fHeight == b.fHeight; +} + +static inline bool operator!=(const SkSize& a, const SkSize& b) { return !(a == b); } +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkSpan.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkSpan.h new file mode 100644 index 0000000000..37cac632b1 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkSpan.h @@ -0,0 +1,13 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +// We want SkSpan to be a public API, but it is also fundamental to many of our internal types. +// Thus, we have a public file that clients can include. This file defers to the private copy +// so we do not have a dependency cycle from our "base" files to our "core" files. + +#include "include/private/base/SkSpan_impl.h" // IWYU pragma: export + diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkStream.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkStream.h new file mode 100644 index 0000000000..208bfe2903 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkStream.h @@ -0,0 +1,512 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkStream_DEFINED +#define SkStream_DEFINED + +#include "include/core/SkData.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" +#include "include/private/base/SkCPUTypes.h" +#include "include/private/base/SkTo.h" + +#include +#include +#include +#include +#include +class SkStreamAsset; + +/** + * SkStream -- abstraction for a source of bytes. Subclasses can be backed by + * memory, or a file, or something else. + */ +class SK_API SkStream { +public: + virtual ~SkStream() {} + SkStream() {} + + /** + * Attempts to open the specified file as a stream, returns nullptr on failure. + */ + static std::unique_ptr MakeFromFile(const char path[]); + + /** Reads or skips size number of bytes. + * If buffer == NULL, skip size bytes, return how many were skipped. + * If buffer != NULL, copy size bytes into buffer, return how many were copied. + * @param buffer when NULL skip size bytes, otherwise copy size bytes into buffer + * @param size the number of bytes to skip or copy + * @return the number of bytes actually read. + */ + virtual size_t read(void* buffer, size_t size) = 0; + + /** Skip size number of bytes. + * @return the actual number bytes that could be skipped. + */ + size_t skip(size_t size) { + return this->read(nullptr, size); + } + + /** + * Attempt to peek at size bytes. + * If this stream supports peeking, copy min(size, peekable bytes) into + * buffer, and return the number of bytes copied. + * If the stream does not support peeking, or cannot peek any bytes, + * return 0 and leave buffer unchanged. + * The stream is guaranteed to be in the same visible state after this + * call, regardless of success or failure. + * @param buffer Must not be NULL, and must be at least size bytes. Destination + * to copy bytes. + * @param size Number of bytes to copy. + * @return The number of bytes peeked/copied. + */ + virtual size_t peek(void* /*buffer*/, size_t /*size*/) const { return 0; } + + /** Returns true when all the bytes in the stream have been read. + * As SkStream represents synchronous I/O, isAtEnd returns false when the + * final stream length isn't known yet, even when all the bytes available + * so far have been read. + * This may return true early (when there are no more bytes to be read) + * or late (after the first unsuccessful read). + */ + virtual bool isAtEnd() const = 0; + + [[nodiscard]] bool readS8(int8_t*); + [[nodiscard]] bool readS16(int16_t*); + [[nodiscard]] bool readS32(int32_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 readBool(bool* b) { + uint8_t i; + if (!this->readU8(&i)) { return false; } + *b = (i != 0); + return true; + } + [[nodiscard]] bool readScalar(SkScalar*); + [[nodiscard]] bool readPackedUInt(size_t*); + +//SkStreamRewindable + /** Rewinds to the beginning of the stream. Returns true if the stream is known + * to be at the beginning after this call returns. + */ + virtual bool rewind() { return false; } + + /** Duplicates this stream. If this cannot be done, returns NULL. + * The returned stream will be positioned at the beginning of its data. + */ + std::unique_ptr duplicate() const { + return std::unique_ptr(this->onDuplicate()); + } + /** Duplicates this stream. If this cannot be done, returns NULL. + * The returned stream will be positioned the same as this stream. + */ + std::unique_ptr fork() const { + return std::unique_ptr(this->onFork()); + } + +//SkStreamSeekable + /** Returns true if this stream can report its current position. */ + virtual bool hasPosition() const { return false; } + /** Returns the current position in the stream. If this cannot be done, returns 0. */ + virtual size_t getPosition() const { return 0; } + + /** Seeks to an absolute position in the stream. If this cannot be done, returns false. + * If an attempt is made to seek past the end of the stream, the position will be set + * to the end of the stream. + */ + virtual bool seek(size_t /*position*/) { return false; } + + /** Seeks to an relative offset in the stream. If this cannot be done, returns false. + * If an attempt is made to move to a position outside the stream, the position will be set + * to the closest point within the stream (beginning or end). + */ + virtual bool move(long /*offset*/) { return false; } + +//SkStreamAsset + /** Returns true if this stream can report its total length. */ + virtual bool hasLength() const { return false; } + /** Returns the total length of the stream. If this cannot be done, returns 0. */ + virtual size_t getLength() const { return 0; } + +//SkStreamMemory + /** Returns the starting address for the data. If this cannot be done, returns NULL. */ + virtual const void* getMemoryBase() { return nullptr; } + virtual sk_sp getData() const { return nullptr; } + +private: + virtual SkStream* onDuplicate() const { return nullptr; } + virtual SkStream* onFork() const { return nullptr; } + + SkStream(SkStream&&) = delete; + SkStream(const SkStream&) = delete; + SkStream& operator=(SkStream&&) = delete; + SkStream& operator=(const SkStream&) = delete; +}; + +/** SkStreamRewindable is a SkStream for which rewind and duplicate are required. */ +class SK_API SkStreamRewindable : public SkStream { +public: + bool rewind() override = 0; + std::unique_ptr duplicate() const { + return std::unique_ptr(this->onDuplicate()); + } +private: + SkStreamRewindable* onDuplicate() const override = 0; +}; + +/** SkStreamSeekable is a SkStreamRewindable for which position, seek, move, and fork are required. */ +class SK_API SkStreamSeekable : public SkStreamRewindable { +public: + std::unique_ptr duplicate() const { + return std::unique_ptr(this->onDuplicate()); + } + + bool hasPosition() const override { return true; } + size_t getPosition() const override = 0; + bool seek(size_t position) override = 0; + bool move(long offset) override = 0; + + std::unique_ptr fork() const { + return std::unique_ptr(this->onFork()); + } +private: + SkStreamSeekable* onDuplicate() const override = 0; + SkStreamSeekable* onFork() const override = 0; +}; + +/** SkStreamAsset is a SkStreamSeekable for which getLength is required. */ +class SK_API SkStreamAsset : public SkStreamSeekable { +public: + bool hasLength() const override { return true; } + size_t getLength() const override = 0; + + std::unique_ptr duplicate() const { + return std::unique_ptr(this->onDuplicate()); + } + std::unique_ptr fork() const { + return std::unique_ptr(this->onFork()); + } +private: + SkStreamAsset* onDuplicate() const override = 0; + SkStreamAsset* onFork() const override = 0; +}; + +/** SkStreamMemory is a SkStreamAsset for which getMemoryBase is required. */ +class SK_API SkStreamMemory : public SkStreamAsset { +public: + const void* getMemoryBase() override = 0; + + std::unique_ptr duplicate() const { + return std::unique_ptr(this->onDuplicate()); + } + std::unique_ptr fork() const { + return std::unique_ptr(this->onFork()); + } +private: + SkStreamMemory* onDuplicate() const override = 0; + SkStreamMemory* onFork() const override = 0; +}; + +class SK_API SkWStream { +public: + virtual ~SkWStream(); + SkWStream() {} + + /** Called to write bytes to a SkWStream. Returns true on success + @param buffer the address of at least size bytes to be written to the stream + @param size The number of bytes in buffer to write to the stream + @return true on success + */ + virtual bool write(const void* buffer, size_t size) = 0; + virtual void flush(); + + virtual size_t bytesWritten() const = 0; + + // helpers + + bool write8(U8CPU value) { + uint8_t v = SkToU8(value); + return this->write(&v, 1); + } + bool write16(U16CPU value) { + uint16_t v = SkToU16(value); + return this->write(&v, 2); + } + bool write32(uint32_t v) { + return this->write(&v, 4); + } + + bool writeText(const char text[]) { + SkASSERT(text); + return this->write(text, std::strlen(text)); + } + + bool newline() { return this->write("\n", std::strlen("\n")); } + + bool writeDecAsText(int32_t); + bool writeBigDecAsText(int64_t, int minDigits = 0); + bool writeHexAsText(uint32_t, int minDigits = 0); + bool writeScalarAsText(SkScalar); + + bool writeBool(bool v) { return this->write8(v); } + bool writeScalar(SkScalar); + bool writePackedUInt(size_t); + + bool writeStream(SkStream* input, size_t length); + + /** + * This returns the number of bytes in the stream required to store + * 'value'. + */ + static int SizeOfPackedUInt(size_t value); + +private: + SkWStream(const SkWStream&) = delete; + SkWStream& operator=(const SkWStream&) = delete; +}; + +class SK_API SkNullWStream : public SkWStream { +public: + SkNullWStream() : fBytesWritten(0) {} + + bool write(const void* , size_t n) override { fBytesWritten += n; return true; } + void flush() override {} + size_t bytesWritten() const override { return fBytesWritten; } + +private: + size_t fBytesWritten; +}; + +//////////////////////////////////////////////////////////////////////////////////////// + +/** A stream that wraps a C FILE* file stream. */ +class SK_API SkFILEStream : public SkStreamAsset { +public: + /** Initialize the stream by calling sk_fopen on the specified path. + * This internal stream will be closed in the destructor. + */ + explicit SkFILEStream(const char path[] = nullptr); + + /** Initialize the stream with an existing C FILE stream. + * The current position of the C FILE stream will be considered the + * beginning of the SkFILEStream and the current seek end of the FILE will be the end. + * The C FILE stream will be closed in the destructor. + */ + explicit SkFILEStream(FILE* file); + + /** Initialize the stream with an existing C FILE stream. + * The current position of the C FILE stream will be considered the + * beginning of the SkFILEStream and size bytes later will be the end. + * The C FILE stream will be closed in the destructor. + */ + explicit SkFILEStream(FILE* file, size_t size); + + ~SkFILEStream() override; + + static std::unique_ptr Make(const char path[]) { + std::unique_ptr stream(new SkFILEStream(path)); + return stream->isValid() ? std::move(stream) : nullptr; + } + + /** Returns true if the current path could be opened. */ + bool isValid() const { return fFILE != nullptr; } + + /** Close this SkFILEStream. */ + void close(); + + size_t read(void* buffer, size_t size) override; + bool isAtEnd() const override; + + bool rewind() override; + std::unique_ptr duplicate() const { + return std::unique_ptr(this->onDuplicate()); + } + + size_t getPosition() const override; + bool seek(size_t position) override; + bool move(long offset) override; + + std::unique_ptr fork() const { + return std::unique_ptr(this->onFork()); + } + + size_t getLength() const override; + +private: + explicit SkFILEStream(FILE*, size_t size, size_t start); + explicit SkFILEStream(std::shared_ptr, size_t end, size_t start); + explicit SkFILEStream(std::shared_ptr, size_t end, size_t start, size_t current); + + SkStreamAsset* onDuplicate() const override; + SkStreamAsset* onFork() const override; + + std::shared_ptr fFILE; + // My own council will I keep on sizes and offsets. + // These are seek positions in the underling FILE, not offsets into the stream. + size_t fEnd; + size_t fStart; + size_t fCurrent; + + using INHERITED = SkStreamAsset; +}; + +class SK_API SkMemoryStream : public SkStreamMemory { +public: + SkMemoryStream(); + + /** We allocate (and free) the memory. Write to it via getMemoryBase() */ + SkMemoryStream(size_t length); + + /** If copyData is true, the stream makes a private copy of the data. */ + SkMemoryStream(const void* data, size_t length, bool copyData = false); + + /** Creates the stream to read from the specified data */ + SkMemoryStream(sk_sp data); + + /** Returns a stream with a copy of the input data. */ + static std::unique_ptr MakeCopy(const void* data, size_t length); + + /** Returns a stream with a bare pointer reference to the input data. */ + static std::unique_ptr MakeDirect(const void* data, size_t length); + + /** Returns a stream with a shared reference to the input data. */ + static std::unique_ptr Make(sk_sp data); + + /** Resets the stream to the specified data and length, + just like the constructor. + if copyData is true, the stream makes a private copy of the data + */ + virtual void setMemory(const void* data, size_t length, + bool copyData = false); + /** Replace any memory buffer with the specified buffer. The caller + must have allocated data with sk_malloc or sk_realloc, since it + will be freed with sk_free. + */ + void setMemoryOwned(const void* data, size_t length); + + sk_sp getData() const override { return fData; } + void setData(sk_sp data); + + const void* getAtPos(); + + size_t read(void* buffer, size_t size) override; + bool isAtEnd() const override; + + size_t peek(void* buffer, size_t size) const override; + + bool rewind() override; + + std::unique_ptr duplicate() const { + return std::unique_ptr(this->onDuplicate()); + } + + size_t getPosition() const override; + bool seek(size_t position) override; + bool move(long offset) override; + + std::unique_ptr fork() const { + return std::unique_ptr(this->onFork()); + } + + size_t getLength() const override; + + const void* getMemoryBase() override; + +private: + SkMemoryStream* onDuplicate() const override; + SkMemoryStream* onFork() const override; + + sk_sp fData; + size_t fOffset; + + using INHERITED = SkStreamMemory; +}; + +///////////////////////////////////////////////////////////////////////////////////////////// + +class SK_API SkFILEWStream : public SkWStream { +public: + SkFILEWStream(const char path[]); + ~SkFILEWStream() override; + + /** Returns true if the current path could be opened. + */ + bool isValid() const { return fFILE != nullptr; } + + bool write(const void* buffer, size_t size) override; + void flush() override; + void fsync(); + size_t bytesWritten() const override; + +private: + FILE* fFILE; + + using INHERITED = SkWStream; +}; + +class SK_API SkDynamicMemoryWStream : public SkWStream { +public: + SkDynamicMemoryWStream() = default; + SkDynamicMemoryWStream(SkDynamicMemoryWStream&&); + SkDynamicMemoryWStream& operator=(SkDynamicMemoryWStream&&); + ~SkDynamicMemoryWStream() override; + + bool write(const void* buffer, size_t size) override; + size_t bytesWritten() const override; + + bool read(void* buffer, size_t offset, size_t size); + + /** More efficient version of read(dst, 0, bytesWritten()). */ + void copyTo(void* dst) const; + bool writeToStream(SkWStream* dst) const; + + /** Equivalent to copyTo() followed by reset(), but may save memory use. */ + void copyToAndReset(void* dst); + + /** Equivalent to writeToStream() followed by reset(), but may save memory use. */ + bool writeToAndReset(SkWStream* dst); + + /** Equivalent to writeToStream() followed by reset(), but may save memory use. + When the dst is also a SkDynamicMemoryWStream, the implementation is constant time. */ + bool writeToAndReset(SkDynamicMemoryWStream* dst); + + /** Prepend this stream to dst, resetting this. */ + void prependToAndReset(SkDynamicMemoryWStream* dst); + + /** Return the contents as SkData, and then reset the stream. */ + sk_sp detachAsData(); + + /** Reset, returning a reader stream with the current content. */ + std::unique_ptr detachAsStream(); + + /** Reset the stream to its original, empty, state. */ + void reset(); + void padToAlign4(); +private: + struct Block; + Block* fHead = nullptr; + Block* fTail = nullptr; + size_t fBytesWrittenBeforeTail = 0; + +#ifdef SK_DEBUG + void validate() const; +#else + void validate() const {} +#endif + + // For access to the Block type. + friend class SkBlockMemoryStream; + friend class SkBlockMemoryRefCnt; + + using INHERITED = SkWStream; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkString.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkString.h new file mode 100644 index 0000000000..819e14d858 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkString.h @@ -0,0 +1,293 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkString_DEFINED +#define SkString_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" +#include "include/private/base/SkTo.h" +#include "include/private/base/SkTypeTraits.h" + +#include +#include +#include +#include +#include +#include +#include + +/* Some helper functions for C strings */ +static inline bool SkStrStartsWith(const char string[], const char prefixStr[]) { + SkASSERT(string); + SkASSERT(prefixStr); + return !strncmp(string, prefixStr, strlen(prefixStr)); +} +static inline bool SkStrStartsWith(const char string[], const char prefixChar) { + SkASSERT(string); + return (prefixChar == *string); +} + +bool SkStrEndsWith(const char string[], const char suffixStr[]); +bool SkStrEndsWith(const char string[], const char suffixChar); + +int SkStrStartsWithOneOf(const char string[], const char prefixes[]); + +static inline int SkStrFind(const char string[], const char substring[]) { + const char *first = strstr(string, substring); + if (nullptr == first) return -1; + return SkToInt(first - &string[0]); +} + +static inline int SkStrFindLastOf(const char string[], const char subchar) { + const char* last = strrchr(string, subchar); + if (nullptr == last) return -1; + return SkToInt(last - &string[0]); +} + +static inline bool SkStrContains(const char string[], const char substring[]) { + SkASSERT(string); + SkASSERT(substring); + return (-1 != SkStrFind(string, substring)); +} +static inline bool SkStrContains(const char string[], const char subchar) { + SkASSERT(string); + char tmp[2]; + tmp[0] = subchar; + tmp[1] = '\0'; + return (-1 != SkStrFind(string, tmp)); +} + +/* + * The SkStrAppend... methods will write into the provided buffer, assuming it is large enough. + * Each method has an associated const (e.g. kSkStrAppendU32_MaxSize) which will be the largest + * value needed for that method's buffer. + * + * char storage[kSkStrAppendU32_MaxSize]; + * SkStrAppendU32(storage, value); + * + * Note : none of the SkStrAppend... methods write a terminating 0 to their buffers. Instead, + * the methods return the ptr to the end of the written part of the buffer. This can be used + * to compute the length, and/or know where to write a 0 if that is desired. + * + * char storage[kSkStrAppendU32_MaxSize + 1]; + * char* stop = SkStrAppendU32(storage, value); + * size_t len = stop - storage; + * *stop = 0; // valid, since storage was 1 byte larger than the max. + */ + +static constexpr int kSkStrAppendU32_MaxSize = 10; +char* SkStrAppendU32(char buffer[], uint32_t); +static constexpr int kSkStrAppendU64_MaxSize = 20; +char* SkStrAppendU64(char buffer[], uint64_t, int minDigits); + +static constexpr int kSkStrAppendS32_MaxSize = kSkStrAppendU32_MaxSize + 1; +char* SkStrAppendS32(char buffer[], int32_t); +static constexpr int kSkStrAppendS64_MaxSize = kSkStrAppendU64_MaxSize + 1; +char* SkStrAppendS64(char buffer[], int64_t, int minDigits); + +/** + * Floats have at most 8 significant digits, so we limit our %g to that. + * However, the total string could be 15 characters: -1.2345678e-005 + * + * In theory we should only expect up to 2 digits for the exponent, but on + * some platforms we have seen 3 (as in the example above). + */ +static constexpr int kSkStrAppendScalar_MaxSize = 15; + +/** + * Write the scalar in decimal format into buffer, and return a pointer to + * the next char after the last one written. Note: a terminating 0 is not + * written into buffer, which must be at least kSkStrAppendScalar_MaxSize. + * Thus if the caller wants to add a 0 at the end, buffer must be at least + * kSkStrAppendScalar_MaxSize + 1 bytes large. + */ +char* SkStrAppendScalar(char buffer[], SkScalar); + +/** \class SkString + + Light weight class for managing strings. Uses reference + counting to make string assignments and copies very fast + with no extra RAM cost. Assumes UTF8 encoding. +*/ +class SK_API SkString { +public: + SkString(); + explicit SkString(size_t len); + explicit SkString(const char text[]); + SkString(const char text[], size_t len); + SkString(const SkString&); + SkString(SkString&&); + explicit SkString(const std::string&); + explicit SkString(std::string_view); + ~SkString(); + + bool isEmpty() const { return 0 == fRec->fLength; } + size_t size() const { return (size_t) fRec->fLength; } + const char* data() const { return fRec->data(); } + const char* c_str() const { return fRec->data(); } + char operator[](size_t n) const { return this->c_str()[n]; } + + bool equals(const SkString&) const; + bool equals(const char text[]) const; + bool equals(const char text[], size_t len) const; + + bool startsWith(const char prefixStr[]) const { + return SkStrStartsWith(fRec->data(), prefixStr); + } + bool startsWith(const 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 { + return SkStrEndsWith(fRec->data(), suffixChar); + } + bool contains(const char substring[]) const { + return SkStrContains(fRec->data(), substring); + } + bool contains(const 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 { + return SkStrFindLastOf(fRec->data(), subchar); + } + + friend bool operator==(const SkString& a, const SkString& b) { + return a.equals(b); + } + friend bool operator!=(const SkString& a, const SkString& b) { + return !a.equals(b); + } + + // these methods edit the string + + SkString& operator=(const SkString&); + SkString& operator=(SkString&&); + SkString& operator=(const char text[]); + + char* data(); + char& operator[](size_t n) { return this->data()[n]; } + + void reset(); + /** String contents are preserved on resize. (For destructive resize, `set(nullptr, length)`.) + * `resize` automatically reserves an extra byte at the end of the buffer for a null terminator. + */ + void resize(size_t len); + void set(const SkString& src) { *this = src; } + void set(const char text[]); + void set(const char text[], size_t len); + void set(std::string_view str) { this->set(str.data(), str.size()); } + + void insert(size_t offset, const char text[]); + void insert(size_t offset, const char text[], size_t len); + void insert(size_t offset, const SkString& str) { this->insert(offset, str.c_str(), str.size()); } + void insert(size_t offset, std::string_view str) { this->insert(offset, str.data(), str.size()); } + void insertUnichar(size_t offset, SkUnichar); + void insertS32(size_t offset, int32_t value); + void insertS64(size_t offset, int64_t value, int minDigits = 0); + void insertU32(size_t offset, uint32_t value); + void insertU64(size_t offset, uint64_t value, int minDigits = 0); + void insertHex(size_t offset, uint32_t value, int minDigits = 0); + void insertScalar(size_t offset, SkScalar); + + void append(const char text[]) { this->insert((size_t)-1, text); } + void append(const char text[], size_t len) { this->insert((size_t)-1, text, len); } + void append(const SkString& str) { this->insert((size_t)-1, str.c_str(), str.size()); } + void append(std::string_view str) { this->insert((size_t)-1, str.data(), str.size()); } + void appendUnichar(SkUnichar uni) { this->insertUnichar((size_t)-1, uni); } + void appendS32(int32_t value) { this->insertS32((size_t)-1, value); } + void appendS64(int64_t value, int minDigits = 0) { this->insertS64((size_t)-1, value, minDigits); } + void appendU32(uint32_t value) { this->insertU32((size_t)-1, value); } + void appendU64(uint64_t value, int minDigits = 0) { this->insertU64((size_t)-1, value, minDigits); } + void appendHex(uint32_t value, int minDigits = 0) { this->insertHex((size_t)-1, value, minDigits); } + void appendScalar(SkScalar value) { this->insertScalar((size_t)-1, value); } + + void prepend(const char text[]) { this->insert(0, text); } + void prepend(const char text[], size_t len) { this->insert(0, text, len); } + void prepend(const SkString& str) { this->insert(0, str.c_str(), str.size()); } + void prepend(std::string_view str) { this->insert(0, str.data(), str.size()); } + void prependUnichar(SkUnichar uni) { this->insertUnichar(0, uni); } + void prependS32(int32_t value) { this->insertS32(0, value); } + void prependS64(int32_t value, int minDigits = 0) { this->insertS64(0, value, minDigits); } + void prependHex(uint32_t value, int minDigits = 0) { this->insertHex(0, value, minDigits); } + void prependScalar(SkScalar value) { this->insertScalar((size_t)-1, value); } + + void printf(const char format[], ...) SK_PRINTF_LIKE(2, 3); + void printVAList(const char format[], va_list) SK_PRINTF_LIKE(2, 0); + void appendf(const char format[], ...) SK_PRINTF_LIKE(2, 3); + void appendVAList(const char format[], va_list) SK_PRINTF_LIKE(2, 0); + void prependf(const char format[], ...) SK_PRINTF_LIKE(2, 3); + void prependVAList(const char format[], va_list) SK_PRINTF_LIKE(2, 0); + + void remove(size_t offset, size_t length); + + SkString& operator+=(const SkString& s) { this->append(s); return *this; } + SkString& operator+=(const char text[]) { this->append(text); return *this; } + SkString& operator+=(const char c) { this->append(&c, 1); return *this; } + + /** + * Swap contents between this and other. This function is guaranteed + * to never fail or throw. + */ + void swap(SkString& other); + + using sk_is_trivially_relocatable = std::true_type; + +private: + struct Rec { + public: + constexpr Rec(uint32_t len, int32_t refCnt) : fLength(len), fRefCnt(refCnt) {} + static sk_sp Make(const char text[], size_t len); + char* data() { return fBeginningOfData; } + const char* data() const { return fBeginningOfData; } + void ref() const; + void unref() const; + bool unique() const; +#ifdef SK_DEBUG + int32_t getRefCnt() const; +#endif + uint32_t fLength; // logically size_t, but we want it to stay 32 bits + + private: + mutable std::atomic fRefCnt; + char fBeginningOfData[1] = {'\0'}; + + // Ensure the unsized delete is called. + void operator delete(void* p) { ::operator delete(p); } + }; + sk_sp fRec; + + static_assert(::sk_is_trivially_relocatable::value); + +#ifdef SK_DEBUG + SkString& validate(); + const SkString& validate() const; +#else + SkString& validate() { return *this; } + const SkString& validate() const { return *this; } +#endif + + static const Rec gEmptyRec; +}; + +/// Creates a new string and writes into it using a printf()-style format. +SK_API SkString SkStringPrintf(const char* format, ...) SK_PRINTF_LIKE(1, 2); +/// This makes it easier to write a caller as a VAR_ARGS function where the format string is +/// optional. +static inline SkString SkStringPrintf() { return SkString(); } + +static inline void swap(SkString& a, SkString& b) { + a.swap(b); +} + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkStrokeRec.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkStrokeRec.h new file mode 100644 index 0000000000..8617b25407 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkStrokeRec.h @@ -0,0 +1,159 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkStrokeRec_DEFINED +#define SkStrokeRec_DEFINED + +#include "include/core/SkPaint.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" +#include "include/private/base/SkMacros.h" + +#include +#include + +class SkPath; + +SK_BEGIN_REQUIRE_DENSE +class SK_API SkStrokeRec { +public: + enum InitStyle { + kHairline_InitStyle, + kFill_InitStyle + }; + SkStrokeRec(InitStyle style); + SkStrokeRec(const SkPaint&, SkPaint::Style, SkScalar resScale = 1); + explicit SkStrokeRec(const SkPaint&, SkScalar resScale = 1); + + enum Style { + kHairline_Style, + kFill_Style, + kStroke_Style, + kStrokeAndFill_Style + }; + + static constexpr int kStyleCount = kStrokeAndFill_Style + 1; + + Style getStyle() const; + SkScalar getWidth() const { return fWidth; } + SkScalar getMiter() const { return fMiterLimit; } + SkPaint::Cap getCap() const { return (SkPaint::Cap)fCap; } + SkPaint::Join getJoin() const { return (SkPaint::Join)fJoin; } + + bool isHairlineStyle() const { + return kHairline_Style == this->getStyle(); + } + + bool isFillStyle() const { + return kFill_Style == this->getStyle(); + } + + void setFillStyle(); + void setHairlineStyle(); + /** + * Specify the strokewidth, and optionally if you want stroke + fill. + * Note, if width==0, then this request is taken to mean: + * strokeAndFill==true -> new style will be Fill + * strokeAndFill==false -> new style will be Hairline + */ + void setStrokeStyle(SkScalar width, bool strokeAndFill = false); + + void setStrokeParams(SkPaint::Cap cap, SkPaint::Join join, SkScalar miterLimit) { + fCap = cap; + fJoin = join; + fMiterLimit = miterLimit; + } + + SkScalar getResScale() const { + return fResScale; + } + + void setResScale(SkScalar rs) { + SkASSERT(rs > 0 && std::isfinite(rs)); + fResScale = rs; + } + + /** + * Returns true if this specifes any thick stroking, i.e. applyToPath() + * will return true. + */ + bool needToApply() const { + Style style = this->getStyle(); + return (kStroke_Style == style) || (kStrokeAndFill_Style == style); + } + + /** + * Apply these stroke parameters to the src path, returning the result + * in dst. + * + * If there was no change (i.e. style == hairline or fill) this returns + * false and dst is unchanged. Otherwise returns true and the result is + * stored in dst. + * + * src and dst may be the same path. + */ + bool applyToPath(SkPath* dst, const SkPath& src) const; + + /** + * Apply these stroke parameters to a paint. + */ + void applyToPaint(SkPaint* paint) const; + + /** + * Gives a conservative value for the outset that should applied to a + * geometries bounds to account for any inflation due to applying this + * strokeRec to the geometry. + */ + SkScalar getInflationRadius() const; + + /** + * Equivalent to: + * SkStrokeRec rec(paint, style); + * rec.getInflationRadius(); + * This does not account for other effects on the paint (i.e. path + * effect). + */ + static SkScalar GetInflationRadius(const SkPaint&, SkPaint::Style); + + static SkScalar GetInflationRadius(SkPaint::Join, SkScalar miterLimit, SkPaint::Cap, + SkScalar strokeWidth); + + /** + * Compare if two SkStrokeRecs have an equal effect on a path. + * Equal SkStrokeRecs produce equal paths. Equality of produced + * paths does not take the ResScale parameter into account. + */ + bool hasEqualEffect(const SkStrokeRec& other) const { + if (!this->needToApply()) { + return this->getStyle() == other.getStyle(); + } + return fWidth == other.fWidth && + (fJoin != SkPaint::kMiter_Join || fMiterLimit == other.fMiterLimit) && + fCap == other.fCap && + fJoin == other.fJoin && + fStrokeAndFill == other.fStrokeAndFill; + } + +private: + void init(const SkPaint&, SkPaint::Style, SkScalar resScale); + + SkScalar fResScale; + SkScalar fWidth; + SkScalar fMiterLimit; + // The following three members are packed together into a single u32. + // This is to avoid unnecessary padding and ensure binary equality for + // hashing (because the padded areas might contain garbage values). + // + // fCap and fJoin are larger than needed to avoid having to initialize + // any pad values + uint32_t fCap : 16; // SkPaint::Cap + uint32_t fJoin : 15; // SkPaint::Join + uint32_t fStrokeAndFill : 1; // bool +}; +SK_END_REQUIRE_DENSE + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkSurface.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkSurface.h new file mode 100644 index 0000000000..72f1bc2bf0 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkSurface.h @@ -0,0 +1,659 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkSurface_DEFINED +#define SkSurface_DEFINED + +#include "include/core/SkImage.h" +#include "include/core/SkImageInfo.h" +#include "include/core/SkPixmap.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkSamplingOptions.h" +#include "include/core/SkScalar.h" +#include "include/core/SkSurfaceProps.h" +#include "include/core/SkTypes.h" + +#include +#include +#include + +class GrBackendSemaphore; +class GrBackendTexture; +class GrRecordingContext; +class GrSurfaceCharacterization; +enum GrSurfaceOrigin : int; +class SkBitmap; +class SkCanvas; +class SkCapabilities; +class SkColorSpace; +class SkPaint; +class SkSurface; +struct SkIRect; +struct SkISize; + +namespace skgpu::graphite { +class Recorder; +} + +namespace SkSurfaces { + +enum class BackendSurfaceAccess { + kNoAccess, //!< back-end surface will not be used by client + kPresent, //!< back-end surface will be used for presenting to screen +}; + +/** Returns SkSurface without backing pixels. Drawing to SkCanvas returned from SkSurface + has no effect. Calling makeImageSnapshot() on returned SkSurface returns nullptr. + + @param width one or greater + @param height one or greater + @return SkSurface if width and height are positive; otherwise, nullptr + + example: https://fiddle.skia.org/c/@Surface_MakeNull +*/ +SK_API sk_sp Null(int width, int height); + +/** Allocates raster SkSurface. SkCanvas returned by SkSurface draws directly into those allocated + pixels, which are zeroed before use. Pixel memory size is imageInfo.height() times + imageInfo.minRowBytes() or rowBytes, if provided and non-zero. + + Pixel memory is deleted when SkSurface is deleted. + + Validity constraints include: + - info dimensions are greater than zero; + - info contains SkColorType and SkAlphaType supported by raster surface. + + @param imageInfo width, height, SkColorType, SkAlphaType, SkColorSpace, + of raster surface; width and height must be greater than zero + @param rowBytes interval from one SkSurface row to the next. + @param props LCD striping orientation and setting for device independent fonts; + may be nullptr + @return SkSurface if parameters are valid and memory was allocated, else nullptr. +*/ +SK_API sk_sp Raster(const SkImageInfo& imageInfo, + size_t rowBytes, + const SkSurfaceProps* surfaceProps); +inline sk_sp Raster(const SkImageInfo& imageInfo, + const SkSurfaceProps* props = nullptr) { + return Raster(imageInfo, 0, props); +} + +/** Allocates raster SkSurface. SkCanvas returned by SkSurface draws directly into the + provided pixels. + + SkSurface is returned if all parameters are valid. + Valid parameters include: + info dimensions are greater than zero; + info contains SkColorType and SkAlphaType supported by raster surface; + pixels is not nullptr; + rowBytes is large enough to contain info width pixels of SkColorType. + + Pixel buffer size should be info height times computed rowBytes. + Pixels are not initialized. + To access pixels after drawing, peekPixels() or readPixels(). + + @param imageInfo width, height, SkColorType, SkAlphaType, SkColorSpace, + of raster surface; width and height must be greater than zero + @param pixels pointer to destination pixels buffer + @param rowBytes interval from one SkSurface row to the next + @param surfaceProps LCD striping orientation and setting for device independent fonts; + may be nullptr + @return SkSurface if all parameters are valid; otherwise, nullptr +*/ + +SK_API sk_sp WrapPixels(const SkImageInfo& imageInfo, + void* pixels, + size_t rowBytes, + const SkSurfaceProps* surfaceProps = nullptr); +inline sk_sp WrapPixels(const SkPixmap& pm, const SkSurfaceProps* props = nullptr) { + return WrapPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), props); +} + +using PixelsReleaseProc = void(void* pixels, void* context); + +/** Allocates raster SkSurface. SkCanvas returned by SkSurface draws directly into the provided + pixels. releaseProc is called with pixels and context when SkSurface is deleted. + + SkSurface is returned if all parameters are valid. + Valid parameters include: + info dimensions are greater than zero; + info contains SkColorType and SkAlphaType supported by raster surface; + pixels is not nullptr; + rowBytes is large enough to contain info width pixels of SkColorType. + + Pixel buffer size should be info height times computed rowBytes. + Pixels are not initialized. + To access pixels after drawing, call flush() or peekPixels(). + + @param imageInfo width, height, SkColorType, SkAlphaType, SkColorSpace, + of raster surface; width and height must be greater than zero + @param pixels pointer to destination pixels buffer + @param rowBytes interval from one SkSurface row to the next + @param releaseProc called when SkSurface is deleted; may be nullptr + @param context passed to releaseProc; may be nullptr + @param surfaceProps LCD striping orientation and setting for device independent fonts; + may be nullptr + @return SkSurface if all parameters are valid; otherwise, nullptr +*/ +SK_API sk_sp WrapPixels(const SkImageInfo& imageInfo, + void* pixels, + size_t rowBytes, + PixelsReleaseProc, + void* context, + const SkSurfaceProps* surfaceProps = nullptr); +} // namespace SkSurfaces + +/** \class SkSurface + SkSurface is responsible for managing the pixels that a canvas draws into. The pixels can be + allocated either in CPU memory (a raster surface) or on the GPU (a GrRenderTarget surface). + SkSurface takes care of allocating a SkCanvas that will draw into the surface. Call + surface->getCanvas() to use that canvas (but don't delete it, it is owned by the surface). + SkSurface always has non-zero dimensions. If there is a request for a new surface, and either + of the requested dimensions are zero, then nullptr will be returned. + + Clients should *not* subclass SkSurface as there is a lot of internal machinery that is + not publicly accessible. +*/ +class SK_API SkSurface : public SkRefCnt { +public: + /** Is this surface compatible with the provided characterization? + + This method can be used to determine if an existing SkSurface is a viable destination + for an GrDeferredDisplayList. + + @param characterization The characterization for which a compatibility check is desired + @return true if this surface is compatible with the characterization; + false otherwise + */ + bool isCompatible(const GrSurfaceCharacterization& characterization) const; + + /** Returns pixel count in each row; may be zero or greater. + + @return number of pixel columns + */ + int width() const { return fWidth; } + + /** Returns pixel row count; may be zero or greater. + + @return number of pixel rows + */ + int height() const { return fHeight; } + + /** Returns an ImageInfo describing the surface. + */ + virtual SkImageInfo imageInfo() const { return SkImageInfo::MakeUnknown(fWidth, fHeight); } + + /** Returns unique value identifying the content of SkSurface. Returned value changes + each time the content changes. Content is changed by drawing, or by calling + notifyContentWillChange(). + + @return unique content identifier + + example: https://fiddle.skia.org/c/@Surface_notifyContentWillChange + */ + uint32_t generationID(); + + /** \enum SkSurface::ContentChangeMode + ContentChangeMode members are parameters to notifyContentWillChange(). + */ + enum ContentChangeMode { + kDiscard_ContentChangeMode, //!< discards surface on change + kRetain_ContentChangeMode, //!< preserves surface on change + }; + + /** Notifies that SkSurface contents will be changed by code outside of Skia. + Subsequent calls to generationID() return a different value. + + TODO: Can kRetain_ContentChangeMode be deprecated? + + example: https://fiddle.skia.org/c/@Surface_notifyContentWillChange + */ + void notifyContentWillChange(ContentChangeMode mode); + + /** Returns the recording context being used by the SkSurface. + + @return the recording context, if available; nullptr otherwise + */ + GrRecordingContext* recordingContext() const; + + /** Returns the recorder being used by the SkSurface. + + @return the recorder, if available; nullptr otherwise + */ + skgpu::graphite::Recorder* recorder() const; + + enum class BackendHandleAccess { + kFlushRead, //!< back-end object is readable + kFlushWrite, //!< back-end object is writable + kDiscardWrite, //!< back-end object must be overwritten + + // Legacy names, remove when clients are migrated + kFlushRead_BackendHandleAccess = kFlushRead, + kFlushWrite_BackendHandleAccess = kFlushWrite, + kDiscardWrite_BackendHandleAccess = kDiscardWrite, + }; + + // Legacy names, remove when clients are migrated + static constexpr BackendHandleAccess kFlushRead_BackendHandleAccess = + BackendHandleAccess::kFlushRead; + static constexpr BackendHandleAccess kFlushWrite_BackendHandleAccess = + BackendHandleAccess::kFlushWrite; + static constexpr BackendHandleAccess kDiscardWrite_BackendHandleAccess = + BackendHandleAccess::kDiscardWrite; + + /** Caller data passed to TextureReleaseProc; may be nullptr. */ + using ReleaseContext = void*; + /** User function called when supplied texture may be deleted. */ + using TextureReleaseProc = void (*)(ReleaseContext); + + /** If the surface was made via MakeFromBackendTexture then it's backing texture may be + substituted with a different texture. The contents of the previous backing texture are + copied into the new texture. SkCanvas state is preserved. The original sample count is + used. The GrBackendFormat and dimensions of replacement texture must match that of + the original. + + Upon success textureReleaseProc is called when it is safe to delete the texture in the + backend API (accounting only for use of the texture by this surface). If SkSurface creation + fails textureReleaseProc is called before this function returns. + + @param backendTexture the new backing texture for the surface + @param mode Retain or discard current Content + @param TextureReleaseProc function called when texture can be released + @param ReleaseContext state passed to textureReleaseProc + */ + virtual bool replaceBackendTexture(const GrBackendTexture& backendTexture, + GrSurfaceOrigin origin, + ContentChangeMode mode = kRetain_ContentChangeMode, + TextureReleaseProc = nullptr, + ReleaseContext = nullptr) = 0; + + /** Returns SkCanvas that draws into SkSurface. Subsequent calls return the same SkCanvas. + SkCanvas returned is managed and owned by SkSurface, and is deleted when SkSurface + is deleted. + + @return drawing SkCanvas for SkSurface + + example: https://fiddle.skia.org/c/@Surface_getCanvas + */ + SkCanvas* getCanvas(); + + /** Returns SkCapabilities that describes the capabilities of the SkSurface's device. + + @return SkCapabilities of SkSurface's device. + */ + sk_sp capabilities(); + + /** Returns a compatible SkSurface, or nullptr. Returned SkSurface contains + the same raster, GPU, or null properties as the original. Returned SkSurface + does not share the same pixels. + + Returns nullptr if imageInfo width or height are zero, or if imageInfo + is incompatible with SkSurface. + + @param imageInfo width, height, SkColorType, SkAlphaType, SkColorSpace, + of SkSurface; width and height must be greater than zero + @return compatible SkSurface or nullptr + + example: https://fiddle.skia.org/c/@Surface_makeSurface + */ + sk_sp makeSurface(const SkImageInfo& imageInfo); + + /** Calls makeSurface(ImageInfo) with the same ImageInfo as this surface, but with the + * specified width and height. + */ + sk_sp makeSurface(int width, int height); + + /** Returns SkImage capturing SkSurface contents. Subsequent drawing to SkSurface contents + are not captured. SkImage allocation is accounted for if SkSurface was created with + skgpu::Budgeted::kYes. + + @return SkImage initialized with SkSurface contents + + example: https://fiddle.skia.org/c/@Surface_makeImageSnapshot + */ + sk_sp makeImageSnapshot(); + + /** + * Like the no-parameter version, this returns an image of the current surface contents. + * This variant takes a rectangle specifying the subset of the surface that is of interest. + * These bounds will be sanitized before being used. + * - If bounds extends beyond the surface, it will be trimmed to just the intersection of + * it and the surface. + * - If bounds does not intersect the surface, then this returns nullptr. + * - If bounds == the surface, then this is the same as calling the no-parameter variant. + + example: https://fiddle.skia.org/c/@Surface_makeImageSnapshot_2 + */ + sk_sp makeImageSnapshot(const SkIRect& bounds); + + /** 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. + + @param canvas SkCanvas drawn into + @param x horizontal offset in SkCanvas + @param y vertical offset in SkCanvas + @param sampling what technique to use when sampling the surface pixels + @param paint SkPaint containing SkBlendMode, SkColorFilter, SkImageFilter, + and so on; or nullptr + + example: https://fiddle.skia.org/c/@Surface_draw + */ + void draw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkSamplingOptions& sampling, + const SkPaint* paint); + + void draw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint = nullptr) { + this->draw(canvas, x, y, SkSamplingOptions(), paint); + } + + /** Copies SkSurface pixel address, row bytes, and SkImageInfo to SkPixmap, if address + is available, and returns true. If pixel address is not available, return + false and leave SkPixmap unchanged. + + pixmap contents become invalid on any future change to SkSurface. + + @param pixmap storage for pixel state if pixels are readable; otherwise, ignored + @return true if SkSurface has direct access to pixels + + example: https://fiddle.skia.org/c/@Surface_peekPixels + */ + bool peekPixels(SkPixmap* pixmap); + + /** Copies SkRect of pixels to dst. + + Source SkRect corners are (srcX, srcY) and SkSurface (width(), height()). + Destination SkRect corners are (0, 0) and (dst.width(), dst.height()). + Copies each readable pixel intersecting both rectangles, without scaling, + converting to dst.colorType() and dst.alphaType() if required. + + Pixels are readable when SkSurface is raster, or backed by a GPU. + + The destination pixel storage must be allocated by the caller. + + Pixel values are converted only if SkColorType and SkAlphaType + do not match. Only pixels within both source and destination rectangles + are copied. dst contents outside SkRect intersection are unchanged. + + Pass negative values for srcX or srcY to offset pixels across or down destination. + + Does not copy, and returns false if: + - Source and destination rectangles do not intersect. + - SkPixmap pixels could not be allocated. + - dst.rowBytes() is too small to contain one row of pixels. + + @param dst storage for pixels copied from SkSurface + @param srcX offset into readable pixels on x-axis; may be negative + @param srcY offset into readable pixels on y-axis; may be negative + @return true if pixels were copied + + example: https://fiddle.skia.org/c/@Surface_readPixels + */ + bool readPixels(const SkPixmap& dst, int srcX, int srcY); + + /** Copies SkRect of pixels from SkCanvas into dstPixels. + + Source SkRect corners are (srcX, srcY) and SkSurface (width(), height()). + Destination SkRect corners are (0, 0) and (dstInfo.width(), dstInfo.height()). + Copies each readable pixel intersecting both rectangles, without scaling, + converting to dstInfo.colorType() and dstInfo.alphaType() if required. + + Pixels are readable when SkSurface is raster, or backed by a GPU. + + The destination pixel storage must be allocated by the caller. + + Pixel values are converted only if SkColorType and SkAlphaType + do not match. Only pixels within both source and destination rectangles + are copied. dstPixels contents outside SkRect intersection are unchanged. + + Pass negative values for srcX or srcY to offset pixels across or down destination. + + Does not copy, and returns false if: + - Source and destination rectangles do not intersect. + - SkSurface pixels could not be converted to dstInfo.colorType() or dstInfo.alphaType(). + - dstRowBytes is too small to contain one row of pixels. + + @param dstInfo width, height, SkColorType, and SkAlphaType of dstPixels + @param dstPixels storage for pixels; dstInfo.height() times dstRowBytes, or larger + @param dstRowBytes size of one destination row; dstInfo.width() times pixel size, or larger + @param srcX offset into readable pixels on x-axis; may be negative + @param srcY offset into readable pixels on y-axis; may be negative + @return true if pixels were copied + */ + bool readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes, + int srcX, int srcY); + + /** Copies SkRect of pixels from SkSurface into bitmap. + + Source SkRect corners are (srcX, srcY) and SkSurface (width(), height()). + Destination SkRect corners are (0, 0) and (bitmap.width(), bitmap.height()). + Copies each readable pixel intersecting both rectangles, without scaling, + converting to bitmap.colorType() and bitmap.alphaType() if required. + + Pixels are readable when SkSurface is raster, or backed by a GPU. + + The destination pixel storage must be allocated by the caller. + + Pixel values are converted only if SkColorType and SkAlphaType + do not match. Only pixels within both source and destination rectangles + are copied. dst contents outside SkRect intersection are unchanged. + + Pass negative values for srcX or srcY to offset pixels across or down destination. + + Does not copy, and returns false if: + - Source and destination rectangles do not intersect. + - SkSurface pixels could not be converted to dst.colorType() or dst.alphaType(). + - dst pixels could not be allocated. + - dst.rowBytes() is too small to contain one row of pixels. + + @param dst storage for pixels copied from SkSurface + @param srcX offset into readable pixels on x-axis; may be negative + @param srcY offset into readable pixels on y-axis; may be negative + @return true if pixels were copied + + example: https://fiddle.skia.org/c/@Surface_readPixels_3 + */ + bool readPixels(const SkBitmap& dst, int srcX, int srcY); + + using AsyncReadResult = SkImage::AsyncReadResult; + + /** Client-provided context that is passed to client-provided ReadPixelsContext. */ + using ReadPixelsContext = void*; + + /** Client-provided callback to asyncRescaleAndReadPixels() or + asyncRescaleAndReadPixelsYUV420() that is called when read result is ready or on failure. + */ + using ReadPixelsCallback = void(ReadPixelsContext, std::unique_ptr); + + /** Controls the gamma that rescaling occurs in for asyncRescaleAndReadPixels() and + asyncRescaleAndReadPixelsYUV420(). + */ + using RescaleGamma = SkImage::RescaleGamma; + using RescaleMode = SkImage::RescaleMode; + + /** Makes surface pixel data available to caller, possibly asynchronously. It can also rescale + the surface pixels. + + Currently asynchronous reads are only supported on the GPU backend and only when the + underlying 3D API supports transfer buffers and CPU/GPU synchronization primitives. In all + other cases this operates synchronously. + + Data is read from the source sub-rectangle, is optionally converted to a linear gamma, is + rescaled to the size indicated by 'info', is then converted to the color space, color type, + and alpha type of 'info'. A 'srcRect' that is not contained by the bounds of the surface + causes failure. + + When the pixel data is ready the caller's ReadPixelsCallback is called with a + AsyncReadResult containing pixel data in the requested color type, alpha type, and color + space. The AsyncReadResult will have count() == 1. Upon failure the callback is called + with nullptr for AsyncReadResult. For a GPU surface this flushes work but a submit must + occur to guarantee a finite time before the callback is called. + + The data is valid for the lifetime of AsyncReadResult with the exception that if the + SkSurface is GPU-backed the data is immediately invalidated if the context is abandoned + or destroyed. + + @param info info of the requested pixels + @param srcRect subrectangle of surface to read + @param rescaleGamma controls whether rescaling is done in the surface's gamma or whether + the source data is transformed to a linear gamma before rescaling. + @param rescaleMode controls the technique of the rescaling + @param callback function to call with result of the read + @param context passed to callback + */ + void asyncRescaleAndReadPixels(const SkImageInfo& info, + const SkIRect& srcRect, + RescaleGamma rescaleGamma, + RescaleMode rescaleMode, + ReadPixelsCallback callback, + ReadPixelsContext context); + + /** + Similar to asyncRescaleAndReadPixels but performs an additional conversion to YUV. The + RGB->YUV conversion is controlled by 'yuvColorSpace'. The YUV data is returned as three + planes ordered y, u, v. The u and v planes are half the width and height of the resized + rectangle. The y, u, and v values are single bytes. Currently this fails if 'dstSize' + width and height are not even. A 'srcRect' that is not contained by the bounds of the + surface causes failure. + + When the pixel data is ready the caller's ReadPixelsCallback is called with a + AsyncReadResult containing the planar data. The AsyncReadResult will have count() == 3. + Upon failure the callback is called with nullptr for AsyncReadResult. For a GPU surface this + flushes work but a submit must occur to guarantee a finite time before the callback is + called. + + The data is valid for the lifetime of AsyncReadResult with the exception that if the + SkSurface is GPU-backed the data is immediately invalidated if the context is abandoned + or destroyed. + + @param yuvColorSpace The transformation from RGB to YUV. Applied to the resized image + after it is converted to dstColorSpace. + @param dstColorSpace The color space to convert the resized image to, after rescaling. + @param srcRect The portion of the surface to rescale and convert to YUV planes. + @param dstSize The size to rescale srcRect to + @param rescaleGamma controls whether rescaling is done in the surface's gamma or whether + the source data is transformed to a linear gamma before rescaling. + @param rescaleMode controls the sampling technique of the rescaling + @param callback function to call with the planar read result + @param context passed to callback + */ + void asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace, + sk_sp dstColorSpace, + const SkIRect& srcRect, + const SkISize& dstSize, + RescaleGamma rescaleGamma, + RescaleMode rescaleMode, + ReadPixelsCallback callback, + ReadPixelsContext context); + + /** + * Identical to asyncRescaleAndReadPixelsYUV420 but a fourth plane is returned in the + * AsyncReadResult passed to 'callback'. The fourth plane contains the alpha chanel at the + * same full resolution as the Y plane. + */ + void asyncRescaleAndReadPixelsYUVA420(SkYUVColorSpace yuvColorSpace, + sk_sp dstColorSpace, + const SkIRect& srcRect, + const SkISize& dstSize, + RescaleGamma rescaleGamma, + RescaleMode rescaleMode, + ReadPixelsCallback callback, + ReadPixelsContext context); + + /** Copies SkRect of pixels from the src SkPixmap to the SkSurface. + + Source SkRect corners are (0, 0) and (src.width(), src.height()). + Destination SkRect corners are (dstX, dstY) and + (dstX + Surface width(), dstY + Surface height()). + + Copies each readable pixel intersecting both rectangles, without scaling, + converting to SkSurface colorType() and SkSurface alphaType() if required. + + @param src storage for pixels to copy to SkSurface + @param dstX x-axis position relative to SkSurface to begin copy; may be negative + @param dstY y-axis position relative to SkSurface to begin copy; may be negative + + example: https://fiddle.skia.org/c/@Surface_writePixels + */ + void writePixels(const SkPixmap& src, int dstX, int dstY); + + /** Copies SkRect of pixels from the src SkBitmap to the SkSurface. + + Source SkRect corners are (0, 0) and (src.width(), src.height()). + Destination SkRect corners are (dstX, dstY) and + (dstX + Surface width(), dstY + Surface height()). + + Copies each readable pixel intersecting both rectangles, without scaling, + converting to SkSurface colorType() and SkSurface alphaType() if required. + + @param src storage for pixels to copy to SkSurface + @param dstX x-axis position relative to SkSurface to begin copy; may be negative + @param dstY y-axis position relative to SkSurface to begin copy; may be negative + + example: https://fiddle.skia.org/c/@Surface_writePixels_2 + */ + void writePixels(const SkBitmap& src, int dstX, int dstY); + + /** Returns SkSurfaceProps for surface. + + @return LCD striping orientation and setting for device independent fonts + */ + const SkSurfaceProps& props() const { return fProps; } + + /** Inserts a list of GPU semaphores that the current GPU-backed API must wait on before + executing any more commands on the GPU for this surface. We only guarantee blocking + transfer and fragment shader work, but may block earlier stages as well depending on the + backend. + If this call returns false, then the GPU back-end will not wait on any passed in + semaphores, and the client will still own the semaphores, regardless of the value of + deleteSemaphoresAfterWait. + + If deleteSemaphoresAfterWait is false then Skia will not delete the semaphores. In this case + it is the client's responsibility to not destroy or attempt to reuse the semaphores until it + knows that Skia has finished waiting on them. This can be done by using finishedProcs + on flush calls. + + @param numSemaphores size of waitSemaphores array + @param waitSemaphores array of semaphore containers + @paramm deleteSemaphoresAfterWait who owns and should delete the semaphores + @return true if GPU is waiting on semaphores + */ + bool wait(int numSemaphores, const GrBackendSemaphore* waitSemaphores, + bool deleteSemaphoresAfterWait = true); + + /** Initializes GrSurfaceCharacterization that can be used to perform GPU back-end + processing in a separate thread. Typically this is used to divide drawing + into multiple tiles. GrDeferredDisplayListRecorder records the drawing commands + for each tile. + + Return true if SkSurface supports characterization. raster surface returns false. + + @param characterization properties for parallel drawing + @return true if supported + + example: https://fiddle.skia.org/c/@Surface_characterize + */ + bool characterize(GrSurfaceCharacterization* characterization) const; + +protected: + SkSurface(int width, int height, const SkSurfaceProps* surfaceProps); + SkSurface(const SkImageInfo& imageInfo, const SkSurfaceProps* surfaceProps); + + // called by subclass if their contents have changed + void dirtyGenerationID() { + fGenerationID = 0; + } + +private: + const SkSurfaceProps fProps; + const int fWidth; + const int fHeight; + uint32_t fGenerationID; + + using INHERITED = SkRefCnt; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkSurfaceProps.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkSurfaceProps.h new file mode 100644 index 0000000000..d8509bf53e --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkSurfaceProps.h @@ -0,0 +1,116 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkSurfaceProps_DEFINED +#define SkSurfaceProps_DEFINED + +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" +#include "include/private/base/SkTo.h" + +/** + * Description of how the LCD strips are arranged for each pixel. If this is unknown, or the + * pixels are meant to be "portable" and/or transformed before showing (e.g. rotated, scaled) + * then use kUnknown_SkPixelGeometry. + */ +enum SkPixelGeometry { + kUnknown_SkPixelGeometry, + kRGB_H_SkPixelGeometry, + kBGR_H_SkPixelGeometry, + kRGB_V_SkPixelGeometry, + kBGR_V_SkPixelGeometry, +}; + +// Returns true iff geo is a known geometry and is RGB. +static inline bool SkPixelGeometryIsRGB(SkPixelGeometry geo) { + return kRGB_H_SkPixelGeometry == geo || kRGB_V_SkPixelGeometry == geo; +} + +// Returns true iff geo is a known geometry and is BGR. +static inline bool SkPixelGeometryIsBGR(SkPixelGeometry geo) { + return kBGR_H_SkPixelGeometry == geo || kBGR_V_SkPixelGeometry == geo; +} + +// Returns true iff geo is a known geometry and is horizontal. +static inline bool SkPixelGeometryIsH(SkPixelGeometry geo) { + return kRGB_H_SkPixelGeometry == geo || kBGR_H_SkPixelGeometry == geo; +} + +// Returns true iff geo is a known geometry and is vertical. +static inline bool SkPixelGeometryIsV(SkPixelGeometry geo) { + return kRGB_V_SkPixelGeometry == geo || kBGR_V_SkPixelGeometry == geo; +} + +/** + * Describes properties and constraints of a given SkSurface. The rendering engine can parse these + * during drawing, and can sometimes optimize its performance (e.g. disabling an expensive + * feature). + */ +class SK_API SkSurfaceProps { +public: + enum Flags { + kDefault_Flag = 0, + kUseDeviceIndependentFonts_Flag = 1 << 0, + // Use internal MSAA to render to non-MSAA GPU surfaces. + kDynamicMSAA_Flag = 1 << 1, + // If set, all rendering will have dithering enabled + // Currently this only impacts GPU backends + kAlwaysDither_Flag = 1 << 2, + }; + + /** No flags, unknown pixel geometry, platform-default contrast/gamma. */ + SkSurfaceProps(); + /** TODO(kschmi): Remove this constructor and replace with the one below. **/ + SkSurfaceProps(uint32_t flags, SkPixelGeometry); + /** Specified pixel geometry, text contrast, and gamma **/ + SkSurfaceProps(uint32_t flags, SkPixelGeometry, SkScalar textContrast, SkScalar textGamma); + + SkSurfaceProps(const SkSurfaceProps&) = default; + SkSurfaceProps& operator=(const SkSurfaceProps&) = default; + + SkSurfaceProps cloneWithPixelGeometry(SkPixelGeometry newPixelGeometry) const { + return SkSurfaceProps(fFlags, newPixelGeometry, fTextContrast, fTextGamma); + } + + static constexpr SkScalar kMaxContrastInclusive = 1; + static constexpr SkScalar kMinContrastInclusive = 0; + static constexpr SkScalar kMaxGammaExclusive = 4; + static constexpr SkScalar kMinGammaInclusive = 0; + + uint32_t flags() const { return fFlags; } + SkPixelGeometry pixelGeometry() const { return fPixelGeometry; } + SkScalar textContrast() const { return fTextContrast; } + SkScalar textGamma() const { return fTextGamma; } + + bool isUseDeviceIndependentFonts() const { + return SkToBool(fFlags & kUseDeviceIndependentFonts_Flag); + } + + bool isAlwaysDither() const { + return SkToBool(fFlags & kAlwaysDither_Flag); + } + + bool operator==(const SkSurfaceProps& that) const { + return fFlags == that.fFlags && fPixelGeometry == that.fPixelGeometry && + fTextContrast == that.fTextContrast && fTextGamma == that.fTextGamma; + } + + bool operator!=(const SkSurfaceProps& that) const { + return !(*this == that); + } + +private: + uint32_t fFlags; + SkPixelGeometry fPixelGeometry; + + // This gamma value is specifically about blending of mask coverage. + // The surface also has a color space, but that applies to the colors. + SkScalar fTextContrast; + SkScalar fTextGamma; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkSwizzle.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkSwizzle.h new file mode 100644 index 0000000000..f7ef658892 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkSwizzle.h @@ -0,0 +1,21 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkSwizzle_DEFINED +#define SkSwizzle_DEFINED + +#include "include/private/base/SkAPI.h" + +#include + +/** + Swizzles byte order of |count| 32-bit pixels, swapping R and B. + (RGBA <-> BGRA) +*/ +SK_API void SkSwapRB(uint32_t* dest, const uint32_t* src, int count); + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkTextBlob.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkTextBlob.h new file mode 100644 index 0000000000..23e2d9bf2a --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkTextBlob.h @@ -0,0 +1,519 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkTextBlob_DEFINED +#define SkTextBlob_DEFINED + +#include "include/core/SkFont.h" +#include "include/core/SkFontTypes.h" +#include "include/core/SkRect.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" +#include "include/private/base/SkDebug.h" +#include "include/private/base/SkTemplates.h" + +#include +#include +#include + +class SkData; +class SkPaint; +class SkTypeface; +struct SkDeserialProcs; +struct SkPoint; +struct SkRSXform; +struct SkSerialProcs; + +namespace sktext { +class GlyphRunList; +} + +/** \class SkTextBlob + SkTextBlob combines multiple text runs into an immutable container. Each text + run consists of glyphs, SkPaint, and position. Only parts of SkPaint related to + fonts and text rendering are used by run. +*/ +class SK_API SkTextBlob final : public SkNVRefCnt { +private: + class RunRecord; + +public: + + /** Returns conservative bounding box. Uses SkPaint associated with each glyph to + determine glyph bounds, and unions all bounds. Returned bounds may be + larger than the bounds of all glyphs in runs. + + @return conservative bounding box + */ + const SkRect& bounds() const { return fBounds; } + + /** Returns a non-zero value unique among all text blobs. + + @return identifier for SkTextBlob + */ + uint32_t uniqueID() const { return fUniqueID; } + + /** Returns the number of intervals that intersect bounds. + bounds describes a pair of lines parallel to the text advance. + The return count is zero or a multiple of two, and is at most twice the number of glyphs in + the the blob. + + Pass nullptr for intervals to determine the size of the interval array. + + Runs within the blob that contain SkRSXform are ignored when computing intercepts. + + @param bounds lower and upper line parallel to the advance + @param intervals returned intersections; may be nullptr + @param paint specifies stroking, SkPathEffect that affects the result; may be nullptr + @return number of intersections; may be zero + */ + int getIntercepts(const SkScalar bounds[2], SkScalar intervals[], + const SkPaint* paint = nullptr) const; + + /** Creates SkTextBlob with a single run. + + font contains attributes used to define the run text. + + When encoding is SkTextEncoding::kUTF8, SkTextEncoding::kUTF16, or + SkTextEncoding::kUTF32, this function uses the default + character-to-glyph mapping from the SkTypeface in font. It does not + perform typeface fallback for characters not found in the SkTypeface. + It does not perform kerning or other complex shaping; glyphs are + positioned based on their default advances. + + @param text character code points or glyphs drawn + @param byteLength byte length of text array + @param font text size, typeface, text scale, and so on, used to draw + @param encoding text encoding used in the text array + @return SkTextBlob constructed from one run + */ + static sk_sp MakeFromText(const void* text, size_t byteLength, const SkFont& font, + SkTextEncoding encoding = SkTextEncoding::kUTF8); + + /** Creates SkTextBlob with a single run. string meaning depends on SkTextEncoding; + by default, string is encoded as UTF-8. + + font contains attributes used to define the run text. + + When encoding is SkTextEncoding::kUTF8, SkTextEncoding::kUTF16, or + SkTextEncoding::kUTF32, this function uses the default + character-to-glyph mapping from the SkTypeface in font. It does not + perform typeface fallback for characters not found in the SkTypeface. + It does not perform kerning or other complex shaping; glyphs are + positioned based on their default advances. + + @param string character code points or glyphs drawn + @param font text size, typeface, text scale, and so on, used to draw + @param encoding text encoding used in the text array + @return SkTextBlob constructed from one run + */ + static sk_sp MakeFromString(const char* string, const SkFont& font, + SkTextEncoding encoding = SkTextEncoding::kUTF8) { + if (!string) { + return nullptr; + } + return MakeFromText(string, strlen(string), font, encoding); + } + + /** Returns a textblob built from a single run of text with x-positions and a single y value. + This is equivalent to using SkTextBlobBuilder and calling allocRunPosH(). + Returns nullptr if byteLength is zero. + + @param text character code points or glyphs drawn (based on encoding) + @param byteLength byte length of text array + @param xpos array of x-positions, must contain values for all of the character points. + @param constY shared y-position for each character point, to be paired with each xpos. + @param font SkFont used for this run + @param encoding specifies the encoding of the text array. + @return new textblob or nullptr + */ + static sk_sp MakeFromPosTextH(const void* text, size_t byteLength, + const SkScalar xpos[], SkScalar constY, const SkFont& font, + SkTextEncoding encoding = SkTextEncoding::kUTF8); + + /** Returns a textblob built from a single run of text with positions. + This is equivalent to using SkTextBlobBuilder and calling allocRunPos(). + Returns nullptr if byteLength is zero. + + @param text character code points or glyphs drawn (based on encoding) + @param byteLength byte length of text array + @param pos array of positions, must contain values for all of the character points. + @param font SkFont used for this run + @param encoding specifies the encoding of the text array. + @return new textblob or nullptr + */ + static sk_sp MakeFromPosText(const void* text, size_t byteLength, + const SkPoint pos[], const SkFont& font, + SkTextEncoding encoding = SkTextEncoding::kUTF8); + + static sk_sp MakeFromRSXform(const void* text, size_t byteLength, + const SkRSXform xform[], const SkFont& font, + SkTextEncoding encoding = SkTextEncoding::kUTF8); + + /** Writes data to allow later reconstruction of SkTextBlob. memory points to storage + to receive the encoded data, and memory_size describes the size of storage. + Returns bytes used if provided storage is large enough to hold all data; + otherwise, returns zero. + + procs.fTypefaceProc permits supplying a custom function to encode SkTypeface. + If procs.fTypefaceProc is nullptr, default encoding is used. procs.fTypefaceCtx + may be used to provide user context to procs.fTypefaceProc; procs.fTypefaceProc + is called with a pointer to SkTypeface and user context. + + @param procs custom serial data encoders; may be nullptr + @param memory storage for data + @param memory_size size of storage + @return bytes written, or zero if required storage is larger than memory_size + + example: https://fiddle.skia.org/c/@TextBlob_serialize + */ + size_t serialize(const SkSerialProcs& procs, void* memory, size_t memory_size) const; + + /** Returns storage containing SkData describing SkTextBlob, using optional custom + encoders. + + procs.fTypefaceProc permits supplying a custom function to encode SkTypeface. + If procs.fTypefaceProc is nullptr, default encoding is used. procs.fTypefaceCtx + may be used to provide user context to procs.fTypefaceProc; procs.fTypefaceProc + is called with a pointer to SkTypeface and user context. + + @param procs custom serial data encoders; may be nullptr + @return storage containing serialized SkTextBlob + + example: https://fiddle.skia.org/c/@TextBlob_serialize_2 + */ + sk_sp serialize(const SkSerialProcs& procs) const; + + /** Recreates SkTextBlob that was serialized into data. Returns constructed SkTextBlob + if successful; otherwise, returns nullptr. Fails if size is smaller than + required data length, or if data does not permit constructing valid SkTextBlob. + + procs.fTypefaceProc permits supplying a custom function to decode SkTypeface. + If procs.fTypefaceProc is nullptr, default decoding is used. procs.fTypefaceCtx + may be used to provide user context to procs.fTypefaceProc; procs.fTypefaceProc + is called with a pointer to SkTypeface data, data byte length, and user context. + + @param data pointer for serial data + @param size size of data + @param procs custom serial data decoders; may be nullptr + @return SkTextBlob constructed from data in memory + */ + static sk_sp Deserialize(const void* data, size_t size, + const SkDeserialProcs& procs); + + class SK_API Iter { + public: + struct Run { + SkTypeface* fTypeface; + int fGlyphCount; + const uint16_t* fGlyphIndices; +#ifdef SK_UNTIL_CRBUG_1187654_IS_FIXED + const uint32_t* fClusterIndex_forTest; + int fUtf8Size_forTest; + const char* fUtf8_forTest; +#endif + }; + + Iter(const SkTextBlob&); + + /** + * Returns true for each "run" inside the textblob, setting the Run fields (if not null). + * If this returns false, there are no more runs, and the Run parameter will be ignored. + */ + bool next(Run*); + + // Experimental, DO NO USE, will change/go-away + struct ExperimentalRun { + SkFont font; + int count; + const uint16_t* glyphs; + const SkPoint* positions; + }; + bool experimentalNext(ExperimentalRun*); + + private: + const RunRecord* fRunRecord; + }; + +private: + friend class SkNVRefCnt; + + enum GlyphPositioning : uint8_t; + + explicit SkTextBlob(const SkRect& bounds); + + ~SkTextBlob(); + + // Memory for objects of this class is created with sk_malloc rather than operator new and must + // be freed with sk_free. + void operator delete(void* p); + void* operator new(size_t); + void* operator new(size_t, void* p); + + static unsigned ScalarsPerGlyph(GlyphPositioning pos); + + using PurgeDelegate = void (*)(uint32_t blobID, uint32_t cacheID); + + // Call when this blob is part of the key to a cache entry. This allows the cache + // to know automatically those entries can be purged when this SkTextBlob is deleted. + void notifyAddedToCache(uint32_t cacheID, PurgeDelegate purgeDelegate) const { + fCacheID.store(cacheID); + fPurgeDelegate.store(purgeDelegate); + } + + friend class sktext::GlyphRunList; + friend class SkTextBlobBuilder; + friend class SkTextBlobPriv; + friend class SkTextBlobRunIterator; + + const SkRect fBounds; + const uint32_t fUniqueID; + mutable std::atomic fCacheID; + mutable std::atomic fPurgeDelegate; + + SkDEBUGCODE(size_t fStorageSize;) + + // The actual payload resides in externally-managed storage, following the object. + // (see the .cpp for more details) + + using INHERITED = SkRefCnt; +}; + +/** \class SkTextBlobBuilder + Helper class for constructing SkTextBlob. +*/ +class SK_API SkTextBlobBuilder { +public: + + /** Constructs empty SkTextBlobBuilder. By default, SkTextBlobBuilder has no runs. + + @return empty SkTextBlobBuilder + + example: https://fiddle.skia.org/c/@TextBlobBuilder_empty_constructor + */ + SkTextBlobBuilder(); + + /** Deletes data allocated internally by SkTextBlobBuilder. + */ + ~SkTextBlobBuilder(); + + /** Returns SkTextBlob built from runs of glyphs added by builder. Returned + SkTextBlob is immutable; it may be copied, but its contents may not be altered. + Returns nullptr if no runs of glyphs were added by builder. + + Resets SkTextBlobBuilder to its initial empty state, allowing it to be + reused to build a new set of runs. + + @return SkTextBlob or nullptr + + example: https://fiddle.skia.org/c/@TextBlobBuilder_make + */ + sk_sp make(); + + /** \struct SkTextBlobBuilder::RunBuffer + RunBuffer supplies storage for glyphs and positions within a run. + + A run is a sequence of glyphs sharing font metrics and positioning. + Each run may position its glyphs in one of three ways: + by specifying where the first glyph is drawn, and allowing font metrics to + determine the advance to subsequent glyphs; by specifying a baseline, and + the position on that baseline for each glyph in run; or by providing SkPoint + array, one per glyph. + */ + struct RunBuffer { + SkGlyphID* glyphs; //!< storage for glyph indexes in run + SkScalar* pos; //!< storage for glyph positions in run + char* utf8text; //!< storage for text UTF-8 code units in run + uint32_t* clusters; //!< storage for glyph clusters (index of UTF-8 code unit) + + // Helpers, since the "pos" field can be different types (always some number of floats). + SkPoint* points() const { return reinterpret_cast(pos); } + SkRSXform* xforms() const { return reinterpret_cast(pos); } + }; + + /** Returns run with storage for glyphs. Caller must write count glyphs to + RunBuffer::glyphs before next call to SkTextBlobBuilder. + + RunBuffer::pos, RunBuffer::utf8text, and RunBuffer::clusters should be ignored. + + Glyphs share metrics in font. + + Glyphs are positioned on a baseline at (x, y), using font metrics to + determine their relative placement. + + bounds defines an optional bounding box, used to suppress drawing when SkTextBlob + bounds does not intersect SkSurface bounds. If bounds is nullptr, SkTextBlob bounds + is computed from (x, y) and RunBuffer::glyphs metrics. + + @param font SkFont used for this run + @param count number of glyphs + @param x horizontal offset within the blob + @param y vertical offset within the blob + @param bounds optional run bounding box + @return writable glyph buffer + */ + const RunBuffer& allocRun(const SkFont& font, int count, SkScalar x, SkScalar y, + const SkRect* bounds = nullptr); + + /** Returns run with storage for glyphs and positions along baseline. Caller must + write count glyphs to RunBuffer::glyphs and count scalars to RunBuffer::pos + before next call to SkTextBlobBuilder. + + RunBuffer::utf8text and RunBuffer::clusters should be ignored. + + Glyphs share metrics in font. + + Glyphs are positioned on a baseline at y, using x-axis positions written by + caller to RunBuffer::pos. + + bounds defines an optional bounding box, used to suppress drawing when SkTextBlob + bounds does not intersect SkSurface bounds. If bounds is nullptr, SkTextBlob bounds + is computed from y, RunBuffer::pos, and RunBuffer::glyphs metrics. + + @param font SkFont used for this run + @param count number of glyphs + @param y vertical offset within the blob + @param bounds optional run bounding box + @return writable glyph buffer and x-axis position buffer + */ + const RunBuffer& allocRunPosH(const SkFont& font, int count, SkScalar y, + const SkRect* bounds = nullptr); + + /** Returns run with storage for glyphs and SkPoint positions. Caller must + write count glyphs to RunBuffer::glyphs and count SkPoint to RunBuffer::pos + before next call to SkTextBlobBuilder. + + RunBuffer::utf8text and RunBuffer::clusters should be ignored. + + Glyphs share metrics in font. + + Glyphs are positioned using SkPoint written by caller to RunBuffer::pos, using + two scalar values for each SkPoint. + + bounds defines an optional bounding box, used to suppress drawing when SkTextBlob + bounds does not intersect SkSurface bounds. If bounds is nullptr, SkTextBlob bounds + is computed from RunBuffer::pos, and RunBuffer::glyphs metrics. + + @param font SkFont used for this run + @param count number of glyphs + @param bounds optional run bounding box + @return writable glyph buffer and SkPoint buffer + */ + const RunBuffer& allocRunPos(const SkFont& font, int count, + const SkRect* bounds = nullptr); + + // RunBuffer.pos points to SkRSXform array + const RunBuffer& allocRunRSXform(const SkFont& font, int count); + + /** Returns run with storage for glyphs, text, and clusters. Caller must + write count glyphs to RunBuffer::glyphs, textByteCount UTF-8 code units + into RunBuffer::utf8text, and count monotonic indexes into utf8text + into RunBuffer::clusters before next call to SkTextBlobBuilder. + + RunBuffer::pos should be ignored. + + Glyphs share metrics in font. + + Glyphs are positioned on a baseline at (x, y), using font metrics to + determine their relative placement. + + bounds defines an optional bounding box, used to suppress drawing when SkTextBlob + bounds does not intersect SkSurface bounds. If bounds is nullptr, SkTextBlob bounds + is computed from (x, y) and RunBuffer::glyphs metrics. + + @param font SkFont used for this run + @param count number of glyphs + @param x horizontal offset within the blob + @param y vertical offset within the blob + @param textByteCount number of UTF-8 code units + @param bounds optional run bounding box + @return writable glyph buffer, text buffer, and cluster buffer + */ + const RunBuffer& allocRunText(const SkFont& font, int count, SkScalar x, SkScalar y, + int textByteCount, const SkRect* bounds = nullptr); + + /** Returns run with storage for glyphs, positions along baseline, text, + and clusters. Caller must write count glyphs to RunBuffer::glyphs, + count scalars to RunBuffer::pos, textByteCount UTF-8 code units into + RunBuffer::utf8text, and count monotonic indexes into utf8text into + RunBuffer::clusters before next call to SkTextBlobBuilder. + + Glyphs share metrics in font. + + Glyphs are positioned on a baseline at y, using x-axis positions written by + caller to RunBuffer::pos. + + bounds defines an optional bounding box, used to suppress drawing when SkTextBlob + bounds does not intersect SkSurface bounds. If bounds is nullptr, SkTextBlob bounds + is computed from y, RunBuffer::pos, and RunBuffer::glyphs metrics. + + @param font SkFont used for this run + @param count number of glyphs + @param y vertical offset within the blob + @param textByteCount number of UTF-8 code units + @param bounds optional run bounding box + @return writable glyph buffer, x-axis position buffer, text buffer, and cluster buffer + */ + const RunBuffer& allocRunTextPosH(const SkFont& font, int count, SkScalar y, int textByteCount, + const SkRect* bounds = nullptr); + + /** Returns run with storage for glyphs, SkPoint positions, text, and + clusters. Caller must write count glyphs to RunBuffer::glyphs, count + SkPoint to RunBuffer::pos, textByteCount UTF-8 code units into + RunBuffer::utf8text, and count monotonic indexes into utf8text into + RunBuffer::clusters before next call to SkTextBlobBuilder. + + Glyphs share metrics in font. + + Glyphs are positioned using SkPoint written by caller to RunBuffer::pos, using + two scalar values for each SkPoint. + + bounds defines an optional bounding box, used to suppress drawing when SkTextBlob + bounds does not intersect SkSurface bounds. If bounds is nullptr, SkTextBlob bounds + is computed from RunBuffer::pos, and RunBuffer::glyphs metrics. + + @param font SkFont used for this run + @param count number of glyphs + @param textByteCount number of UTF-8 code units + @param bounds optional run bounding box + @return writable glyph buffer, SkPoint buffer, text buffer, and cluster buffer + */ + const RunBuffer& allocRunTextPos(const SkFont& font, int count, int textByteCount, + const SkRect* bounds = nullptr); + + // RunBuffer.pos points to SkRSXform array + const RunBuffer& allocRunTextRSXform(const SkFont& font, int count, int textByteCount, + const SkRect* bounds = nullptr); + +private: + void reserve(size_t size); + void allocInternal(const SkFont& font, SkTextBlob::GlyphPositioning positioning, + int count, int textBytes, SkPoint offset, const SkRect* bounds); + bool mergeRun(const SkFont& font, SkTextBlob::GlyphPositioning positioning, + uint32_t count, SkPoint offset); + void updateDeferredBounds(); + + static SkRect ConservativeRunBounds(const SkTextBlob::RunRecord&); + static SkRect TightRunBounds(const SkTextBlob::RunRecord&); + + friend class SkTextBlobPriv; + friend class SkTextBlobBuilderPriv; + + skia_private::AutoTMalloc fStorage; + size_t fStorageSize; + size_t fStorageUsed; + + SkRect fBounds; + int fRunCount; + bool fDeferredBounds; + size_t fLastRun; // index into fStorage + + RunBuffer fCurrentRunBuffer; +}; + +#endif // SkTextBlob_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkTextureCompressionType.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkTextureCompressionType.h new file mode 100644 index 0000000000..e9b441378d --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkTextureCompressionType.h @@ -0,0 +1,30 @@ +/* + * Copyright 2023 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkTextureCompressionType_DEFINED +#define SkTextureCompressionType_DEFINED +/* + * Skia | GL_COMPRESSED_* | MTLPixelFormat* | VK_FORMAT_*_BLOCK + * -------------------------------------------------------------------------------------- + * kETC2_RGB8_UNORM | ETC1_RGB8 | ETC2_RGB8 (iOS-only) | ETC2_R8G8B8_UNORM + * | RGB8_ETC2 | | + * -------------------------------------------------------------------------------------- + * kBC1_RGB8_UNORM | RGB_S3TC_DXT1_EXT | N/A | BC1_RGB_UNORM + * -------------------------------------------------------------------------------------- + * kBC1_RGBA8_UNORM | RGBA_S3TC_DXT1_EXT | BC1_RGBA (macOS-only)| BC1_RGBA_UNORM + */ +enum class SkTextureCompressionType { + kNone, + kETC2_RGB8_UNORM, + + kBC1_RGB8_UNORM, + kBC1_RGBA8_UNORM, + kLast = kBC1_RGBA8_UNORM, + kETC1_RGB8 = kETC2_RGB8_UNORM, +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkTileMode.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkTileMode.h new file mode 100644 index 0000000000..8a9d020958 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkTileMode.h @@ -0,0 +1,41 @@ +/* + * Copyright 2019 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkTileModes_DEFINED +#define SkTileModes_DEFINED + +#include "include/core/SkTypes.h" + +enum class SkTileMode { + /** + * Replicate the edge color if the shader draws outside of its + * original bounds. + */ + kClamp, + + /** + * Repeat the shader's image horizontally and vertically. + */ + kRepeat, + + /** + * Repeat the shader's image horizontally and vertically, alternating + * mirror images so that adjacent images always seam. + */ + kMirror, + + /** + * Only draw within the original domain, return transparent-black everywhere else. + */ + kDecal, + + kLastTileMode = kDecal, +}; + +static constexpr int kSkTileModeCount = static_cast(SkTileMode::kLastTileMode) + 1; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkTiledImageUtils.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkTiledImageUtils.h new file mode 100644 index 0000000000..fc5a4f25c5 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkTiledImageUtils.h @@ -0,0 +1,125 @@ +/* + * Copyright 2023 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkTiledImageUtils_DEFINED +#define SkTiledImageUtils_DEFINED + +#include "include/core/SkCanvas.h" +#include "include/core/SkImage.h" +#include "include/core/SkRect.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkSamplingOptions.h" +#include "include/core/SkScalar.h" +#include "include/private/base/SkAPI.h" + +#include + +class SkPaint; + +/** \namespace SkTiledImageUtils + SkTiledImageUtils' DrawImage/DrawImageRect methods are intended to be direct replacements + for their SkCanvas equivalents. The SkTiledImageUtils calls will break SkBitmap-backed + SkImages into smaller tiles and draw them if the original image is too large to be + uploaded to the GPU. If the original image doesn't need tiling or is already gpu-backed + the DrawImage/DrawImageRect calls will fall through to the matching SkCanvas call. +*/ +namespace SkTiledImageUtils { + +SK_API void DrawImageRect(SkCanvas* canvas, + const SkImage* image, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions& sampling = {}, + const SkPaint* paint = nullptr, + SkCanvas::SrcRectConstraint constraint = + SkCanvas::kFast_SrcRectConstraint); + +inline void DrawImageRect(SkCanvas* canvas, + const sk_sp& image, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions& sampling = {}, + const SkPaint* paint = nullptr, + SkCanvas::SrcRectConstraint constraint = + SkCanvas::kFast_SrcRectConstraint) { + DrawImageRect(canvas, image.get(), src, dst, sampling, paint, constraint); +} + +inline void DrawImageRect(SkCanvas* canvas, + const SkImage* image, + const SkRect& dst, + const SkSamplingOptions& sampling = {}, + const SkPaint* paint = nullptr, + SkCanvas::SrcRectConstraint constraint = + SkCanvas::kFast_SrcRectConstraint) { + if (!image) { + return; + } + + SkRect src = SkRect::MakeIWH(image->width(), image->height()); + + DrawImageRect(canvas, image, src, dst, sampling, paint, constraint); +} + +inline void DrawImageRect(SkCanvas* canvas, + const sk_sp& image, + const SkRect& dst, + const SkSamplingOptions& sampling = {}, + const SkPaint* paint = nullptr, + SkCanvas::SrcRectConstraint constraint = + SkCanvas::kFast_SrcRectConstraint) { + DrawImageRect(canvas, image.get(), dst, sampling, paint, constraint); +} + +inline void DrawImage(SkCanvas* canvas, + const SkImage* image, + SkScalar x, SkScalar y, + const SkSamplingOptions& sampling = {}, + const SkPaint* paint = nullptr, + SkCanvas::SrcRectConstraint constraint = + SkCanvas::kFast_SrcRectConstraint) { + if (!image) { + return; + } + + SkRect src = SkRect::MakeIWH(image->width(), image->height()); + SkRect dst = SkRect::MakeXYWH(x, y, image->width(), image->height()); + + DrawImageRect(canvas, image, src, dst, sampling, paint, constraint); +} + +inline void DrawImage(SkCanvas* canvas, + const sk_sp& image, + SkScalar x, SkScalar y, + const SkSamplingOptions& sampling = {}, + const SkPaint* paint = nullptr, + SkCanvas::SrcRectConstraint constraint = + SkCanvas::kFast_SrcRectConstraint) { + DrawImage(canvas, image.get(), x, y, sampling, paint, constraint); +} + +static constexpr int kNumImageKeyValues = 6; + +/** Retrieves a set of values that can be used as part of a cache key for the provided image. + + Unfortunately, SkImage::uniqueID isn't sufficient as an SkImage cache key. In particular, + SkBitmap-backed SkImages can share a single SkBitmap and refer to different subsets of it. + In this situation the optimal key is based on the SkBitmap's generation ID and the subset + rectangle. + For Picture-backed images this method will attempt to generate a concise internally-based + key (i.e., containing picture ID, matrix translation, width and height, etc.). For complicated + Picture-backed images (i.e., those w/ a paint or a full matrix) it will fall back to + using 'image's unique key. + + @param image The image for which key values are desired + @param keyValues The resulting key values +*/ +SK_API void GetImageKeyValues(const SkImage* image, uint32_t keyValues[kNumImageKeyValues]); + +} // namespace SkTiledImageUtils + +#endif // SkTiledImageUtils_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkTraceMemoryDump.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkTraceMemoryDump.h new file mode 100644 index 0000000000..d01b01bd68 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkTraceMemoryDump.h @@ -0,0 +1,114 @@ +/* + * 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 SkTraceMemoryDump_DEFINED +#define SkTraceMemoryDump_DEFINED + +#include "include/core/SkTypes.h" + +class SkDiscardableMemory; + +/** + * Interface for memory tracing. + * This interface is meant to be passed as argument to the memory dump methods of Skia objects. + * The implementation of this interface is provided by the embedder. + */ +class SK_API SkTraceMemoryDump { +public: + /** + * Enum to specify the level of the requested details for the dump from the Skia objects. + */ + enum LevelOfDetail { + // Dump only the minimal details to get the total memory usage (Usually just the totals). + kLight_LevelOfDetail, + + // Dump the detailed breakdown of the objects in the caches. + kObjectsBreakdowns_LevelOfDetail + }; + + /** + * Appends a new memory dump (i.e. a row) to the trace memory infrastructure. + * If dumpName does not exist yet, a new one is created. Otherwise, a new column is appended to + * the previously created dump. + * Arguments: + * dumpName: an absolute, slash-separated, name for the item being dumped + * e.g., "skia/CacheX/EntryY". + * valueName: a string indicating the name of the column. + * e.g., "size", "active_size", "number_of_objects". + * This string is supposed to be long lived and is NOT copied. + * units: a string indicating the units for the value. + * e.g., "bytes", "objects". + * This string is supposed to be long lived and is NOT copied. + * value: the actual value being dumped. + */ + virtual void dumpNumericValue(const char* dumpName, + const char* valueName, + const char* units, + uint64_t value) = 0; + + virtual void dumpStringValue(const char* /*dumpName*/, + const char* /*valueName*/, + const char* /*value*/) { } + + /** + * Sets the memory backing for an existing dump. + * backingType and backingObjectId are used by the embedder to associate the memory dumped via + * dumpNumericValue with the corresponding dump that backs the memory. + */ + virtual void setMemoryBacking(const char* dumpName, + const char* backingType, + const char* backingObjectId) = 0; + + /** + * Specialization for memory backed by discardable memory. + */ + virtual void setDiscardableMemoryBacking( + const char* dumpName, + const SkDiscardableMemory& discardableMemoryObject) = 0; + + /** + * Returns the type of details requested in the dump. The granularity of the dump is supposed to + * match the LevelOfDetail argument. The level of detail must not affect the total size + * reported, but only granularity of the child entries. + */ + virtual LevelOfDetail getRequestedDetails() const = 0; + + /** + * Returns true if we should dump wrapped objects. Wrapped objects come from outside Skia, and + * may be independently tracked there. + */ + virtual bool shouldDumpWrappedObjects() const { return true; } + + /** + * If shouldDumpWrappedObjects() returns true then this function will be called to populate + * the output with information on whether the item being dumped is a wrapped object. + */ + virtual void dumpWrappedState(const char* /*dumpName*/, bool /*isWrappedObject*/) {} + + /** + * Returns true if we should dump unbudgeted objects. Unbudgeted objects can either come from + * wrapped objects passed into Skia from the client or from Skia created objects currently held + * by the client in a public Skia object (e.g. SkSurface or SkImage). This call is only used + * when dumping Graphite memory statistics. + */ + virtual bool shouldDumpUnbudgetedObjects() const { return true; } + + /** + * If shouldDumpUnbudgetedObjects() returns true then this function will be called to populate + * the output with information on whether the item being dumped is budgeted. This call is only + * used when dumping Graphite memory statistics. + */ + virtual void dumpBudgetedState(const char* /*dumpName*/, bool /*isBudgeted*/) {} + +protected: + virtual ~SkTraceMemoryDump() = default; + SkTraceMemoryDump() = default; + SkTraceMemoryDump(const SkTraceMemoryDump&) = delete; + SkTraceMemoryDump& operator=(const SkTraceMemoryDump&) = delete; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkTypeface.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkTypeface.h new file mode 100644 index 0000000000..d35396a9f2 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkTypeface.h @@ -0,0 +1,434 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkTypeface_DEFINED +#define SkTypeface_DEFINED + +#include "include/core/SkFontArguments.h" +#include "include/core/SkFontParameters.h" +#include "include/core/SkFontStyle.h" +#include "include/core/SkRect.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkString.h" +#include "include/core/SkTypes.h" +#include "include/private/SkWeakRefCnt.h" +#include "include/private/base/SkOnce.h" + +#include +#include +#include + +class SkData; +class SkDescriptor; +class SkFontMgr; +class SkFontDescriptor; +class SkScalerContext; +class SkStream; +class SkStreamAsset; +class SkWStream; +enum class SkTextEncoding; +struct SkAdvancedTypefaceMetrics; +struct SkScalerContextEffects; +struct SkScalerContextRec; + +using SkTypefaceID = uint32_t; + +/** Machine endian. */ +typedef uint32_t SkFontTableTag; + +/** \class SkTypeface + + The SkTypeface class specifies the typeface and intrinsic style of a font. + This is used in the paint, along with optionally algorithmic settings like + textSize, textSkewX, textScaleX, kFakeBoldText_Mask, to specify + how text appears when drawn (and measured). + + Typeface objects are immutable, and so they can be shared between threads. +*/ +class SK_API SkTypeface : public SkWeakRefCnt { +public: + /** Returns the typeface's intrinsic style attributes. */ + SkFontStyle fontStyle() const { + return fStyle; + } + + /** Returns true if style() has the kBold bit set. */ + bool isBold() const { return fStyle.weight() >= SkFontStyle::kSemiBold_Weight; } + + /** Returns true if style() has the kItalic bit set. */ + bool isItalic() const { return fStyle.slant() != SkFontStyle::kUpright_Slant; } + + /** 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; } + + /** Copy into 'coordinates' (allocated by the caller) the design variation coordinates. + * + * @param coordinates the buffer into which to write the design variation coordinates. + * @param coordinateCount the number of entries available through 'coordinates'. + * + * @return The number of axes, or -1 if there is an error. + * If 'coordinates != nullptr' and 'coordinateCount >= numAxes' then 'coordinates' will be + * filled with the variation coordinates describing the position of this typeface in design + * variation space. It is possible the number of axes can be retrieved but actual position + * cannot. + */ + int getVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const; + + /** Copy into 'parameters' (allocated by the caller) the design variation parameters. + * + * @param parameters the buffer into which to write the design variation parameters. + * @param coordinateCount the number of entries available through 'parameters'. + * + * @return The number of axes, or -1 if there is an error. + * If 'parameters != nullptr' and 'parameterCount >= numAxes' then 'parameters' will be + * filled with the variation parameters describing the position of this typeface in design + * variation space. It is possible the number of axes can be retrieved but actual parameters + * cannot. + */ + int getVariationDesignParameters(SkFontParameters::Variation::Axis parameters[], + int parameterCount) const; + + /** Return a 32bit value for this typeface, unique for the underlying font + data. Will never return 0. + */ + SkTypefaceID uniqueID() const { return fUniqueID; } + + /** Returns true if the two typefaces reference the same underlying font, + handling either being null (treating null as not equal to any font). + */ + static bool Equal(const SkTypeface* facea, const SkTypeface* faceb); + + /** Returns a non-null typeface which contains no glyphs. */ + static sk_sp MakeEmpty(); + + /** Return a new typeface based on this typeface but parameterized as specified in the + SkFontArguments. If the SkFontArguments does not supply an argument for a parameter + in the font then the value from this typeface will be used as the value for that + argument. If the cloned typeface would be exaclty the same as this typeface then + this typeface may be ref'ed and returned. May return nullptr on failure. + */ + sk_sp makeClone(const SkFontArguments&) const; + + /** + * A typeface can serialize just a descriptor (names, etc.), or it can also include the + * actual font data (which can be large). This enum controls how serialize() decides what + * to serialize. + */ + enum class SerializeBehavior { + kDoIncludeData, + kDontIncludeData, + kIncludeDataIfLocal, + }; + + /** Write a unique signature to a stream, sufficient to reconstruct a + typeface referencing the same font when Deserialize is called. + */ + void serialize(SkWStream*, SerializeBehavior = SerializeBehavior::kIncludeDataIfLocal) const; + + /** + * Same as serialize(SkWStream*, ...) but returns the serialized data in SkData, instead of + * writing it to a stream. + */ + sk_sp serialize(SerializeBehavior = SerializeBehavior::kIncludeDataIfLocal) const; + + /** Given the data previously written by serialize(), return a new instance + of a typeface referring to the same font. If that font is not available, + return nullptr. + Goes through all registered typeface factories and lastResortMgr (if non-null). + Does not affect ownership of SkStream. + */ + + static sk_sp MakeDeserialize(SkStream*, sk_sp lastResortMgr); + + /** + * Given an array of UTF32 character codes, return their corresponding glyph IDs. + * + * @param chars pointer to the array of UTF32 chars + * @param number of chars and glyphs + * @param glyphs returns the corresponding glyph IDs for each character. + */ + void unicharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const; + + int textToGlyphs(const void* text, size_t byteLength, SkTextEncoding encoding, + SkGlyphID glyphs[], int maxGlyphCount) const; + + /** + * Return the glyphID that corresponds to the specified unicode code-point + * (in UTF32 encoding). If the unichar is not supported, returns 0. + * + * This is a short-cut for calling unicharsToGlyphs(). + */ + SkGlyphID unicharToGlyph(SkUnichar unichar) const; + + /** + * Return the number of glyphs in the typeface. + */ + int countGlyphs() const; + + // Table getters -- may fail if the underlying font format is not organized + // as 4-byte tables. + + /** Return the number of tables in the font. */ + int countTables() const; + + /** Copy into tags[] (allocated by the caller) the list of table tags in + * the font, and return the number. This will be the same as CountTables() + * or 0 if an error occured. If tags == NULL, this only returns the count + * (the same as calling countTables()). + */ + int getTableTags(SkFontTableTag tags[]) const; + + /** Given a table tag, return the size of its contents, or 0 if not present + */ + size_t getTableSize(SkFontTableTag) const; + + /** Copy the contents of a table into data (allocated by the caller). Note + * that the contents of the table will be in their native endian order + * (which for most truetype tables is big endian). If the table tag is + * not found, or there is an error copying the data, then 0 is returned. + * If this happens, it is possible that some or all of the memory pointed + * to by data may have been written to, even though an error has occured. + * + * @param tag The table tag whose contents are to be copied + * @param offset The offset in bytes into the table's contents where the + * copy should start from. + * @param length The number of bytes, starting at offset, of table data + * to copy. + * @param data storage address where the table contents are copied to + * @return the number of bytes actually copied into data. If offset+length + * exceeds the table's size, then only the bytes up to the table's + * size are actually copied, and this is the value returned. If + * offset > the table's size, or tag is not a valid table, + * then 0 is returned. + */ + size_t getTableData(SkFontTableTag tag, size_t offset, size_t length, + void* data) const; + + /** + * Return an immutable copy of the requested font table, or nullptr if that table was + * not found. This can sometimes be faster than calling getTableData() twice: once to find + * the length, and then again to copy the data. + * + * @param tag The table tag whose contents are to be copied + * @return an immutable copy of the table's data, or nullptr. + */ + sk_sp copyTableData(SkFontTableTag tag) const; + + /** + * Return the units-per-em value for this typeface, or zero if there is an + * error. + */ + int getUnitsPerEm() const; + + /** + * Given a run of glyphs, return the associated horizontal adjustments. + * Adjustments are in "design units", which are integers relative to the + * typeface's units per em (see getUnitsPerEm). + * + * Some typefaces are known to never support kerning. Calling this method + * with all zeros (e.g. getKerningPairAdustments(NULL, 0, NULL)) returns + * a boolean indicating if the typeface might support kerning. If it + * returns false, then it will always return false (no kerning) for all + * possible glyph runs. If it returns true, then it *may* return true for + * somne glyph runs. + * + * If count is non-zero, then the glyphs parameter must point to at least + * [count] valid glyph IDs, and the adjustments parameter must be + * sized to at least [count - 1] entries. If the method returns true, then + * [count-1] entries in the adjustments array will be set. If the method + * returns false, then no kerning should be applied, and the adjustments + * array will be in an undefined state (possibly some values may have been + * written, but none of them should be interpreted as valid values). + */ + bool getKerningPairAdjustments(const SkGlyphID glyphs[], int count, + int32_t adjustments[]) const; + + struct LocalizedString { + SkString fString; + SkString fLanguage; + }; + class LocalizedStrings { + public: + LocalizedStrings() = default; + virtual ~LocalizedStrings() { } + virtual bool next(LocalizedString* localizedString) = 0; + void unref() { delete this; } + + private: + LocalizedStrings(const LocalizedStrings&) = delete; + LocalizedStrings& operator=(const LocalizedStrings&) = delete; + }; + /** + * Returns an iterator which will attempt to enumerate all of the + * family names specified by the font. + * It is the caller's responsibility to unref() the returned pointer. + */ + LocalizedStrings* createFamilyNameIterator() const; + + /** + * Return the family name for this typeface. It will always be returned + * encoded as UTF8, but the language of the name is whatever the host + * platform chooses. + */ + void getFamilyName(SkString* name) const; + + /** + * Return the PostScript name for this typeface. + * Value may change based on variation parameters. + * Returns false if no PostScript name is available. + */ + bool getPostScriptName(SkString* name) 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 + * of this typeface within the stream, or 0 if the stream is not a + * collection. + * The caller is responsible for deleting the stream. + */ + std::unique_ptr openStream(int* ttcIndex) const; + + /** + * Return a stream for the contents of the font data. + * Returns nullptr on failure or if the font data isn't already available in stream form. + * Use when the stream can be used opportunistically but the calling code would prefer + * to fall back to table access if creating the stream would be expensive. + * Otherwise acts the same as openStream. + */ + std::unique_ptr openExistingStream(int* ttcIndex) const; + + /** + * Return a scalercontext for the given descriptor. It may return a + * stub scalercontext that will not crash, but will draw nothing. + */ + std::unique_ptr createScalerContext(const SkScalerContextEffects&, + const SkDescriptor*) const; + + /** + * Return a rectangle (scaled to 1-pt) that represents the union of the bounds of all + * of the glyphs, but each one positioned at (0,). This may be conservatively large, and + * will not take into account any hinting or other size-specific adjustments. + */ + SkRect getBounds() const; + + // PRIVATE / EXPERIMENTAL -- do not call + void filterRec(SkScalerContextRec* rec) const { + this->onFilterRec(rec); + } + // PRIVATE / EXPERIMENTAL -- do not call + void getFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const { + this->onGetFontDescriptor(desc, isLocal); + } + // PRIVATE / EXPERIMENTAL -- do not call + void* internal_private_getCTFontRef() const { + return this->onGetCTFontRef(); + } + + /* Skia reserves all tags that begin with a lower case letter and 0 */ + using FactoryId = SkFourByteTag; + static void Register( + FactoryId id, + sk_sp (*make)(std::unique_ptr, const SkFontArguments&)); + +protected: + explicit SkTypeface(const SkFontStyle& style, bool isFixedPitch = false); + ~SkTypeface() override; + + virtual sk_sp onMakeClone(const SkFontArguments&) const = 0; + + /** Sets the fixedPitch bit. If used, must be called in the constructor. */ + void setIsFixedPitch(bool isFixedPitch) { fIsFixedPitch = isFixedPitch; } + /** Sets the font style. If used, must be called in the constructor. */ + void setFontStyle(SkFontStyle style) { fStyle = style; } + + // Must return a valid scaler context. It can not return nullptr. + virtual std::unique_ptr onCreateScalerContext(const SkScalerContextEffects&, + const SkDescriptor*) const = 0; + virtual void onFilterRec(SkScalerContextRec*) const = 0; + friend class SkScalerContext; // onFilterRec + + // Subclasses *must* override this method to work with the PDF backend. + virtual std::unique_ptr onGetAdvancedMetrics() const = 0; + // For type1 postscript fonts only, set the glyph names for each glyph. + // destination array is non-null, and points to an array of size this->countGlyphs(). + // Backends that do not suport type1 fonts should not override. + virtual void getPostScriptGlyphNames(SkString*) const = 0; + + // The mapping from glyph to Unicode; array indices are glyph ids. + // For each glyph, give the default Unicode value, if it exists. + // dstArray is non-null, and points to an array of size this->countGlyphs(). + virtual void getGlyphToUnicodeMap(SkUnichar* dstArray) const = 0; + + virtual std::unique_ptr onOpenStream(int* ttcIndex) const = 0; + + virtual std::unique_ptr onOpenExistingStream(int* ttcIndex) const; + + virtual bool onGlyphMaskNeedsCurrentColor() const = 0; + + virtual int onGetVariationDesignPosition( + SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const = 0; + + virtual int onGetVariationDesignParameters( + SkFontParameters::Variation::Axis parameters[], int parameterCount) const = 0; + + virtual void onGetFontDescriptor(SkFontDescriptor*, bool* isLocal) const = 0; + + virtual void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const = 0; + virtual int onCountGlyphs() const = 0; + + virtual int onGetUPEM() const = 0; + virtual bool onGetKerningPairAdjustments(const SkGlyphID glyphs[], int count, + int32_t adjustments[]) const; + + /** Returns the family name of the typeface as known by its font manager. + * This name may or may not be produced by the family name iterator. + */ + virtual void onGetFamilyName(SkString* familyName) const = 0; + virtual bool onGetPostScriptName(SkString*) const = 0; + + /** Returns an iterator over the family names in the font. */ + virtual LocalizedStrings* onCreateFamilyNameIterator() const = 0; + + virtual int onGetTableTags(SkFontTableTag tags[]) const = 0; + virtual size_t onGetTableData(SkFontTableTag, size_t offset, + size_t length, void* data) const = 0; + virtual sk_sp onCopyTableData(SkFontTableTag) const; + + virtual bool onComputeBounds(SkRect*) const; + + virtual void* onGetCTFontRef() const { return nullptr; } + +private: + /** Returns true if the typeface's glyph masks may refer to the foreground + * paint foreground color. This is needed to determine caching requirements. Usually true for + * typefaces that contain a COLR table. + */ + bool glyphMaskNeedsCurrentColor() const; + friend class SkStrikeServerImpl; // glyphMaskNeedsCurrentColor + friend class SkTypefaceProxyPrototype; // glyphMaskNeedsCurrentColor + + /** Retrieve detailed typeface metrics. Used by the PDF backend. */ + std::unique_ptr getAdvancedMetrics() const; + friend class SkRandomTypeface; // getAdvancedMetrics + friend class SkPDFFont; // getAdvancedMetrics + + friend class SkFontPriv; // getGlyphToUnicodeMap + +private: + SkTypefaceID fUniqueID; + SkFontStyle fStyle; + mutable SkRect fBounds; + mutable SkOnce fBoundsOnce; + bool fIsFixedPitch; + + using INHERITED = SkWeakRefCnt; +}; +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkTypes.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkTypes.h new file mode 100644 index 0000000000..722e7354ff --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkTypes.h @@ -0,0 +1,199 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkTypes_DEFINED +#define SkTypes_DEFINED + +// All of these files should be independent of things users can set via the user config file. +// They should also be able to be included in any order. +// IWYU pragma: begin_exports +#include "include/private/base/SkFeatures.h" + +// Load and verify defines from the user config file. +#include "include/private/base/SkLoadUserConfig.h" + +// Any includes or defines below can be configured by the user config file. +#include "include/private/base/SkAPI.h" +#include "include/private/base/SkAssert.h" +#include "include/private/base/SkAttributes.h" +#include "include/private/base/SkDebug.h" +// IWYU pragma: end_exports + +#include +#include + +#if !defined(SK_GANESH) && !defined(SK_GRAPHITE) +# undef SK_GL +# undef SK_VULKAN +# undef SK_METAL +# undef SK_DAWN +# undef SK_DIRECT3D +#endif + +// If SK_R32_SHIFT is set, we'll use that to choose RGBA or BGRA. +// If not, we'll default to RGBA everywhere except BGRA on Windows. +#if defined(SK_R32_SHIFT) + static_assert(SK_R32_SHIFT == 0 || SK_R32_SHIFT == 16, ""); +#elif defined(SK_BUILD_FOR_WIN) + #define SK_R32_SHIFT 16 +#else + #define SK_R32_SHIFT 0 +#endif + +#if defined(SK_B32_SHIFT) + static_assert(SK_B32_SHIFT == (16-SK_R32_SHIFT), ""); +#else + #define SK_B32_SHIFT (16-SK_R32_SHIFT) +#endif + +#define SK_G32_SHIFT 8 +#define SK_A32_SHIFT 24 + +/** + * SK_PMCOLOR_BYTE_ORDER can be used to query the byte order of SkPMColor at compile time. + */ +#ifdef SK_CPU_BENDIAN +# define SK_PMCOLOR_BYTE_ORDER(C0, C1, C2, C3) \ + (SK_ ## C3 ## 32_SHIFT == 0 && \ + SK_ ## C2 ## 32_SHIFT == 8 && \ + SK_ ## C1 ## 32_SHIFT == 16 && \ + SK_ ## C0 ## 32_SHIFT == 24) +#else +# define SK_PMCOLOR_BYTE_ORDER(C0, C1, C2, C3) \ + (SK_ ## C0 ## 32_SHIFT == 0 && \ + SK_ ## C1 ## 32_SHIFT == 8 && \ + SK_ ## C2 ## 32_SHIFT == 16 && \ + SK_ ## C3 ## 32_SHIFT == 24) +#endif + +#if defined SK_DEBUG && defined SK_BUILD_FOR_WIN + #ifdef free + #undef free + #endif + #include + #undef free +#endif + +#ifndef SK_ALLOW_STATIC_GLOBAL_INITIALIZERS + #define SK_ALLOW_STATIC_GLOBAL_INITIALIZERS 0 +#endif + +#if !defined(SK_GAMMA_EXPONENT) + #define SK_GAMMA_EXPONENT (0.0f) // SRGB +#endif + +#if !defined(SK_GAMMA_CONTRAST) + // A value of 0.5 for SK_GAMMA_CONTRAST appears to be a good compromise. + // With lower values small text appears washed out (though correctly so). + // With higher values lcd fringing is worse and the smoothing effect of + // partial coverage is diminished. + #define SK_GAMMA_CONTRAST (0.5f) +#endif + +#if defined(SK_HISTOGRAM_ENUMERATION) || \ + defined(SK_HISTOGRAM_BOOLEAN) || \ + defined(SK_HISTOGRAM_EXACT_LINEAR) || \ + defined(SK_HISTOGRAM_MEMORY_KB) +# define SK_HISTOGRAMS_ENABLED 1 +#else +# define SK_HISTOGRAMS_ENABLED 0 +#endif + +#ifndef SK_HISTOGRAM_BOOLEAN +# define SK_HISTOGRAM_BOOLEAN(name, sample) +#endif + +#ifndef SK_HISTOGRAM_ENUMERATION +# define SK_HISTOGRAM_ENUMERATION(name, sample, enum_size) +#endif + +#ifndef SK_HISTOGRAM_EXACT_LINEAR +# define SK_HISTOGRAM_EXACT_LINEAR(name, sample, value_max) +#endif + +#ifndef SK_HISTOGRAM_MEMORY_KB +# define SK_HISTOGRAM_MEMORY_KB(name, sample) +#endif + +#define SK_HISTOGRAM_PERCENTAGE(name, percent_as_int) \ + SK_HISTOGRAM_EXACT_LINEAR(name, percent_as_int, 101) + +// The top-level define SK_ENABLE_OPTIMIZE_SIZE can be used to remove several large features at once +#if defined(SK_ENABLE_OPTIMIZE_SIZE) + #if !defined(SK_FORCE_RASTER_PIPELINE_BLITTER) + #define SK_FORCE_RASTER_PIPELINE_BLITTER + #endif + #define SK_DISABLE_SDF_TEXT +#endif + +#ifndef SK_DISABLE_LEGACY_SHADERCONTEXT +# define SK_ENABLE_LEGACY_SHADERCONTEXT +#endif + +#if defined(SK_BUILD_FOR_LIBFUZZER) || defined(SK_BUILD_FOR_AFL_FUZZ) +#if !defined(SK_BUILD_FOR_FUZZER) + #define SK_BUILD_FOR_FUZZER +#endif +#endif + +/** + * These defines are set to 0 or 1, rather than being undefined or defined + * TODO: consider updating these for consistency + */ + +#if !defined(GR_CACHE_STATS) + #if defined(SK_DEBUG) || defined(SK_DUMP_STATS) + #define GR_CACHE_STATS 1 + #else + #define GR_CACHE_STATS 0 + #endif +#endif + +#if !defined(GR_GPU_STATS) + #if defined(SK_DEBUG) || defined(SK_DUMP_STATS) || defined(GR_TEST_UTILS) + #define GR_GPU_STATS 1 + #else + #define GR_GPU_STATS 0 + #endif +#endif + +//////////////////////////////////////////////////////////////////////////////// + +typedef uint32_t SkFourByteTag; +static inline constexpr SkFourByteTag SkSetFourByteTag(char a, char b, char c, char d) { + return (((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)c << 8) | (uint32_t)d); +} + +//////////////////////////////////////////////////////////////////////////////// + +/** 32 bit integer to hold a unicode value +*/ +typedef int32_t SkUnichar; + +/** 16 bit unsigned integer to hold a glyph index +*/ +typedef uint16_t SkGlyphID; + +/** 32 bit value to hold a millisecond duration + Note that SK_MSecMax is about 25 days. +*/ +typedef uint32_t SkMSec; + +/** Maximum representable milliseconds; 24d 20h 31m 23.647s. +*/ +static constexpr SkMSec SK_MSecMax = INT32_MAX; + +/** The generation IDs in Skia reserve 0 has an invalid marker. +*/ +static constexpr uint32_t SK_InvalidGenID = 0; + +/** The unique IDs in Skia reserve 0 has an invalid marker. +*/ +static constexpr uint32_t SK_InvalidUniqueID = 0; + + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkUnPreMultiply.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkUnPreMultiply.h new file mode 100644 index 0000000000..649c89f9cc --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkUnPreMultiply.h @@ -0,0 +1,55 @@ + +/* + * Copyright 2008 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SkUnPreMultiply_DEFINED +#define SkUnPreMultiply_DEFINED + +#include "include/core/SkColor.h" +#include "include/core/SkTypes.h" +#include "include/private/base/SkCPUTypes.h" + +#include + +class SK_API SkUnPreMultiply { +public: + typedef uint32_t Scale; + + // index this table with alpha [0..255] + static const Scale* GetScaleTable() { + return gTable; + } + + static Scale GetScale(U8CPU alpha) { + SkASSERT(alpha <= 255); + return gTable[alpha]; + } + + /** Usage: + + const Scale* table = SkUnPreMultiply::GetScaleTable(); + + for (...) { + unsigned a = ... + SkUnPreMultiply::Scale scale = table[a]; + + red = SkUnPreMultiply::ApplyScale(scale, red); + ... + // now red is unpremultiplied + } + */ + static U8CPU ApplyScale(Scale scale, U8CPU component) { + SkASSERT(component <= 255); + return (scale * component + (1 << 23)) >> 24; + } + + static SkColor PMColorToColor(SkPMColor c); + +private: + static const uint32_t gTable[256]; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkVertices.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkVertices.h new file mode 100644 index 0000000000..0f17e45216 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkVertices.h @@ -0,0 +1,136 @@ +/* + * 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 SkVertices_DEFINED +#define SkVertices_DEFINED + +#include "include/core/SkColor.h" +#include "include/core/SkPoint.h" +#include "include/core/SkRect.h" +#include "include/core/SkRefCnt.h" +#include "include/private/base/SkAPI.h" + +#include +#include +#include + +class SkVerticesPriv; + +/** + * An immutable set of vertex data that can be used with SkCanvas::drawVertices. + */ +class SK_API SkVertices : public SkNVRefCnt { + struct Desc; + struct Sizes; +public: + enum VertexMode { + kTriangles_VertexMode, + kTriangleStrip_VertexMode, + kTriangleFan_VertexMode, + + kLast_VertexMode = kTriangleFan_VertexMode, + }; + + /** + * Create a vertices by copying the specified arrays. texs, colors may be nullptr, + * and indices is ignored if indexCount == 0. + */ + static sk_sp MakeCopy(VertexMode mode, int vertexCount, + const SkPoint positions[], + const SkPoint texs[], + const SkColor colors[], + int indexCount, + const uint16_t indices[]); + + static sk_sp MakeCopy(VertexMode mode, int vertexCount, + const SkPoint positions[], + const SkPoint texs[], + const SkColor colors[]) { + return MakeCopy(mode, + vertexCount, + positions, + texs, + colors, + 0, + nullptr); + } + + enum BuilderFlags { + kHasTexCoords_BuilderFlag = 1 << 0, + kHasColors_BuilderFlag = 1 << 1, + }; + class SK_API Builder { + public: + Builder(VertexMode mode, int vertexCount, int indexCount, uint32_t flags); + + bool isValid() const { return fVertices != nullptr; } + + SkPoint* positions(); + uint16_t* indices(); // returns null if there are no indices + + // If we have custom attributes, these will always be null + SkPoint* texCoords(); // returns null if there are no texCoords + SkColor* colors(); // returns null if there are no colors + + // Detach the built vertices object. After the first call, this will always return null. + sk_sp detach(); + + private: + Builder(const Desc&); + + void init(const Desc&); + + // holds a partially complete object. only completed in detach() + sk_sp fVertices; + // Extra storage for intermediate vertices in the case where the client specifies indexed + // triangle fans. These get converted to indexed triangles when the Builder is finalized. + std::unique_ptr fIntermediateFanIndices; + + friend class SkVertices; + friend class SkVerticesPriv; + }; + + uint32_t uniqueID() const { return fUniqueID; } + const SkRect& bounds() const { return fBounds; } + + // returns approximate byte size of the vertices object + size_t approximateSize() const; + + // Provides access to functions that aren't part of the public API. + SkVerticesPriv priv(); + const SkVerticesPriv priv() const; // NOLINT(readability-const-return-type) + +private: + SkVertices() {} + + friend class SkVerticesPriv; + + // these are needed since we've manually sized our allocation (see Builder::init) + friend class SkNVRefCnt; + void operator delete(void* p); + + Sizes getSizes() const; + + // we store this first, to pair with the refcnt in our base-class, so we don't have an + // unnecessary pad between it and the (possibly 8-byte aligned) ptrs. + uint32_t fUniqueID; + + // these point inside our allocation, so none of these can be "freed" + SkPoint* fPositions; // [vertexCount] + uint16_t* fIndices; // [indexCount] or null + SkPoint* fTexs; // [vertexCount] or null + SkColor* fColors; // [vertexCount] or null + + SkRect fBounds; // computed to be the union of the fPositions[] + int fVertexCount; + int fIndexCount; + + VertexMode fMode; + // below here is where the actual array data is stored. +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkYUVAInfo.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkYUVAInfo.h new file mode 100644 index 0000000000..bbbae5d383 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkYUVAInfo.h @@ -0,0 +1,308 @@ +/* + * Copyright 2020 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkYUVAInfo_DEFINED +#define SkYUVAInfo_DEFINED + +#include "include/codec/SkEncodedOrigin.h" +#include "include/core/SkImageInfo.h" +#include "include/core/SkMatrix.h" +#include "include/core/SkSize.h" +#include "include/core/SkTypes.h" + +#include +#include +#include +#include + +/** + * Specifies the structure of planes for a YUV image with optional alpha. The actual planar data + * is not part of this structure and depending on usage is in external textures or pixmaps. + */ +class SK_API SkYUVAInfo { +public: + enum YUVAChannels { kY, kU, kV, kA, kLast = kA }; + static constexpr int kYUVAChannelCount = static_cast(YUVAChannels::kLast + 1); + + struct YUVALocation; // For internal use. + using YUVALocations = std::array; + + /** + * Specifies how YUV (and optionally A) are divided among planes. Planes are separated by + * underscores in the enum value names. Within each plane the pixmap/texture channels are + * mapped to the YUVA channels in the order specified, e.g. for kY_UV Y is in channel 0 of plane + * 0, U is in channel 0 of plane 1, and V is in channel 1 of plane 1. Channel ordering + * within a pixmap/texture given the channels it contains: + * A: 0:A + * Luminance/Gray: 0:Gray + * Luminance/Gray + Alpha: 0:Gray, 1:A + * RG 0:R, 1:G + * RGB 0:R, 1:G, 2:B + * RGBA 0:R, 1:G, 2:B, 3:A + */ + enum class PlaneConfig { + kUnknown, + + kY_U_V, ///< Plane 0: Y, Plane 1: U, Plane 2: V + kY_V_U, ///< Plane 0: Y, Plane 1: V, Plane 2: U + kY_UV, ///< Plane 0: Y, Plane 1: UV + kY_VU, ///< Plane 0: Y, Plane 1: VU + kYUV, ///< Plane 0: YUV + kUYV, ///< Plane 0: UYV + + kY_U_V_A, ///< Plane 0: Y, Plane 1: U, Plane 2: V, Plane 3: A + kY_V_U_A, ///< Plane 0: Y, Plane 1: V, Plane 2: U, Plane 3: A + kY_UV_A, ///< Plane 0: Y, Plane 1: UV, Plane 2: A + kY_VU_A, ///< Plane 0: Y, Plane 1: VU, Plane 2: A + kYUVA, ///< Plane 0: YUVA + kUYVA, ///< Plane 0: UYVA + + kLast = kUYVA + }; + + /** + * UV subsampling is also specified in the enum value names using J:a:b notation (e.g. 4:2:0 is + * 1/2 horizontal and 1/2 vertical resolution for U and V). If alpha is present it is not sub- + * sampled. Note that Subsampling values other than k444 are only valid with PlaneConfig values + * that have U and V in different planes than Y (and A, if present). + */ + enum class Subsampling { + kUnknown, + + k444, ///< No subsampling. UV values for each Y. + k422, ///< 1 set of UV values for each 2x1 block of Y values. + k420, ///< 1 set of UV values for each 2x2 block of Y values. + k440, ///< 1 set of UV values for each 1x2 block of Y values. + k411, ///< 1 set of UV values for each 4x1 block of Y values. + k410, ///< 1 set of UV values for each 4x2 block of Y values. + + kLast = k410 + }; + + /** + * Describes how subsampled chroma values are sited relative to luma values. + * + * Currently only centered siting is supported but will expand to support additional sitings. + */ + enum class Siting { + /** + * Subsampled chroma value is sited at the center of the block of corresponding luma values. + */ + kCentered, + }; + + static constexpr int kMaxPlanes = 4; + + /** ratio of Y/A values to U/V values in x and y. */ + static std::tuple SubsamplingFactors(Subsampling); + + /** + * SubsamplingFactors(Subsampling) if planedIdx refers to a U/V plane and otherwise {1, 1} if + * inputs are valid. Invalid inputs consist of incompatible PlaneConfig/Subsampling/planeIdx + * combinations. {0, 0} is returned for invalid inputs. + */ + static std::tuple PlaneSubsamplingFactors(PlaneConfig, Subsampling, int planeIdx); + + /** + * Given image dimensions, a planer configuration, subsampling, and origin, determine the + * expected size of each plane. Returns the number of expected planes. planeDimensions[0] + * through planeDimensions[] are written. The input image dimensions are as displayed + * (after the planes have been transformed to the intended display orientation). The plane + * dimensions are output as the planes are stored in memory (may be rotated from image + * dimensions). + */ + static int PlaneDimensions(SkISize imageDimensions, + PlaneConfig, + Subsampling, + SkEncodedOrigin, + SkISize planeDimensions[kMaxPlanes]); + + /** Number of planes for a given PlaneConfig. */ + static constexpr int NumPlanes(PlaneConfig); + + /** + * Number of Y, U, V, A channels in the ith plane for a given PlaneConfig (or 0 if i is + * invalid). + */ + static constexpr int NumChannelsInPlane(PlaneConfig, int i); + + /** + * Given a PlaneConfig and a set of channel flags for each plane, convert to YUVALocations + * representation. Fails if channel flags aren't valid for the PlaneConfig (i.e. don't have + * enough channels in a plane) by returning an invalid set of locations (plane indices are -1). + */ + static YUVALocations GetYUVALocations(PlaneConfig, const uint32_t* planeChannelFlags); + + /** Does the PlaneConfig have alpha values? */ + static bool HasAlpha(PlaneConfig); + + SkYUVAInfo() = default; + SkYUVAInfo(const SkYUVAInfo&) = default; + + /** + * 'dimensions' should specify the size of the full resolution image (after planes have been + * oriented to how the image is displayed as indicated by 'origin'). + */ + SkYUVAInfo(SkISize dimensions, + PlaneConfig, + Subsampling, + SkYUVColorSpace, + SkEncodedOrigin origin = kTopLeft_SkEncodedOrigin, + Siting sitingX = Siting::kCentered, + Siting sitingY = Siting::kCentered); + + SkYUVAInfo& operator=(const SkYUVAInfo& that) = default; + + PlaneConfig planeConfig() const { return fPlaneConfig; } + Subsampling subsampling() const { return fSubsampling; } + + std::tuple planeSubsamplingFactors(int planeIdx) const { + return PlaneSubsamplingFactors(fPlaneConfig, fSubsampling, planeIdx); + } + + /** + * Dimensions of the full resolution image (after planes have been oriented to how the image + * is displayed as indicated by fOrigin). + */ + SkISize dimensions() const { return fDimensions; } + int width() const { return fDimensions.width(); } + int height() const { return fDimensions.height(); } + + SkYUVColorSpace yuvColorSpace() const { return fYUVColorSpace; } + Siting sitingX() const { return fSitingX; } + Siting sitingY() const { return fSitingY; } + + SkEncodedOrigin origin() const { return fOrigin; } + + SkMatrix originMatrix() const { + return SkEncodedOriginToMatrix(fOrigin, this->width(), this->height()); + } + + bool hasAlpha() const { return HasAlpha(fPlaneConfig); } + + /** + * Returns the number of planes and initializes planeDimensions[0]..planeDimensions[] to + * the expected dimensions for each plane. Dimensions are as stored in memory, before + * transformation to image display space as indicated by origin(). + */ + int planeDimensions(SkISize planeDimensions[kMaxPlanes]) const { + return PlaneDimensions(fDimensions, fPlaneConfig, fSubsampling, fOrigin, planeDimensions); + } + + /** + * Given a per-plane row bytes, determine size to allocate for all planes. Optionally retrieves + * the per-plane byte sizes in planeSizes if not null. If total size overflows will return + * SIZE_MAX and set all planeSizes to SIZE_MAX. + */ + size_t computeTotalBytes(const size_t rowBytes[kMaxPlanes], + size_t planeSizes[kMaxPlanes] = nullptr) const; + + int numPlanes() const { return NumPlanes(fPlaneConfig); } + + int numChannelsInPlane(int i) const { return NumChannelsInPlane(fPlaneConfig, i); } + + /** + * Given a set of channel flags for each plane, converts this->planeConfig() to YUVALocations + * representation. Fails if the channel flags aren't valid for the PlaneConfig (i.e. don't have + * enough channels in a plane) by returning default initialized locations (all plane indices are + * -1). + */ + YUVALocations toYUVALocations(const uint32_t* channelFlags) const; + + /** + * Makes a SkYUVAInfo that is identical to this one but with the passed Subsampling. If the + * passed Subsampling is not k444 and this info's PlaneConfig is not compatible with chroma + * subsampling (because Y is in the same plane as UV) then the result will be an invalid + * SkYUVAInfo. + */ + SkYUVAInfo makeSubsampling(SkYUVAInfo::Subsampling) const; + + /** + * Makes a SkYUVAInfo that is identical to this one but with the passed dimensions. If the + * passed dimensions is empty then the result will be an invalid SkYUVAInfo. + */ + SkYUVAInfo makeDimensions(SkISize) const; + + bool operator==(const SkYUVAInfo& that) const; + bool operator!=(const SkYUVAInfo& that) const { return !(*this == that); } + + bool isValid() const { return fPlaneConfig != PlaneConfig::kUnknown; } + +private: + SkISize fDimensions = {0, 0}; + + PlaneConfig fPlaneConfig = PlaneConfig::kUnknown; + Subsampling fSubsampling = Subsampling::kUnknown; + + SkYUVColorSpace fYUVColorSpace = SkYUVColorSpace::kIdentity_SkYUVColorSpace; + + /** + * YUVA data often comes from formats like JPEG that support EXIF orientation. + * Code that operates on the raw YUV data often needs to know that orientation. + */ + SkEncodedOrigin fOrigin = kTopLeft_SkEncodedOrigin; + + Siting fSitingX = Siting::kCentered; + Siting fSitingY = Siting::kCentered; +}; + +constexpr int SkYUVAInfo::NumPlanes(PlaneConfig planeConfig) { + switch (planeConfig) { + case PlaneConfig::kUnknown: return 0; + case PlaneConfig::kY_U_V: return 3; + case PlaneConfig::kY_V_U: return 3; + case PlaneConfig::kY_UV: return 2; + case PlaneConfig::kY_VU: return 2; + case PlaneConfig::kYUV: return 1; + case PlaneConfig::kUYV: return 1; + case PlaneConfig::kY_U_V_A: return 4; + case PlaneConfig::kY_V_U_A: return 4; + case PlaneConfig::kY_UV_A: return 3; + case PlaneConfig::kY_VU_A: return 3; + case PlaneConfig::kYUVA: return 1; + case PlaneConfig::kUYVA: return 1; + } + SkUNREACHABLE; +} + +constexpr int SkYUVAInfo::NumChannelsInPlane(PlaneConfig config, int i) { + switch (config) { + case PlaneConfig::kUnknown: + return 0; + + case SkYUVAInfo::PlaneConfig::kY_U_V: + case SkYUVAInfo::PlaneConfig::kY_V_U: + return i >= 0 && i < 3 ? 1 : 0; + case SkYUVAInfo::PlaneConfig::kY_UV: + case SkYUVAInfo::PlaneConfig::kY_VU: + switch (i) { + case 0: return 1; + case 1: return 2; + default: return 0; + } + case SkYUVAInfo::PlaneConfig::kYUV: + case SkYUVAInfo::PlaneConfig::kUYV: + return i == 0 ? 3 : 0; + case SkYUVAInfo::PlaneConfig::kY_U_V_A: + case SkYUVAInfo::PlaneConfig::kY_V_U_A: + return i >= 0 && i < 4 ? 1 : 0; + case SkYUVAInfo::PlaneConfig::kY_UV_A: + case SkYUVAInfo::PlaneConfig::kY_VU_A: + switch (i) { + case 0: return 1; + case 1: return 2; + case 2: return 1; + default: return 0; + } + case SkYUVAInfo::PlaneConfig::kYUVA: + case SkYUVAInfo::PlaneConfig::kUYVA: + return i == 0 ? 4 : 0; + } + return 0; +} + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkYUVAPixmaps.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkYUVAPixmaps.h new file mode 100644 index 0000000000..11ec6c01a6 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/core/SkYUVAPixmaps.h @@ -0,0 +1,337 @@ +/* + * Copyright 2020 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkYUVAPixmaps_DEFINED +#define SkYUVAPixmaps_DEFINED + +#include "include/core/SkColorType.h" +#include "include/core/SkData.h" +#include "include/core/SkImageInfo.h" +#include "include/core/SkPixmap.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkSize.h" +#include "include/core/SkTypes.h" +#include "include/core/SkYUVAInfo.h" +#include "include/private/base/SkTo.h" + +#include +#include +#include +#include + +/** + * SkYUVAInfo combined with per-plane SkColorTypes and row bytes. Fully specifies the SkPixmaps + * for a YUVA image without the actual pixel memory and data. + */ +class SK_API SkYUVAPixmapInfo { +public: + static constexpr auto kMaxPlanes = SkYUVAInfo::kMaxPlanes; + + using PlaneConfig = SkYUVAInfo::PlaneConfig; + using Subsampling = SkYUVAInfo::Subsampling; + + /** + * Data type for Y, U, V, and possibly A channels independent of how values are packed into + * planes. + **/ + enum class DataType { + kUnorm8, ///< 8 bit unsigned normalized + kUnorm16, ///< 16 bit unsigned normalized + kFloat16, ///< 16 bit (half) floating point + kUnorm10_Unorm2, ///< 10 bit unorm for Y, U, and V. 2 bit unorm for alpha (if present). + + kLast = kUnorm10_Unorm2 + }; + static constexpr int kDataTypeCnt = static_cast(DataType::kLast) + 1; + + class SK_API SupportedDataTypes { + public: + /** Defaults to nothing supported. */ + constexpr SupportedDataTypes() = default; + + /** All legal combinations of PlaneConfig and DataType are supported. */ + static constexpr SupportedDataTypes All(); + + /** + * Checks whether there is a supported combination of color types for planes structured + * as indicated by PlaneConfig with channel data types as indicated by DataType. + */ + constexpr bool supported(PlaneConfig, DataType) const; + + /** + * Update to add support for pixmaps with numChannel channels where each channel is + * represented as DataType. + */ + void enableDataType(DataType, int numChannels); + + private: + // The bit for DataType dt with n channels is at index kDataTypeCnt*(n-1) + dt. + std::bitset fDataTypeSupport = {}; + }; + + /** + * Gets the default SkColorType to use with numChannels channels, each represented as DataType. + * Returns kUnknown_SkColorType if no such color type. + */ + static constexpr SkColorType DefaultColorTypeForDataType(DataType dataType, int numChannels); + + /** + * If the SkColorType is supported for YUVA pixmaps this will return the number of YUVA channels + * that can be stored in a plane of this color type and what the DataType is of those channels. + * If the SkColorType is not supported as a YUVA plane the number of channels is reported as 0 + * and the DataType returned should be ignored. + */ + static std::tuple NumChannelsAndDataType(SkColorType); + + /** Default SkYUVAPixmapInfo is invalid. */ + SkYUVAPixmapInfo() = default; + + /** + * Initializes the SkYUVAPixmapInfo from a SkYUVAInfo with per-plane color types and row bytes. + * This will be invalid if the colorTypes aren't compatible with the SkYUVAInfo or if a + * rowBytes entry is not valid for the plane dimensions and color type. Color type and + * row byte values beyond the number of planes in SkYUVAInfo are ignored. All SkColorTypes + * must have the same DataType or this will be invalid. + * + * If rowBytes is nullptr then bpp*width is assumed for each plane. + */ + SkYUVAPixmapInfo(const SkYUVAInfo&, + const SkColorType[kMaxPlanes], + const size_t rowBytes[kMaxPlanes]); + /** + * Like above but uses DefaultColorTypeForDataType to determine each plane's SkColorType. If + * rowBytes is nullptr then bpp*width is assumed for each plane. + */ + SkYUVAPixmapInfo(const SkYUVAInfo&, DataType, const size_t rowBytes[kMaxPlanes]); + + SkYUVAPixmapInfo(const SkYUVAPixmapInfo&) = default; + + SkYUVAPixmapInfo& operator=(const SkYUVAPixmapInfo&) = default; + + bool operator==(const SkYUVAPixmapInfo&) const; + bool operator!=(const SkYUVAPixmapInfo& that) const { return !(*this == that); } + + const SkYUVAInfo& yuvaInfo() const { return fYUVAInfo; } + + SkYUVColorSpace yuvColorSpace() const { return fYUVAInfo.yuvColorSpace(); } + + /** The number of SkPixmap planes, 0 if this SkYUVAPixmapInfo is invalid. */ + int numPlanes() const { return fYUVAInfo.numPlanes(); } + + /** The per-YUV[A] channel data type. */ + DataType dataType() const { return fDataType; } + + /** + * Row bytes for the ith plane. Returns zero if i >= numPlanes() or this SkYUVAPixmapInfo is + * invalid. + */ + size_t rowBytes(int i) const { return fRowBytes[static_cast(i)]; } + + /** Image info for the ith plane, or default SkImageInfo if i >= numPlanes() */ + const SkImageInfo& planeInfo(int i) const { return fPlaneInfos[static_cast(i)]; } + + /** + * Determine size to allocate for all planes. Optionally retrieves the per-plane sizes in + * planeSizes if not null. If total size overflows will return SIZE_MAX and set all planeSizes + * to SIZE_MAX. Returns 0 and fills planesSizes with 0 if this SkYUVAPixmapInfo is not valid. + */ + size_t computeTotalBytes(size_t planeSizes[kMaxPlanes] = nullptr) const; + + /** + * Takes an allocation that is assumed to be at least computeTotalBytes() in size and configures + * the first numPlanes() entries in pixmaps array to point into that memory. The remaining + * entries of pixmaps are default initialized. Fails if this SkYUVAPixmapInfo not valid. + */ + bool initPixmapsFromSingleAllocation(void* memory, SkPixmap pixmaps[kMaxPlanes]) const; + + /** + * Returns true if this has been configured with a non-empty dimensioned SkYUVAInfo with + * compatible color types and row bytes. + */ + bool isValid() const { return fYUVAInfo.isValid(); } + + /** Is this valid and does it use color types allowed by the passed SupportedDataTypes? */ + bool isSupported(const SupportedDataTypes&) const; + +private: + SkYUVAInfo fYUVAInfo; + std::array fPlaneInfos = {}; + std::array fRowBytes = {}; + DataType fDataType = DataType::kUnorm8; + static_assert(kUnknown_SkColorType == 0, "default init isn't kUnknown"); +}; + +/** + * Helper to store SkPixmap planes as described by a SkYUVAPixmapInfo. Can be responsible for + * allocating/freeing memory for pixmaps or use external memory. + */ +class SK_API SkYUVAPixmaps { +public: + using DataType = SkYUVAPixmapInfo::DataType; + static constexpr auto kMaxPlanes = SkYUVAPixmapInfo::kMaxPlanes; + + static SkColorType RecommendedRGBAColorType(DataType); + + /** Allocate space for pixmaps' pixels in the SkYUVAPixmaps. */ + static SkYUVAPixmaps Allocate(const SkYUVAPixmapInfo& yuvaPixmapInfo); + + /** + * Use storage in SkData as backing store for pixmaps' pixels. SkData is retained by the + * SkYUVAPixmaps. + */ + static SkYUVAPixmaps FromData(const SkYUVAPixmapInfo&, sk_sp); + + /** + * Makes a deep copy of the src SkYUVAPixmaps. The returned SkYUVAPixmaps owns its planes' + * backing stores. + */ + static SkYUVAPixmaps MakeCopy(const SkYUVAPixmaps& src); + + /** + * Use passed in memory as backing store for pixmaps' pixels. Caller must ensure memory remains + * allocated while pixmaps are in use. There must be at least + * SkYUVAPixmapInfo::computeTotalBytes() allocated starting at memory. + */ + static SkYUVAPixmaps FromExternalMemory(const SkYUVAPixmapInfo&, void* memory); + + /** + * Wraps existing SkPixmaps. The SkYUVAPixmaps will have no ownership of the SkPixmaps' pixel + * memory so the caller must ensure it remains valid. Will return an invalid SkYUVAPixmaps if + * the SkYUVAInfo isn't compatible with the SkPixmap array (number of planes, plane dimensions, + * sufficient color channels in planes, ...). + */ + static SkYUVAPixmaps FromExternalPixmaps(const SkYUVAInfo&, const SkPixmap[kMaxPlanes]); + + /** Default SkYUVAPixmaps is invalid. */ + SkYUVAPixmaps() = default; + ~SkYUVAPixmaps() = default; + + SkYUVAPixmaps(SkYUVAPixmaps&& that) = default; + SkYUVAPixmaps& operator=(SkYUVAPixmaps&& that) = default; + SkYUVAPixmaps(const SkYUVAPixmaps&) = default; + SkYUVAPixmaps& operator=(const SkYUVAPixmaps& that) = default; + + /** Does have initialized pixmaps compatible with its SkYUVAInfo. */ + bool isValid() const { return !fYUVAInfo.dimensions().isEmpty(); } + + const SkYUVAInfo& yuvaInfo() const { return fYUVAInfo; } + + DataType dataType() const { return fDataType; } + + SkYUVAPixmapInfo pixmapsInfo() const; + + /** Number of pixmap planes or 0 if this SkYUVAPixmaps is invalid. */ + int numPlanes() const { return this->isValid() ? fYUVAInfo.numPlanes() : 0; } + + /** + * Access the SkPixmap planes. They are default initialized if this is not a valid + * SkYUVAPixmaps. + */ + const std::array& planes() const { return fPlanes; } + + /** + * Get the ith SkPixmap plane. SkPixmap will be default initialized if i >= numPlanes or this + * SkYUVAPixmaps is invalid. + */ + const SkPixmap& plane(int i) const { return fPlanes[SkToSizeT(i)]; } + + /** + * Computes a YUVALocations representation of the planar layout. The result is guaranteed to be + * valid if this->isValid(). + */ + SkYUVAInfo::YUVALocations toYUVALocations() const; + + /** Does this SkPixmaps own the backing store of the planes? */ + bool ownsStorage() const { return SkToBool(fData); } + +private: + SkYUVAPixmaps(const SkYUVAPixmapInfo&, sk_sp); + SkYUVAPixmaps(const SkYUVAInfo&, DataType, const SkPixmap[kMaxPlanes]); + + std::array fPlanes = {}; + sk_sp fData; + SkYUVAInfo fYUVAInfo; + DataType fDataType; +}; + +////////////////////////////////////////////////////////////////////////////// + +constexpr SkYUVAPixmapInfo::SupportedDataTypes SkYUVAPixmapInfo::SupportedDataTypes::All() { + using ULL = unsigned long long; // bitset cons. takes this. + ULL bits = 0; + for (ULL c = 1; c <= 4; ++c) { + for (ULL dt = 0; dt <= ULL(kDataTypeCnt); ++dt) { + if (DefaultColorTypeForDataType(static_cast(dt), + static_cast(c)) != kUnknown_SkColorType) { + bits |= ULL(1) << (dt + static_cast(kDataTypeCnt)*(c - 1)); + } + } + } + SupportedDataTypes combinations; + combinations.fDataTypeSupport = bits; + return combinations; +} + +constexpr bool SkYUVAPixmapInfo::SupportedDataTypes::supported(PlaneConfig config, + DataType type) const { + int n = SkYUVAInfo::NumPlanes(config); + for (int i = 0; i < n; ++i) { + auto c = static_cast(SkYUVAInfo::NumChannelsInPlane(config, i)); + SkASSERT(c >= 1 && c <= 4); + if (!fDataTypeSupport[static_cast(type) + + (c - 1)*static_cast(kDataTypeCnt)]) { + return false; + } + } + return true; +} + +constexpr SkColorType SkYUVAPixmapInfo::DefaultColorTypeForDataType(DataType dataType, + int numChannels) { + switch (numChannels) { + case 1: + switch (dataType) { + case DataType::kUnorm8: return kGray_8_SkColorType; + case DataType::kUnorm16: return kA16_unorm_SkColorType; + case DataType::kFloat16: return kA16_float_SkColorType; + case DataType::kUnorm10_Unorm2: return kUnknown_SkColorType; + } + break; + case 2: + switch (dataType) { + case DataType::kUnorm8: return kR8G8_unorm_SkColorType; + case DataType::kUnorm16: return kR16G16_unorm_SkColorType; + case DataType::kFloat16: return kR16G16_float_SkColorType; + case DataType::kUnorm10_Unorm2: return kUnknown_SkColorType; + } + break; + case 3: + // None of these are tightly packed. The intended use case is for interleaved YUVA + // planes where we're forcing opaqueness by ignoring the alpha values. + // There are "x" rather than "A" variants for Unorm8 and Unorm10_Unorm2 but we don't + // choose them because 1) there is no inherent advantage and 2) there is better support + // in the GPU backend for the "A" versions. + switch (dataType) { + case DataType::kUnorm8: return kRGBA_8888_SkColorType; + case DataType::kUnorm16: return kR16G16B16A16_unorm_SkColorType; + case DataType::kFloat16: return kRGBA_F16_SkColorType; + case DataType::kUnorm10_Unorm2: return kRGBA_1010102_SkColorType; + } + break; + case 4: + switch (dataType) { + case DataType::kUnorm8: return kRGBA_8888_SkColorType; + case DataType::kUnorm16: return kR16G16B16A16_unorm_SkColorType; + case DataType::kFloat16: return kRGBA_F16_SkColorType; + case DataType::kUnorm10_Unorm2: return kRGBA_1010102_SkColorType; + } + break; + } + return kUnknown_SkColorType; +} + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/docs/SkMultiPictureDocument.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/docs/SkMultiPictureDocument.h new file mode 100644 index 0000000000..5b8b8063b1 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/docs/SkMultiPictureDocument.h @@ -0,0 +1,53 @@ +/* + * Copyright 2023 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkMultiPictureDocument_DEFINED +#define SkMultiPictureDocument_DEFINED + +#include "include/core/SkPicture.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkSize.h" +#include "include/core/SkTypes.h" + +#include + +class SkDocument; +class SkStreamSeekable; +class SkWStream; +struct SkDeserialProcs; +struct SkSerialProcs; + +struct SkDocumentPage { + sk_sp fPicture; + SkSize fSize; +}; + +namespace SkMultiPictureDocument { +/** + * Writes into a file format that is similar to SkPicture::serialize() + * Accepts a callback for endPage behavior + */ +SK_API sk_sp Make(SkWStream* dst, const SkSerialProcs* = nullptr, + std::function onEndPage = nullptr); + +/** + * Returns the number of pages in the SkMultiPictureDocument. + */ +SK_API int ReadPageCount(SkStreamSeekable* src); + +/** + * Read the SkMultiPictureDocument into the provided array of pages. + * dstArrayCount must equal SkMultiPictureDocumentReadPageCount(). + * Return false on error. + */ +SK_API bool Read(SkStreamSeekable* src, + SkDocumentPage* dstArray, + int dstArrayCount, + const SkDeserialProcs* = nullptr); +} // namespace SkMultiPictureDocument + +#endif // SkMultiPictureDocument_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/docs/SkPDFDocument.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/docs/SkPDFDocument.h new file mode 100644 index 0000000000..bf5fe9dd8e --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/docs/SkPDFDocument.h @@ -0,0 +1,224 @@ +// Copyright 2018 Google LLC. +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. +#ifndef SkPDFDocument_DEFINED +#define SkPDFDocument_DEFINED + +#include "include/core/SkDocument.h" +#include "include/core/SkMilestone.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkScalar.h" +#include "include/core/SkString.h" +#include "include/private/base/SkAPI.h" +#include "include/private/base/SkNoncopyable.h" + +#include +#include +#include + +class SkCanvas; +class SkExecutor; +class SkPDFArray; +class SkPDFTagTree; +class SkWStream; + +#define SKPDF_STRING(X) SKPDF_STRING_IMPL(X) +#define SKPDF_STRING_IMPL(X) #X + +namespace SkPDF { + +/** Attributes for nodes in the PDF tree. */ +class SK_API AttributeList : SkNoncopyable { +public: + AttributeList(); + ~AttributeList(); + + // Each attribute must have an owner (e.g. "Layout", "List", "Table", etc) + // and an attribute name (e.g. "BBox", "RowSpan", etc.) from PDF32000_2008 14.8.5, + // and then a value of the proper type according to the spec. + void appendInt(const char* owner, const char* name, int value); + void appendFloat(const char* owner, const char* name, float value); + void appendName(const char* owner, const char* attrName, const char* value); + void appendFloatArray(const char* owner, + const char* name, + const std::vector& value); + void appendNodeIdArray(const char* owner, + const char* attrName, + const std::vector& nodeIds); + +private: + friend class ::SkPDFTagTree; + + std::unique_ptr fAttrs; +}; + +/** A node in a PDF structure tree, giving a semantic representation + of the content. Each node ID is associated with content + by passing the SkCanvas and node ID to SkPDF::SetNodeId() when drawing. + NodeIDs should be unique within each tree. +*/ +struct StructureElementNode { + SkString fTypeString; + std::vector> fChildVector; + int fNodeId = 0; + std::vector fAdditionalNodeIds; + AttributeList fAttributes; + SkString fAlt; + SkString fLang; +}; + +struct DateTime { + int16_t fTimeZoneMinutes; // The number of minutes that this + // is ahead of or behind UTC. + uint16_t fYear; //!< e.g. 2005 + uint8_t fMonth; //!< 1..12 + uint8_t fDayOfWeek; //!< 0..6, 0==Sunday + uint8_t fDay; //!< 1..31 + uint8_t fHour; //!< 0..23 + uint8_t fMinute; //!< 0..59 + uint8_t fSecond; //!< 0..59 + + void toISO8601(SkString* dst) const; +}; + +/** Optional metadata to be passed into the PDF factory function. +*/ +struct Metadata { + /** The document's title. + */ + SkString fTitle; + + /** The name of the person who created the document. + */ + SkString fAuthor; + + /** The subject of the document. + */ + SkString fSubject; + + /** Keywords associated with the document. Commas may be used to delineate + keywords within the string. + */ + SkString fKeywords; + + /** If the document was converted to PDF from another format, + the name of the conforming product that created the + original document from which it was converted. + */ + SkString fCreator; + + /** The product that is converting this document to PDF. + */ + SkString fProducer = SkString("Skia/PDF m" SKPDF_STRING(SK_MILESTONE)); + + /** The date and time the document was created. + The zero default value represents an unknown/unset time. + */ + DateTime fCreation = {0, 0, 0, 0, 0, 0, 0, 0}; + + /** The date and time the document was most recently modified. + The zero default value represents an unknown/unset time. + */ + DateTime fModified = {0, 0, 0, 0, 0, 0, 0, 0}; + + /** The natural language of the text in the PDF. If fLang is empty, the root + StructureElementNode::fLang will be used (if not empty). Text not in + this language should be marked with StructureElementNode::fLang. + */ + SkString fLang; + + /** The DPI (pixels-per-inch) at which features without native PDF support + will be rasterized (e.g. draw image with perspective, draw text with + perspective, ...) A larger DPI would create a PDF that reflects the + original intent with better fidelity, but it can make for larger PDF + files too, which would use more memory while rendering, and it would be + slower to be processed or sent online or to printer. + */ + SkScalar fRasterDPI = SK_ScalarDefaultRasterDPI; + + /** If true, include XMP metadata, a document UUID, and sRGB output intent + information. This adds length to the document and makes it + non-reproducable, but are necessary features for PDF/A-2b conformance + */ + bool fPDFA = false; + + /** Encoding quality controls the trade-off between size and quality. By + default this is set to 101 percent, which corresponds to lossless + encoding. If this value is set to a value <= 100, and the image is + opaque, it will be encoded (using JPEG) with that quality setting. + */ + int fEncodingQuality = 101; + + /** An optional tree of structured document tags that provide + a semantic representation of the content. The caller + should retain ownership. + */ + StructureElementNode* fStructureElementTreeRoot = nullptr; + + enum class Outline : int { + None = 0, + StructureElementHeaders = 1, + } fOutline = Outline::None; + + /** Executor to handle threaded work within PDF Backend. 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 executing Deflate algorithm in parallel. + + If set, the PDF output will be non-reproducible in the order and + internal numbering of objects, but should render the same. + + Experimental. + */ + SkExecutor* fExecutor = nullptr; + + /** PDF streams may be compressed to save space. + Use this to specify the desired compression vs time tradeoff. + */ + enum class CompressionLevel : int { + Default = -1, + None = 0, + LowButFast = 1, + Average = 6, + HighButSlow = 9, + } fCompressionLevel = CompressionLevel::Default; + + /** Preferred Subsetter. */ + enum Subsetter { + kHarfbuzz_Subsetter, + } fSubsetter = kHarfbuzz_Subsetter; +}; + +/** 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. + + @param canvas The canvas used to draw to the PDF. + @param nodeId The node ID for subsequent drawing commands. +*/ +SK_API void SetNodeId(SkCanvas* dst, int nodeID); + +/** Create a PDF-backed document, writing the results into a SkWStream. + + PDF pages are sized in point units. 1 pt == 1/72 inch == 127/360 mm. + + @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. + + @returns NULL if there is an error, otherwise a newly created PDF-backed SkDocument. +*/ +SK_API sk_sp MakeDocument(SkWStream* stream, const Metadata& metadata); + +static inline sk_sp MakeDocument(SkWStream* stream) { + return MakeDocument(stream, Metadata()); +} + +} // namespace SkPDF + +#undef SKPDF_STRING +#undef SKPDF_STRING_IMPL +#endif // SkPDFDocument_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/docs/SkXPSDocument.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/docs/SkXPSDocument.h new file mode 100644 index 0000000000..5cd0777c9b --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/docs/SkXPSDocument.h @@ -0,0 +1,27 @@ +/* + * 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 SkXPSDocument_DEFINED +#define SkXPSDocument_DEFINED + +#include "include/core/SkTypes.h" + +#ifdef SK_BUILD_FOR_WIN + +#include "include/core/SkDocument.h" + +struct IXpsOMObjectFactory; + +namespace SkXPS { + +SK_API sk_sp MakeDocument(SkWStream* stream, + IXpsOMObjectFactory* xpsFactory, + SkScalar dpi = SK_ScalarDefaultRasterDPI); + +} // namespace SkXPS +#endif // SK_BUILD_FOR_WIN +#endif // SkXPSDocument_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/Sk1DPathEffect.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/Sk1DPathEffect.h new file mode 100644 index 0000000000..fd05c52df7 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/Sk1DPathEffect.h @@ -0,0 +1,40 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef Sk1DPathEffect_DEFINED +#define Sk1DPathEffect_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" + +class SkPath; +class SkPathEffect; + +class SK_API SkPath1DPathEffect { +public: + enum Style { + kTranslate_Style, // translate the shape to each position + kRotate_Style, // rotate the shape about its center + kMorph_Style, // transform each point, and turn lines into curves + + kLastEnum_Style = kMorph_Style, + }; + + /** Dash by replicating the specified path. + @param path The path to replicate (dash) + @param advance The space between instances of path + @param phase distance (mod advance) along path for its initial position + @param style how to transform path at each point (based on the current + position and tangent) + */ + static sk_sp Make(const SkPath& path, SkScalar advance, SkScalar phase, Style); + + static void RegisterFlattenables(); +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/Sk2DPathEffect.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/Sk2DPathEffect.h new file mode 100644 index 0000000000..b8b3ba3981 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/Sk2DPathEffect.h @@ -0,0 +1,33 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef Sk2DPathEffect_DEFINED +#define Sk2DPathEffect_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" + +class SkMatrix; +class SkPath; +class SkPathEffect; + +class SK_API SkLine2DPathEffect { +public: + static sk_sp Make(SkScalar width, const SkMatrix& matrix); + + static void RegisterFlattenables(); +}; + +class SK_API SkPath2DPathEffect { +public: + static sk_sp Make(const SkMatrix& matrix, const SkPath& path); + + static void RegisterFlattenables(); +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkBlenders.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkBlenders.h new file mode 100644 index 0000000000..7507071b05 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkBlenders.h @@ -0,0 +1,27 @@ +/* + * Copyright 2021 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkBlenders_DEFINED +#define SkBlenders_DEFINED + +#include "include/core/SkBlender.h" + +class SK_API SkBlenders { +public: + /** + * Create a blender that implements the following: + * k1 * src * dst + k2 * src + k3 * dst + k4 + * @param k1, k2, k3, k4 The four coefficients. + * @param enforcePMColor If true, the RGB channels will be clamped to the calculated alpha. + */ + static sk_sp Arithmetic(float k1, float k2, float k3, float k4, bool enforcePremul); + +private: + SkBlenders() = delete; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkBlurMaskFilter.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkBlurMaskFilter.h new file mode 100644 index 0000000000..1b9319869e --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkBlurMaskFilter.h @@ -0,0 +1,35 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkBlurMaskFilter_DEFINED +#define SkBlurMaskFilter_DEFINED + +// we include this since our callers will need to at least be able to ref/unref +#include "include/core/SkBlurTypes.h" +#include "include/core/SkMaskFilter.h" +#include "include/core/SkRect.h" +#include "include/core/SkScalar.h" + +class SkRRect; + +class SK_API SkBlurMaskFilter { +public: +#ifdef SK_SUPPORT_LEGACY_EMBOSSMASKFILTER + /** Create an emboss maskfilter + @param blurSigma standard deviation of the Gaussian blur to apply + before applying lighting (e.g. 3) + @param direction array of 3 scalars [x, y, z] specifying the direction of the light source + @param ambient 0...1 amount of ambient light + @param specular coefficient for specular highlights (e.g. 8) + @return the emboss maskfilter + */ + static sk_sp MakeEmboss(SkScalar blurSigma, const SkScalar direction[3], + SkScalar ambient, SkScalar specular); +#endif +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkColorMatrix.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkColorMatrix.h new file mode 100644 index 0000000000..5092278f0d --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkColorMatrix.h @@ -0,0 +1,57 @@ +/* + * Copyright 2007 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkColorMatrix_DEFINED +#define SkColorMatrix_DEFINED + +#include "include/core/SkTypes.h" + +#include +#include + +enum SkYUVColorSpace : int; + +class SK_API SkColorMatrix { +public: + constexpr SkColorMatrix() : SkColorMatrix(1, 0, 0, 0, 0, + 0, 1, 0, 0, 0, + 0, 0, 1, 0, 0, + 0, 0, 0, 1, 0) {} + + constexpr SkColorMatrix(float m00, float m01, float m02, float m03, float m04, + float m10, float m11, float m12, float m13, float m14, + float m20, float m21, float m22, float m23, float m24, + float m30, float m31, float m32, float m33, float m34) + : fMat { m00, m01, m02, m03, m04, + m10, m11, m12, m13, m14, + m20, m21, m22, m23, m24, + m30, m31, m32, m33, m34 } {} + + static SkColorMatrix RGBtoYUV(SkYUVColorSpace); + static SkColorMatrix YUVtoRGB(SkYUVColorSpace); + + void setIdentity(); + void setScale(float rScale, float gScale, float bScale, float aScale = 1.0f); + + void postTranslate(float dr, float dg, float db, float da); + + void setConcat(const SkColorMatrix& a, const SkColorMatrix& b); + void preConcat(const SkColorMatrix& mat) { this->setConcat(*this, mat); } + void postConcat(const SkColorMatrix& mat) { this->setConcat(mat, *this); } + + void setSaturation(float sat); + + void setRowMajor(const float src[20]) { std::copy_n(src, 20, fMat.begin()); } + void getRowMajor(float dst[20]) const { std::copy_n(fMat.begin(), 20, dst); } + +private: + std::array fMat; + + friend class SkColorFilters; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkColorMatrixFilter.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkColorMatrixFilter.h new file mode 100644 index 0000000000..3e5337b0cf --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkColorMatrixFilter.h @@ -0,0 +1,22 @@ +/* + * Copyright 2007 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkColorMatrixFilter_DEFINED +#define SkColorMatrixFilter_DEFINED + +#include "include/core/SkColorFilter.h" + +// (DEPRECATED) This factory function is deprecated. Please use the one in +// SkColorFilters (i.e., Lighting). +class SK_API SkColorMatrixFilter : public SkColorFilter { +public: + static sk_sp MakeLightingFilter(SkColor mul, SkColor add) { + return SkColorFilters::Lighting(mul, add); + } +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkCornerPathEffect.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkCornerPathEffect.h new file mode 100644 index 0000000000..7f7e7159f3 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkCornerPathEffect.h @@ -0,0 +1,32 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkCornerPathEffect_DEFINED +#define SkCornerPathEffect_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" + +class SkPathEffect; + +/** \class SkCornerPathEffect + + SkCornerPathEffect is a subclass of SkPathEffect that can turn sharp corners + into various treatments (e.g. rounded corners) +*/ +class SK_API SkCornerPathEffect { +public: + /** radius must be > 0 to have an effect. It specifies the distance from each corner + that should be "rounded". + */ + static sk_sp Make(SkScalar radius); + + static void RegisterFlattenables(); +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkDashPathEffect.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkDashPathEffect.h new file mode 100644 index 0000000000..f30064aa94 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkDashPathEffect.h @@ -0,0 +1,43 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkDashPathEffect_DEFINED +#define SkDashPathEffect_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" + +class SkPathEffect; + +class SK_API SkDashPathEffect { +public: + /** intervals: array containing an even number of entries (>=2), with + the even indices specifying the length of "on" intervals, and the odd + indices specifying the length of "off" intervals. This array will be + copied in Make, and can be disposed of freely after. + count: number of elements in the intervals array + phase: offset into the intervals array (mod the sum of all of the + intervals). + + For example: if intervals[] = {10, 20}, count = 2, and phase = 25, + this will set up a dashed path like so: + 5 pixels off + 10 pixels on + 20 pixels off + 10 pixels on + 20 pixels off + ... + A phase of -5, 25, 55, 85, etc. would all result in the same path, + because the sum of all the intervals is 30. + + Note: only affects stroked paths. + */ + static sk_sp Make(const SkScalar intervals[], int count, SkScalar phase); +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkDiscretePathEffect.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkDiscretePathEffect.h new file mode 100644 index 0000000000..6054cbdc99 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkDiscretePathEffect.h @@ -0,0 +1,37 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkDiscretePathEffect_DEFINED +#define SkDiscretePathEffect_DEFINED + +#include "include/core/SkPathEffect.h" + +/** \class SkDiscretePathEffect + + This path effect chops a path into discrete segments, and randomly displaces them. +*/ +class SK_API SkDiscretePathEffect { +public: + /** Break the path into segments of segLength length, and randomly move the endpoints + away from the original path by a maximum of deviation. + Note: works on filled or framed paths + + @param seedAssist This is a caller-supplied seedAssist that modifies + the seed value that is used to randomize the path + segments' endpoints. If not supplied it defaults to 0, + in which case filtering a path multiple times will + result in the same set of segments (this is useful for + testing). If a caller does not want this behaviour + they can pass in a different seedAssist to get a + different set of path segments. + */ + static sk_sp Make(SkScalar segLength, SkScalar dev, uint32_t seedAssist = 0); + + static void RegisterFlattenables(); +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkGradientShader.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkGradientShader.h new file mode 100644 index 0000000000..d725c2d8b1 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkGradientShader.h @@ -0,0 +1,354 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkGradientShader_DEFINED +#define SkGradientShader_DEFINED + +#include "include/core/SkColor.h" +#include "include/core/SkColorSpace.h" +#include "include/core/SkPoint.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkScalar.h" +#include "include/core/SkShader.h" // IWYU pragma: keep +#include "include/core/SkTileMode.h" +#include "include/private/base/SkAPI.h" + +#include +#include + +class SkMatrix; + +/** \class SkGradientShader + + SkGradientShader hosts factories for creating subclasses of SkShader that + render linear and radial gradients. In general, degenerate cases should not + produce surprising results, but there are several types of degeneracies: + + * A linear gradient made from the same two points. + * A radial gradient with a radius of zero. + * A sweep gradient where the start and end angle are the same. + * A two point conical gradient where the two centers and the two radii are + the same. + + For any degenerate gradient with a decal tile mode, it will draw empty since the interpolating + region is zero area and the outer region is discarded by the decal mode. + + For any degenerate gradient with a repeat or mirror tile mode, it will draw a solid color that + is the average gradient color, since infinitely many repetitions of the gradients will fill the + shape. + + For a clamped gradient, every type is well-defined at the limit except for linear gradients. The + radial gradient with zero radius becomes the last color. The sweep gradient draws the sector + from 0 to the provided angle with the first color, with a hardstop switching to the last color. + When the provided angle is 0, this is just the solid last color again. Similarly, the two point + conical gradient becomes a circle filled with the first color, sized to the provided radius, + with a hardstop switching to the last color. When the two radii are both zero, this is just the + solid last color. + + As a linear gradient approaches the degenerate case, its shader will approach the appearance of + two half planes, each filled by the first and last colors of the gradient. The planes will be + oriented perpendicular to the vector between the two defining points of the gradient. However, + once they become the same point, Skia cannot reconstruct what that expected orientation is. To + provide a stable and predictable color in this case, Skia just uses the last color as a solid + fill to be similar to many of the other degenerate gradients' behaviors in clamp mode. +*/ +class SK_API SkGradientShader { +public: + enum Flags { + /** By default gradients will interpolate their colors in unpremul space + * and then premultiply each of the results. By setting this flag, the + * gradients will premultiply their colors first, and then interpolate + * between them. + * example: https://fiddle.skia.org/c/@GradientShader_MakeLinear + */ + kInterpolateColorsInPremul_Flag = 1 << 0, + }; + + struct Interpolation { + enum class InPremul : bool { kNo = false, kYes = true }; + + enum class ColorSpace : uint8_t { + // Default Skia behavior: interpolate in the color space of the destination surface + kDestination, + + // https://www.w3.org/TR/css-color-4/#interpolation-space + kSRGBLinear, + kLab, + kOKLab, + // This is the same as kOKLab, except it has a simplified version of the CSS gamut + // mapping algorithm (https://www.w3.org/TR/css-color-4/#css-gamut-mapping) + // into Rec2020 space applied to it. + // Warning: This space is experimental and should not be used in production. + kOKLabGamutMap, + kLCH, + kOKLCH, + // This is the same as kOKLCH, except it has the same gamut mapping applied to it + // as kOKLabGamutMap does. + // Warning: This space is experimental and should not be used in production. + kOKLCHGamutMap, + kSRGB, + kHSL, + kHWB, + + kLastColorSpace = kHWB, + }; + static constexpr int kColorSpaceCount = static_cast(ColorSpace::kLastColorSpace) + 1; + + enum class HueMethod : uint8_t { + // https://www.w3.org/TR/css-color-4/#hue-interpolation + kShorter, + kLonger, + kIncreasing, + kDecreasing, + + kLastHueMethod = kDecreasing, + }; + static constexpr int kHueMethodCount = static_cast(HueMethod::kLastHueMethod) + 1; + + InPremul fInPremul = InPremul::kNo; + ColorSpace fColorSpace = ColorSpace::kDestination; + HueMethod fHueMethod = HueMethod::kShorter; // Only relevant for LCH, OKLCH, HSL, or HWB + + static Interpolation FromFlags(uint32_t flags) { + return {flags & kInterpolateColorsInPremul_Flag ? InPremul::kYes : InPremul::kNo, + ColorSpace::kDestination, + HueMethod::kShorter}; + } + }; + + /** Returns a shader that generates a linear gradient between the two specified points. +

+ @param pts The start and end points for the gradient. + @param colors The array[count] of colors, to be distributed between the two points + @param pos May be NULL. array[count] of SkScalars, or NULL, of the relative position of + each corresponding color in the colors array. If this is NULL, + the the colors are distributed evenly between the start and end point. + If this is not null, the values must lie between 0.0 and 1.0, and be + strictly increasing. If the first value is not 0.0, then an additional + color stop is added at position 0.0, with the same color as colors[0]. + If the the last value is not 1.0, then an additional color stop is added + at position 1.0, with the same color as colors[count - 1]. + @param count Must be >=2. The number of colors (and pos if not NULL) entries. + @param mode The tiling mode + + example: https://fiddle.skia.org/c/@GradientShader_MakeLinear + */ + static sk_sp MakeLinear(const SkPoint pts[2], + const SkColor colors[], const SkScalar pos[], int count, + SkTileMode mode, + uint32_t flags = 0, const SkMatrix* localMatrix = nullptr); + + /** Returns a shader that generates a linear gradient between the two specified points. +

+ @param pts The start and end points for the gradient. + @param colors The array[count] of colors, to be distributed between the two points + @param pos May be NULL. array[count] of SkScalars, or NULL, of the relative position of + each corresponding color in the colors array. If this is NULL, + the the colors are distributed evenly between the start and end point. + If this is not null, the values must lie between 0.0 and 1.0, and be + strictly increasing. If the first value is not 0.0, then an additional + color stop is added at position 0.0, with the same color as colors[0]. + If the the last value is not 1.0, then an additional color stop is added + at position 1.0, with the same color as colors[count - 1]. + @param count Must be >=2. The number of colors (and pos if not NULL) entries. + @param mode The tiling mode + + example: https://fiddle.skia.org/c/@GradientShader_MakeLinear + */ + static sk_sp MakeLinear(const SkPoint pts[2], + const SkColor4f colors[], sk_sp colorSpace, + const SkScalar pos[], int count, SkTileMode mode, + const Interpolation& interpolation, + const SkMatrix* localMatrix); + static sk_sp MakeLinear(const SkPoint pts[2], + const SkColor4f colors[], sk_sp colorSpace, + const SkScalar pos[], int count, SkTileMode mode, + uint32_t flags = 0, const SkMatrix* localMatrix = nullptr) { + return MakeLinear(pts, colors, std::move(colorSpace), pos, count, mode, + Interpolation::FromFlags(flags), localMatrix); + } + + /** Returns a shader that generates a radial gradient given the center and radius. +

+ @param center The center of the circle for this gradient + @param radius Must be positive. The radius of the circle for this gradient + @param colors The array[count] of colors, to be distributed between the center and edge of the circle + @param pos May be NULL. The array[count] of SkScalars, or NULL, of the relative position of + each corresponding color in the colors array. If this is NULL, + the the colors are distributed evenly between the center and edge of the circle. + If this is not null, the values must lie between 0.0 and 1.0, and be + strictly increasing. If the first value is not 0.0, then an additional + color stop is added at position 0.0, with the same color as colors[0]. + If the the last value is not 1.0, then an additional color stop is added + at position 1.0, with the same color as colors[count - 1]. + @param count Must be >= 2. The number of colors (and pos if not NULL) entries + @param mode The tiling mode + */ + static sk_sp MakeRadial(const SkPoint& center, SkScalar radius, + const SkColor colors[], const SkScalar pos[], int count, + SkTileMode mode, + uint32_t flags = 0, const SkMatrix* localMatrix = nullptr); + + /** Returns a shader that generates a radial gradient given the center and radius. +

+ @param center The center of the circle for this gradient + @param radius Must be positive. The radius of the circle for this gradient + @param colors The array[count] of colors, to be distributed between the center and edge of the circle + @param pos May be NULL. The array[count] of SkScalars, or NULL, of the relative position of + each corresponding color in the colors array. If this is NULL, + the the colors are distributed evenly between the center and edge of the circle. + If this is not null, the values must lie between 0.0 and 1.0, and be + strictly increasing. If the first value is not 0.0, then an additional + color stop is added at position 0.0, with the same color as colors[0]. + If the the last value is not 1.0, then an additional color stop is added + at position 1.0, with the same color as colors[count - 1]. + @param count Must be >= 2. The number of colors (and pos if not NULL) entries + @param mode The tiling mode + */ + static sk_sp MakeRadial(const SkPoint& center, SkScalar radius, + const SkColor4f colors[], sk_sp colorSpace, + const SkScalar pos[], int count, SkTileMode mode, + const Interpolation& interpolation, + const SkMatrix* localMatrix); + static sk_sp MakeRadial(const SkPoint& center, SkScalar radius, + const SkColor4f colors[], sk_sp colorSpace, + const SkScalar pos[], int count, SkTileMode mode, + uint32_t flags = 0, const SkMatrix* localMatrix = nullptr) { + return MakeRadial(center, radius, colors, std::move(colorSpace), pos, count, mode, + Interpolation::FromFlags(flags), localMatrix); + } + + /** + * Returns a shader that generates a conical gradient given two circles, or + * returns NULL if the inputs are invalid. The gradient interprets the + * two circles according to the following HTML spec. + * http://dev.w3.org/html5/2dcontext/#dom-context-2d-createradialgradient + */ + static sk_sp MakeTwoPointConical(const SkPoint& start, SkScalar startRadius, + const SkPoint& end, SkScalar endRadius, + const SkColor colors[], const SkScalar pos[], + int count, SkTileMode mode, + uint32_t flags = 0, + const SkMatrix* localMatrix = nullptr); + + /** + * Returns a shader that generates a conical gradient given two circles, or + * returns NULL if the inputs are invalid. The gradient interprets the + * two circles according to the following HTML spec. + * http://dev.w3.org/html5/2dcontext/#dom-context-2d-createradialgradient + */ + static sk_sp MakeTwoPointConical(const SkPoint& start, SkScalar startRadius, + const SkPoint& end, SkScalar endRadius, + const SkColor4f colors[], + sk_sp colorSpace, const SkScalar pos[], + int count, SkTileMode mode, + const Interpolation& interpolation, + const SkMatrix* localMatrix); + static sk_sp MakeTwoPointConical(const SkPoint& start, SkScalar startRadius, + const SkPoint& end, SkScalar endRadius, + const SkColor4f colors[], + sk_sp colorSpace, const SkScalar pos[], + int count, SkTileMode mode, + uint32_t flags = 0, + const SkMatrix* localMatrix = nullptr) { + return MakeTwoPointConical(start, startRadius, end, endRadius, colors, + std::move(colorSpace), pos, count, mode, + Interpolation::FromFlags(flags), localMatrix); + } + + /** Returns a shader that generates a sweep gradient given a center. + + The shader accepts negative angles and angles larger than 360, draws + between 0 and 360 degrees, similar to the CSS conic-gradient + semantics. 0 degrees means horizontal positive x axis. The start angle + must be less than the end angle, otherwise a null pointer is + returned. If color stops do not contain 0 and 1 but are within this + range, the respective outer color stop is repeated for 0 and 1. Color + stops less than 0 are clamped to 0, and greater than 1 are clamped to 1. +

+ @param cx The X coordinate of the center of the sweep + @param cx The Y coordinate of the center of the sweep + @param colors The array[count] of colors, to be distributed around the center, within + the gradient angle range. + @param pos May be NULL. The array[count] of SkScalars, or NULL, of the relative + position of each corresponding color in the colors array. If this is + NULL, then the colors are distributed evenly within the angular range. + If this is not null, the values must lie between 0.0 and 1.0, and be + strictly increasing. If the first value is not 0.0, then an additional + color stop is added at position 0.0, with the same color as colors[0]. + If the the last value is not 1.0, then an additional color stop is added + at position 1.0, with the same color as colors[count - 1]. + @param count Must be >= 2. The number of colors (and pos if not NULL) entries + @param mode Tiling mode: controls drawing outside of the gradient angular range. + @param startAngle Start of the angular range, corresponding to pos == 0. + @param endAngle End of the angular range, corresponding to pos == 1. + */ + static sk_sp MakeSweep(SkScalar cx, SkScalar cy, + const SkColor colors[], const SkScalar pos[], int count, + SkTileMode mode, + SkScalar startAngle, SkScalar endAngle, + uint32_t flags, const SkMatrix* localMatrix); + static sk_sp MakeSweep(SkScalar cx, SkScalar cy, + const SkColor colors[], const SkScalar pos[], int count, + uint32_t flags = 0, const SkMatrix* localMatrix = nullptr) { + return MakeSweep(cx, cy, colors, pos, count, SkTileMode::kClamp, 0, 360, flags, + localMatrix); + } + + /** Returns a shader that generates a sweep gradient given a center. + + The shader accepts negative angles and angles larger than 360, draws + between 0 and 360 degrees, similar to the CSS conic-gradient + semantics. 0 degrees means horizontal positive x axis. The start angle + must be less than the end angle, otherwise a null pointer is + returned. If color stops do not contain 0 and 1 but are within this + range, the respective outer color stop is repeated for 0 and 1. Color + stops less than 0 are clamped to 0, and greater than 1 are clamped to 1. +

+ @param cx The X coordinate of the center of the sweep + @param cx The Y coordinate of the center of the sweep + @param colors The array[count] of colors, to be distributed around the center, within + the gradient angle range. + @param pos May be NULL. The array[count] of SkScalars, or NULL, of the relative + position of each corresponding color in the colors array. If this is + NULL, then the colors are distributed evenly within the angular range. + If this is not null, the values must lie between 0.0 and 1.0, and be + strictly increasing. If the first value is not 0.0, then an additional + color stop is added at position 0.0, with the same color as colors[0]. + If the the last value is not 1.0, then an additional color stop is added + at position 1.0, with the same color as colors[count - 1]. + @param count Must be >= 2. The number of colors (and pos if not NULL) entries + @param mode Tiling mode: controls drawing outside of the gradient angular range. + @param startAngle Start of the angular range, corresponding to pos == 0. + @param endAngle End of the angular range, corresponding to pos == 1. + */ + static sk_sp MakeSweep(SkScalar cx, SkScalar cy, + const SkColor4f colors[], sk_sp colorSpace, + const SkScalar pos[], int count, + SkTileMode mode, + SkScalar startAngle, SkScalar endAngle, + const Interpolation& interpolation, + const SkMatrix* localMatrix); + static sk_sp MakeSweep(SkScalar cx, SkScalar cy, + const SkColor4f colors[], sk_sp colorSpace, + const SkScalar pos[], int count, + SkTileMode mode, + SkScalar startAngle, SkScalar endAngle, + uint32_t flags, const SkMatrix* localMatrix) { + return MakeSweep(cx, cy, colors, std::move(colorSpace), pos, count, mode, startAngle, + endAngle, Interpolation::FromFlags(flags), localMatrix); + } + static sk_sp MakeSweep(SkScalar cx, SkScalar cy, + const SkColor4f colors[], sk_sp colorSpace, + const SkScalar pos[], int count, + uint32_t flags = 0, const SkMatrix* localMatrix = nullptr) { + return MakeSweep(cx, cy, colors, std::move(colorSpace), pos, count, SkTileMode::kClamp, + 0, 360, flags, localMatrix); + } +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkHighContrastFilter.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkHighContrastFilter.h new file mode 100644 index 0000000000..6badf8486e --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkHighContrastFilter.h @@ -0,0 +1,84 @@ +/* +* 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 SkHighContrastFilter_DEFINED +#define SkHighContrastFilter_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" + +class SkColorFilter; + +/** + * Configuration struct for SkHighContrastFilter. + * + * Provides transformations to improve contrast for users with low vision. + */ +struct SkHighContrastConfig { + enum class InvertStyle { + kNoInvert, + kInvertBrightness, + kInvertLightness, + + kLast = kInvertLightness + }; + + SkHighContrastConfig() { + fGrayscale = false; + fInvertStyle = InvertStyle::kNoInvert; + fContrast = 0.0f; + } + + SkHighContrastConfig(bool grayscale, + InvertStyle invertStyle, + SkScalar contrast) + : fGrayscale(grayscale) + , fInvertStyle(invertStyle) + , fContrast(contrast) {} + + // Returns true if all of the fields are set within the valid range. + bool isValid() const { + return fInvertStyle >= InvertStyle::kNoInvert && + fInvertStyle <= InvertStyle::kInvertLightness && + fContrast >= -1.0 && + fContrast <= 1.0; + } + + // If true, the color will be converted to grayscale. + bool fGrayscale; + + // Whether to invert brightness, lightness, or neither. + InvertStyle fInvertStyle; + + // After grayscale and inverting, the contrast can be adjusted linearly. + // The valid range is -1.0 through 1.0, where 0.0 is no adjustment. + SkScalar fContrast; +}; + +/** + * Color filter that provides transformations to improve contrast + * for users with low vision. + * + * Applies the following transformations in this order. Each of these + * can be configured using SkHighContrastConfig. + * + * - Conversion to grayscale + * - Color inversion (either in RGB or HSL space) + * - Increasing the resulting contrast. + * + * Calling SkHighContrastFilter::Make will return nullptr if the config is + * not valid, e.g. if you try to call it with a contrast outside the range of + * -1.0 to 1.0. + */ + +struct SK_API SkHighContrastFilter { + // Returns the filter, or nullptr if the config is invalid. + static sk_sp Make(const SkHighContrastConfig& config); +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkImageFilters.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkImageFilters.h new file mode 100644 index 0000000000..926896f9ed --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkImageFilters.h @@ -0,0 +1,615 @@ +/* + * Copyright 2019 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkImageFilters_DEFINED +#define SkImageFilters_DEFINED + +#include "include/core/SkColor.h" +#include "include/core/SkImage.h" +#include "include/core/SkImageFilter.h" +#include "include/core/SkPicture.h" +#include "include/core/SkRect.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkScalar.h" +#include "include/core/SkShader.h" +#include "include/core/SkTileMode.h" +#include "include/core/SkTypes.h" + +#include +#include +#include +#include + +class SkBlender; +class SkColorFilter; +class SkMatrix; +class SkRuntimeShaderBuilder; +enum class SkBlendMode; +struct SkIPoint; +struct SkISize; +struct SkPoint3; +struct SkSamplingOptions; + +// A set of factory functions providing useful SkImageFilter effects. For image filters that take an +// input filter, providing nullptr means it will automatically use the dynamic source image. This +// source depends on how the filter is applied, but is either the contents of a saved layer when +// drawing with SkCanvas, or an explicit SkImage if using one of the SkImages::MakeWithFilter +// factories. +class SK_API SkImageFilters { +public: + // This is just a convenience type to allow passing SkIRects, SkRects, and optional pointers + // to those types as a crop rect for the image filter factories. It's not intended to be used + // directly. + struct CropRect : public std::optional { + CropRect() {} + // Intentionally not explicit so callers don't have to use this type but can use SkIRect or + // SkRect as desired. + CropRect(const SkIRect& crop) : std::optional(SkRect::Make(crop)) {} + CropRect(const SkRect& crop) : std::optional(crop) {} + CropRect(const std::optional& crop) : std::optional(crop) {} + CropRect(const std::nullopt_t&) : std::optional() {} + + // Backwards compatibility for when the APIs used to explicitly accept "const SkRect*" + CropRect(std::nullptr_t) {} + CropRect(const SkIRect* optionalCrop) { + if (optionalCrop) { + *this = SkRect::Make(*optionalCrop); + } + } + CropRect(const SkRect* optionalCrop) { + if (optionalCrop) { + *this = *optionalCrop; + } + } + + // std::optional doesn't define == when comparing to another optional... + bool operator==(const CropRect& o) const { + return this->has_value() == o.has_value() && + (!this->has_value() || this->value() == *o); + } + }; + + /** + * Create a filter that implements a custom blend mode. Each output pixel is the result of + * combining the corresponding background and foreground pixels using the 4 coefficients: + * k1 * foreground * background + k2 * foreground + k3 * background + k4 + * @param k1, k2, k3, k4 The four coefficients used to combine the foreground and background. + * @param enforcePMColor If true, the RGB channels will be clamped to the calculated alpha. + * @param background The background content, using the source bitmap when this is null. + * @param foreground The foreground content, using the source bitmap when this is null. + * @param cropRect Optional rectangle that crops the inputs and output. + */ + static sk_sp Arithmetic(SkScalar k1, SkScalar k2, SkScalar k3, SkScalar k4, + bool enforcePMColor, sk_sp background, + sk_sp foreground, + const CropRect& cropRect = {}); + + /** + * This filter takes an SkBlendMode and uses it to composite the two filters together. + * @param mode The blend mode that defines the compositing operation + * @param background The Dst pixels used in blending, if null the source bitmap is used. + * @param foreground The Src pixels used in blending, if null the source bitmap is used. + * @cropRect Optional rectangle to crop input and output. + */ + static sk_sp Blend(SkBlendMode mode, sk_sp background, + sk_sp foreground = nullptr, + const CropRect& cropRect = {}); + + /** + * This filter takes an SkBlendMode and uses it to composite the two filters together. + * @param blender The blender that defines the compositing operation + * @param background The Dst pixels used in blending, if null the source bitmap is used. + * @param foreground The Src pixels used in blending, if null the source bitmap is used. + * @cropRect Optional rectangle to crop input and output. + */ + static sk_sp Blend(sk_sp blender, sk_sp background, + sk_sp foreground = nullptr, + const CropRect& cropRect = {}); + + /** + * Create a filter that blurs its input by the separate X and Y sigmas. The provided tile mode + * is used when the blur kernel goes outside the input image. + * @param sigmaX The Gaussian sigma value for blurring along the X axis. + * @param sigmaY The Gaussian sigma value for blurring along the Y axis. + * @param tileMode The tile mode applied at edges . + * TODO (michaelludwig) - kMirror is not supported yet + * @param input The input filter that is blurred, uses source bitmap if this is null. + * @param cropRect Optional rectangle that crops the input and output. + */ + static sk_sp Blur(SkScalar sigmaX, SkScalar sigmaY, SkTileMode tileMode, + sk_sp input, const CropRect& cropRect = {}); + // As above, but defaults to the decal tile mode. + static sk_sp Blur(SkScalar sigmaX, SkScalar sigmaY, sk_sp input, + const CropRect& cropRect = {}) { + return Blur(sigmaX, sigmaY, SkTileMode::kDecal, std::move(input), cropRect); + } + + /** + * Create a filter that applies the color filter to the input filter results. + * @param cf The color filter that transforms the input image. + * @param input The input filter, or uses the source bitmap if this is null. + * @param cropRect Optional rectangle that crops the input and output. + */ + static sk_sp ColorFilter(sk_sp cf, sk_sp input, + const CropRect& cropRect = {}); + + /** + * Create a filter that composes 'inner' with 'outer', such that the results of 'inner' are + * treated as the source bitmap passed to 'outer', i.e. result = outer(inner(source)). + * @param outer The outer filter that evaluates the results of inner. + * @param inner The inner filter that produces the input to outer. + */ + static sk_sp Compose(sk_sp outer, sk_sp inner); + + /** + * Create a filter that applies a crop to the result of the 'input' filter. Pixels within the + * crop rectangle are unmodified from what 'input' produced. Pixels outside of crop match the + * provided SkTileMode (defaulting to kDecal). + * + * NOTE: The optional CropRect argument for many of the factories is equivalent to creating the + * filter without a CropRect and then wrapping it in ::Crop(rect, kDecal). Explicitly adding + * Crop filters lets you control their tiling and use different geometry for the input and the + * output of another filter. + * + * @param rect The cropping geometry + * @param tileMode The tilemode applied to pixels *outside* of 'crop' + * @param input The input filter that is cropped, uses source image if this is null + */ + static sk_sp Crop(const SkRect& rect, + SkTileMode tileMode, + sk_sp input); + static sk_sp Crop(const SkRect& rect, sk_sp input) { + return Crop(rect, SkTileMode::kDecal, std::move(input)); + } + + /** + * Create a filter that moves each pixel in its color input based on an (x,y) vector encoded + * in its displacement input filter. Two color components of the displacement image are + * mapped into a vector as scale * (color[xChannel], color[yChannel]), where the channel + * selectors are one of R, G, B, or A. + * @param xChannelSelector RGBA channel that encodes the x displacement per pixel. + * @param yChannelSelector RGBA channel that encodes the y displacement per pixel. + * @param scale Scale applied to displacement extracted from image. + * @param displacement The filter defining the displacement image, or null to use source. + * @param color The filter providing the color pixels to be displaced. If null, + * it will use the source. + * @param cropRect Optional rectangle that crops the color input and output. + */ + static sk_sp DisplacementMap(SkColorChannel xChannelSelector, + SkColorChannel yChannelSelector, + SkScalar scale, sk_sp displacement, + sk_sp color, + const CropRect& cropRect = {}); + + /** + * Create a filter that draws a drop shadow under the input content. This filter produces an + * image that includes the inputs' content. + * @param dx The X offset of the shadow. + * @param dy The Y offset of the shadow. + * @param sigmaX The blur radius for the shadow, along the X axis. + * @param sigmaY The blur radius for the shadow, along the Y axis. + * @param color The color of the drop shadow. + * @param input The input filter, or will use the source bitmap if this is null. + * @param cropRect Optional rectangle that crops the input and output. + */ + static sk_sp DropShadow(SkScalar dx, SkScalar dy, + SkScalar sigmaX, SkScalar sigmaY, + SkColor color, sk_sp input, + const CropRect& cropRect = {}); + /** + * Create a filter that renders a drop shadow, in exactly the same manner as ::DropShadow, + * except that the resulting image does not include the input content. This allows the shadow + * and input to be composed by a filter DAG in a more flexible manner. + * @param dx The X offset of the shadow. + * @param dy The Y offset of the shadow. + * @param sigmaX The blur radius for the shadow, along the X axis. + * @param sigmaY The blur radius for the shadow, along the Y axis. + * @param color The color of the drop shadow. + * @param input The input filter, or will use the source bitmap if this is null. + * @param cropRect Optional rectangle that crops the input and output. + */ + static sk_sp DropShadowOnly(SkScalar dx, SkScalar dy, + SkScalar sigmaX, SkScalar sigmaY, + SkColor color, sk_sp input, + const CropRect& cropRect = {}); + + /** + * Create a filter that always produces transparent black. + */ + static sk_sp Empty(); + + /** + * Create a filter that draws the 'srcRect' portion of image into 'dstRect' using the given + * filter quality. Similar to SkCanvas::drawImageRect. The returned image filter evaluates + * to transparent black if 'image' is null. + * + * @param image The image that is output by the filter, subset by 'srcRect'. + * @param srcRect The source pixels sampled into 'dstRect' + * @param dstRect The local rectangle to draw the image into. + * @param sampling The sampling to use when drawing the image. + */ + static sk_sp Image(sk_sp image, const SkRect& srcRect, + const SkRect& dstRect, const SkSamplingOptions& sampling); + + /** + * Create a filter that draws the image using the given sampling. + * Similar to SkCanvas::drawImage. The returned image filter evaluates to transparent black if + * 'image' is null. + * + * @param image The image that is output by the filter. + * @param sampling The sampling to use when drawing the image. + */ + static sk_sp Image(sk_sp image, const SkSamplingOptions& sampling) { + if (image) { + SkRect r = SkRect::Make(image->bounds()); + return Image(std::move(image), r, r, sampling); + } else { + return nullptr; + } + } + + /** + * Create a filter that fills 'lensBounds' with a magnification of the input. + * + * @param lensBounds The outer bounds of the magnifier effect + * @param zoomAmount The amount of magnification applied to the input image + * @param inset The size or width of the fish-eye distortion around the magnified content + * @param sampling The SkSamplingOptions applied to the input image when magnified + * @param input The input filter that is magnified; if null the source bitmap is used + * @param cropRect Optional rectangle that crops the input and output. + */ + static sk_sp Magnifier(const SkRect& lensBounds, + SkScalar zoomAmount, + SkScalar inset, + const SkSamplingOptions& sampling, + sk_sp input, + const CropRect& cropRect = {}); + + /** + * Create a filter that applies an NxM image processing kernel to the input image. This can be + * used to produce effects such as sharpening, blurring, edge detection, etc. + * @param kernelSize The kernel size in pixels, in each dimension (N by M). + * @param kernel The image processing kernel. Must contain N * M elements, in row order. + * @param gain A scale factor applied to each pixel after convolution. This can be + * used to normalize the kernel, if it does not already sum to 1. + * @param bias A bias factor added to each pixel after convolution. + * @param kernelOffset An offset applied to each pixel coordinate before convolution. + * This can be used to center the kernel over the image + * (e.g., a 3x3 kernel should have an offset of {1, 1}). + * @param tileMode How accesses outside the image are treated. + * TODO (michaelludwig) - kMirror is not supported yet + * @param convolveAlpha If true, all channels are convolved. If false, only the RGB channels + * are convolved, and alpha is copied from the source image. + * @param input The input image filter, if null the source bitmap is used instead. + * @param cropRect Optional rectangle to which the output processing will be limited. + */ + static sk_sp MatrixConvolution(const SkISize& kernelSize, + const SkScalar kernel[], SkScalar gain, + SkScalar bias, const SkIPoint& kernelOffset, + SkTileMode tileMode, bool convolveAlpha, + sk_sp input, + const CropRect& cropRect = {}); + + /** + * Create a filter that transforms the input image by 'matrix'. This matrix transforms the + * local space, which means it effectively happens prior to any transformation coming from the + * SkCanvas initiating the filtering. + * @param matrix The matrix to apply to the original content. + * @param sampling How the image will be sampled when it is transformed + * @param input The image filter to transform, or null to use the source image. + */ + static sk_sp MatrixTransform(const SkMatrix& matrix, + const SkSamplingOptions& sampling, + sk_sp input); + + /** + * Create a filter that merges the 'count' filters together by drawing their results in order + * with src-over blending. + * @param filters The input filter array to merge, which must have 'count' elements. Any null + * filter pointers will use the source bitmap instead. + * @param count The number of input filters to be merged. + * @param cropRect Optional rectangle that crops all input filters and the output. + */ + static sk_sp Merge(sk_sp* const filters, int count, + const CropRect& cropRect = {}); + /** + * Create a filter that merges the results of the two filters together with src-over blending. + * @param first The first input filter, or the source bitmap if this is null. + * @param second The second input filter, or the source bitmap if this null. + * @param cropRect Optional rectangle that crops the inputs and output. + */ + static sk_sp Merge(sk_sp first, sk_sp second, + const CropRect& cropRect = {}) { + sk_sp array[] = { std::move(first), std::move(second) }; + return Merge(array, 2, cropRect); + } + + /** + * Create a filter that offsets the input filter by the given vector. + * @param dx The x offset in local space that the image is shifted. + * @param dy The y offset in local space that the image is shifted. + * @param input The input that will be moved, if null the source bitmap is used instead. + * @param cropRect Optional rectangle to crop the input and output. + */ + static sk_sp Offset(SkScalar dx, SkScalar dy, sk_sp input, + const CropRect& cropRect = {}); + + /** + * Create a filter that produces the SkPicture as its output, clipped to both 'targetRect' and + * the picture's internal cull rect. + * + * If 'pic' is null, the returned image filter produces transparent black. + * + * @param pic The picture that is drawn for the filter output. + * @param targetRect The drawing region for the picture. + */ + static sk_sp Picture(sk_sp pic, const SkRect& targetRect); + // As above, but uses SkPicture::cullRect for the drawing region. + static sk_sp Picture(sk_sp pic) { + SkRect target = pic ? pic->cullRect() : SkRect::MakeEmpty(); + return Picture(std::move(pic), target); + } + + /** + * Create a filter that fills the output with the per-pixel evaluation of the SkShader produced + * by the SkRuntimeShaderBuilder. The shader is defined in the image filter's local coordinate + * system, so it will automatically be affected by SkCanvas' transform. + * + * This variant assumes that the runtime shader samples 'childShaderName' with the same input + * coordinate passed to to shader. + * + * This requires a GPU backend or SkSL to be compiled in. + * + * @param builder The builder used to produce the runtime shader, that will in turn + * fill the result image + * @param childShaderName The name of the child shader defined in the builder that will be + * bound to the input param (or the source image if the input param + * is null). If empty, the builder can have exactly one child shader, + * which automatically binds the input param. + * @param input The image filter that will be provided as input to the runtime + * shader. If null the implicit source image is used instead + */ + static sk_sp RuntimeShader(const SkRuntimeShaderBuilder& builder, + std::string_view childShaderName, + sk_sp input) { + return RuntimeShader(builder, /*sampleRadius=*/0.f, childShaderName, std::move(input)); + } + + /** + * As above, but 'sampleRadius' defines the sampling radius of 'childShaderName' relative to + * the runtime shader produced by 'builder'. If greater than 0, the coordinate passed to + * childShader.eval() will be up to 'sampleRadius' away (maximum absolute offset in 'x' or 'y') + * from the coordinate passed into the runtime shader. + * + * This allows Skia to provide sampleable values for the image filter without worrying about + * boundary conditions. + * + * This requires a GPU backend or SkSL to be compiled in. + */ + static sk_sp RuntimeShader(const SkRuntimeShaderBuilder& builder, + SkScalar sampleRadius, + std::string_view childShaderName, + sk_sp input); + + /** + * Create a filter that fills the output with the per-pixel evaluation of the SkShader produced + * by the SkRuntimeShaderBuilder. The shader is defined in the image filter's local coordinate + * system, so it will automatically be affected by SkCanvas' transform. + * + * This requires a GPU backend or SkSL to be compiled in. + * + * @param builder The builder used to produce the runtime shader, that will in turn + * fill the result image + * @param childShaderNames The names of the child shaders defined in the builder that will be + * bound to the input params (or the source image if the input param + * is null). If any name is null, or appears more than once, factory + * fails and returns nullptr. + * @param inputs The image filters that will be provided as input to the runtime + * shader. If any are null, the implicit source image is used instead. + * @param inputCount How many entries are present in 'childShaderNames' and 'inputs'. + */ + static sk_sp RuntimeShader(const SkRuntimeShaderBuilder& builder, + std::string_view childShaderNames[], + const sk_sp inputs[], + int inputCount) { + return RuntimeShader(builder, /*maxSampleRadius=*/0.f, childShaderNames, + inputs, inputCount); + } + + /** + * As above, but 'maxSampleRadius' defines the sampling limit on coordinates provided to all + * child shaders. Like the single-child variant with a sample radius, this can be used to + * inform Skia that the runtime shader guarantees that all dynamic children (defined in + * childShaderNames) will be evaluated with coordinates at most 'maxSampleRadius' away from the + * coordinate provided to the runtime shader itself. + * + * This requires a GPU backend or SkSL to be compiled in. + */ + static sk_sp RuntimeShader(const SkRuntimeShaderBuilder& builder, + SkScalar maxSampleRadius, + std::string_view childShaderNames[], + const sk_sp inputs[], + int inputCount); + + enum class Dither : bool { + kNo = false, + kYes = true + }; + + /** + * Create a filter that fills the output with the per-pixel evaluation of the SkShader. The + * shader is defined in the image filter's local coordinate system, so will automatically + * be affected by SkCanvas' transform. + * + * Like Image() and Picture(), this is a leaf filter that can be used to introduce inputs to + * a complex filter graph, but should generally be combined with a filter that as at least + * one null input to use the implicit source image. + * + * Returns an image filter that evaluates to transparent black if 'shader' is null. + * + * @param shader The shader that fills the result image + */ + static sk_sp Shader(sk_sp shader, const CropRect& cropRect = {}) { + return Shader(std::move(shader), Dither::kNo, cropRect); + } + static sk_sp Shader(sk_sp shader, Dither dither, + const CropRect& cropRect = {}); + + /** + * Create a tile image filter. + * @param src Defines the pixels to tile + * @param dst Defines the pixel region that the tiles will be drawn to + * @param input The input that will be tiled, if null the source bitmap is used instead. + */ + static sk_sp Tile(const SkRect& src, const SkRect& dst, + sk_sp input); + + // Morphology filter effects + + /** + * Create a filter that dilates each input pixel's channel values to the max value within the + * given radii along the x and y axes. + * @param radiusX The distance to dilate along the x axis to either side of each pixel. + * @param radiusY The distance to dilate along the y axis to either side of each pixel. + * @param input The image filter that is dilated, using source bitmap if this is null. + * @param cropRect Optional rectangle that crops the input and output. + */ + static sk_sp Dilate(SkScalar radiusX, SkScalar radiusY, + sk_sp input, + const CropRect& cropRect = {}); + + /** + * Create a filter that erodes each input pixel's channel values to the minimum channel value + * within the given radii along the x and y axes. + * @param radiusX The distance to erode along the x axis to either side of each pixel. + * @param radiusY The distance to erode along the y axis to either side of each pixel. + * @param input The image filter that is eroded, using source bitmap if this is null. + * @param cropRect Optional rectangle that crops the input and output. + */ + static sk_sp Erode(SkScalar radiusX, SkScalar radiusY, + sk_sp input, + const CropRect& cropRect = {}); + + // Lighting filter effects + + /** + * Create a filter that calculates the diffuse illumination from a distant light source, + * interpreting the alpha channel of the input as the height profile of the surface (to + * approximate normal vectors). + * @param direction The direction to the distance light. + * @param lightColor The color of the diffuse light source. + * @param surfaceScale Scale factor to transform from alpha values to physical height. + * @param kd Diffuse reflectance coefficient. + * @param input The input filter that defines surface normals (as alpha), or uses the + * source bitmap when null. + * @param cropRect Optional rectangle that crops the input and output. + */ + static sk_sp DistantLitDiffuse(const SkPoint3& direction, SkColor lightColor, + SkScalar surfaceScale, SkScalar kd, + sk_sp input, + const CropRect& cropRect = {}); + /** + * Create a filter that calculates the diffuse illumination from a point light source, using + * alpha channel of the input as the height profile of the surface (to approximate normal + * vectors). + * @param location The location of the point light. + * @param lightColor The color of the diffuse light source. + * @param surfaceScale Scale factor to transform from alpha values to physical height. + * @param kd Diffuse reflectance coefficient. + * @param input The input filter that defines surface normals (as alpha), or uses the + * source bitmap when null. + * @param cropRect Optional rectangle that crops the input and output. + */ + static sk_sp PointLitDiffuse(const SkPoint3& location, SkColor lightColor, + SkScalar surfaceScale, SkScalar kd, + sk_sp input, + const CropRect& cropRect = {}); + /** + * Create a filter that calculates the diffuse illumination from a spot light source, using + * alpha channel of the input as the height profile of the surface (to approximate normal + * vectors). The spot light is restricted to be within 'cutoffAngle' of the vector between + * the location and target. + * @param location The location of the spot light. + * @param target The location that the spot light is point towards + * @param falloffExponent Exponential falloff parameter for illumination outside of cutoffAngle + * @param cutoffAngle Maximum angle from lighting direction that receives full light + * @param lightColor The color of the diffuse light source. + * @param surfaceScale Scale factor to transform from alpha values to physical height. + * @param kd Diffuse reflectance coefficient. + * @param input The input filter that defines surface normals (as alpha), or uses the + * source bitmap when null. + * @param cropRect Optional rectangle that crops the input and output. + */ + static sk_sp SpotLitDiffuse(const SkPoint3& location, const SkPoint3& target, + SkScalar falloffExponent, SkScalar cutoffAngle, + SkColor lightColor, SkScalar surfaceScale, + SkScalar kd, sk_sp input, + const CropRect& cropRect = {}); + + /** + * Create a filter that calculates the specular illumination from a distant light source, + * interpreting the alpha channel of the input as the height profile of the surface (to + * approximate normal vectors). + * @param direction The direction to the distance light. + * @param lightColor The color of the specular light source. + * @param surfaceScale Scale factor to transform from alpha values to physical height. + * @param ks Specular reflectance coefficient. + * @param shininess The specular exponent determining how shiny the surface is. + * @param input The input filter that defines surface normals (as alpha), or uses the + * source bitmap when null. + * @param cropRect Optional rectangle that crops the input and output. + */ + static sk_sp DistantLitSpecular(const SkPoint3& direction, SkColor lightColor, + SkScalar surfaceScale, SkScalar ks, + SkScalar shininess, sk_sp input, + const CropRect& cropRect = {}); + /** + * Create a filter that calculates the specular illumination from a point light source, using + * alpha channel of the input as the height profile of the surface (to approximate normal + * vectors). + * @param location The location of the point light. + * @param lightColor The color of the specular light source. + * @param surfaceScale Scale factor to transform from alpha values to physical height. + * @param ks Specular reflectance coefficient. + * @param shininess The specular exponent determining how shiny the surface is. + * @param input The input filter that defines surface normals (as alpha), or uses the + * source bitmap when null. + * @param cropRect Optional rectangle that crops the input and output. + */ + static sk_sp PointLitSpecular(const SkPoint3& location, SkColor lightColor, + SkScalar surfaceScale, SkScalar ks, + SkScalar shininess, sk_sp input, + const CropRect& cropRect = {}); + /** + * Create a filter that calculates the specular illumination from a spot light source, using + * alpha channel of the input as the height profile of the surface (to approximate normal + * vectors). The spot light is restricted to be within 'cutoffAngle' of the vector between + * the location and target. + * @param location The location of the spot light. + * @param target The location that the spot light is point towards + * @param falloffExponent Exponential falloff parameter for illumination outside of cutoffAngle + * @param cutoffAngle Maximum angle from lighting direction that receives full light + * @param lightColor The color of the specular light source. + * @param surfaceScale Scale factor to transform from alpha values to physical height. + * @param ks Specular reflectance coefficient. + * @param shininess The specular exponent determining how shiny the surface is. + * @param input The input filter that defines surface normals (as alpha), or uses the + * source bitmap when null. + * @param cropRect Optional rectangle that crops the input and output. + */ + static sk_sp SpotLitSpecular(const SkPoint3& location, const SkPoint3& target, + SkScalar falloffExponent, SkScalar cutoffAngle, + SkColor lightColor, SkScalar surfaceScale, + SkScalar ks, SkScalar shininess, + sk_sp input, + const CropRect& cropRect = {}); + +private: + SkImageFilters() = delete; +}; + +#endif // SkImageFilters_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkLumaColorFilter.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkLumaColorFilter.h new file mode 100644 index 0000000000..41a9a45f3f --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkLumaColorFilter.h @@ -0,0 +1,37 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkLumaColorFilter_DEFINED +#define SkLumaColorFilter_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkTypes.h" + +class SkColorFilter; + +/** + * SkLumaColorFilter multiplies the luma of its input into the alpha channel, + * and sets the red, green, and blue channels to zero. + * + * SkLumaColorFilter(r,g,b,a) = {0,0,0, a * luma(r,g,b)} + * + * This is similar to a luminanceToAlpha feColorMatrix, + * but note how this filter folds in the previous alpha, + * something an feColorMatrix cannot do. + * + * feColorMatrix(luminanceToAlpha; r,g,b,a) = {0,0,0, luma(r,g,b)} + * + * (Despite its name, an feColorMatrix using luminanceToAlpha does + * actually compute luma, a dot-product of gamma-encoded color channels, + * not luminance, a dot-product of linear color channels. So at least + * SkLumaColorFilter and feColorMatrix+luminanceToAlpha agree there.) + */ +struct SK_API SkLumaColorFilter { + static sk_sp Make(); +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkOverdrawColorFilter.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkOverdrawColorFilter.h new file mode 100644 index 0000000000..5f1642483a --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkOverdrawColorFilter.h @@ -0,0 +1,32 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "include/core/SkColor.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkTypes.h" + +class SkColorFilter; + +#ifndef SkOverdrawColorFilter_DEFINED +#define SkOverdrawColorFilter_DEFINED + +/** + * Uses the value in the src alpha channel to set the dst pixel. + * 0 -> colors[0] + * 1 -> colors[1] + * ... + * 5 (or larger) -> colors[5] + * + */ +class SK_API SkOverdrawColorFilter { +public: + static constexpr int kNumColors = 6; + + static sk_sp MakeWithSkColors(const SkColor[kNumColors]); +}; + +#endif // SkOverdrawColorFilter_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkPerlinNoiseShader.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkPerlinNoiseShader.h new file mode 100644 index 0000000000..7ef2f1fc50 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkPerlinNoiseShader.h @@ -0,0 +1,53 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkPerlinNoiseShader_DEFINED +#define SkPerlinNoiseShader_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkScalar.h" +#include "include/core/SkShader.h" // IWYU pragma: keep +#include "include/private/base/SkAPI.h" + +struct SkISize; + +/** \class SkPerlinNoiseShader + + SkPerlinNoiseShader creates an image using the Perlin turbulence function. + + It can produce tileable noise if asked to stitch tiles and provided a tile size. + In order to fill a large area with repeating noise, set the stitchTiles flag to + true, and render exactly a single tile of noise. Without this flag, the result + will contain visible seams between tiles. + + The algorithm used is described here : + http://www.w3.org/TR/SVG/filters.html#feTurbulenceElement +*/ +namespace SkShaders { +/** + * This will construct Perlin noise of the given type (Fractal Noise or Turbulence). + * + * Both base frequencies (X and Y) have a usual range of (0..1) and must be non-negative. + * + * The number of octaves provided should be fairly small, with a limit of 255 enforced. + * Each octave doubles the frequency, so 10 octaves would produce noise from + * baseFrequency * 1, * 2, * 4, ..., * 512, which quickly yields insignificantly small + * periods and resembles regular unstructured noise rather than Perlin noise. + * + * If tileSize isn't NULL or an empty size, the tileSize parameter will be used to modify + * the frequencies so that the noise will be tileable for the given tile size. If tileSize + * is NULL or an empty size, the frequencies will be used as is without modification. + */ +SK_API sk_sp MakeFractalNoise(SkScalar baseFrequencyX, SkScalar baseFrequencyY, + int numOctaves, SkScalar seed, + const SkISize* tileSize = nullptr); +SK_API sk_sp MakeTurbulence(SkScalar baseFrequencyX, SkScalar baseFrequencyY, + int numOctaves, SkScalar seed, + const SkISize* tileSize = nullptr); +} // namespace SkShaders + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkRuntimeEffect.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkRuntimeEffect.h new file mode 100644 index 0000000000..d26e64bb40 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkRuntimeEffect.h @@ -0,0 +1,517 @@ +/* + * Copyright 2019 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkRuntimeEffect_DEFINED +#define SkRuntimeEffect_DEFINED + +#include "include/core/SkBlender.h" // IWYU pragma: keep +#include "include/core/SkColorFilter.h" // IWYU pragma: keep +#include "include/core/SkData.h" +#include "include/core/SkFlattenable.h" +#include "include/core/SkMatrix.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkShader.h" +#include "include/core/SkSpan.h" +#include "include/core/SkString.h" +#include "include/core/SkTypes.h" +#include "include/private/SkSLSampleUsage.h" +#include "include/private/base/SkOnce.h" +#include "include/private/base/SkTemplates.h" +#include "include/private/base/SkTo.h" +#include "include/private/base/SkTypeTraits.h" +#include "include/sksl/SkSLDebugTrace.h" +#include "include/sksl/SkSLVersion.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct SkIPoint; + +namespace SkSL { +class DebugTracePriv; +class FunctionDefinition; +struct Program; +enum class ProgramKind : int8_t; +struct ProgramSettings; +} // namespace SkSL + +namespace SkSL::RP { +class Program; +} + +/* + * SkRuntimeEffect supports creating custom SkShader and SkColorFilter objects using Skia's SkSL + * shading language. + * + * NOTE: This API is experimental and subject to change. + */ +class SK_API SkRuntimeEffect : public SkRefCnt { +public: + // Reflected description of a uniform variable in the effect's SkSL + struct SK_API Uniform { + enum class Type { + kFloat, + kFloat2, + kFloat3, + kFloat4, + kFloat2x2, + kFloat3x3, + kFloat4x4, + kInt, + kInt2, + kInt3, + kInt4, + }; + + enum Flags { + // Uniform is declared as an array. 'count' contains array length. + kArray_Flag = 0x1, + + // Uniform is declared with layout(color). Colors should be supplied as unpremultiplied, + // extended-range (unclamped) sRGB (ie SkColor4f). The uniform will be automatically + // transformed to unpremultiplied extended-range working-space colors. + kColor_Flag = 0x2, + + // When used with SkMeshSpecification, indicates that the uniform is present in the + // vertex shader. Not used with SkRuntimeEffect. + kVertex_Flag = 0x4, + + // When used with SkMeshSpecification, indicates that the uniform is present in the + // fragment shader. Not used with SkRuntimeEffect. + kFragment_Flag = 0x8, + + // This flag indicates that the SkSL uniform uses a medium-precision type + // (i.e., `half` instead of `float`). + kHalfPrecision_Flag = 0x10, + }; + + std::string_view name; + size_t offset; + Type type; + int count; + uint32_t flags; + + bool isArray() const { return SkToBool(this->flags & kArray_Flag); } + bool isColor() const { return SkToBool(this->flags & kColor_Flag); } + size_t sizeInBytes() const; + }; + + // Reflected description of a uniform child (shader or colorFilter) in the effect's SkSL + enum class ChildType { + kShader, + kColorFilter, + kBlender, + }; + + struct Child { + std::string_view name; + ChildType type; + int index; + }; + + class Options { + public: + // For testing purposes, disables optimization and inlining. (Normally, Runtime Effects + // don't run the inliner directly, but they still get an inlining pass once they are + // painted.) + bool forceUnoptimized = false; + + private: + friend class SkRuntimeEffect; + friend class SkRuntimeEffectPriv; + + // This flag allows Runtime Effects to access Skia implementation details like sk_FragCoord + // and functions with private identifiers (e.g. $rgb_to_hsl). + bool allowPrivateAccess = false; + // When not 0, this field allows Skia to assign a stable key to a known runtime effect + uint32_t fStableKey = 0; + + // TODO(skia:11209) - Replace this with a promised SkCapabilities? + // This flag lifts the ES2 restrictions on Runtime Effects that are gated by the + // `strictES2Mode` check. Be aware that the software renderer and pipeline-stage effect are + // still largely ES3-unaware and can still fail or crash if post-ES2 features are used. + // This is only intended for use by tests and certain internally created effects. + SkSL::Version maxVersionAllowed = SkSL::Version::k100; + }; + + // If the effect is compiled successfully, `effect` will be non-null. + // Otherwise, `errorText` will contain the reason for failure. + struct Result { + sk_sp effect; + SkString errorText; + }; + + // MakeForColorFilter and MakeForShader verify that the SkSL code is valid for those stages of + // the Skia pipeline. In all of the signatures described below, color parameters and return + // values are flexible. They are listed as being 'vec4', but they can also be 'half4' or + // 'float4'. ('vec4' is an alias for 'float4'). + + // We can't use a default argument for `options` due to a bug in Clang. + // https://bugs.llvm.org/show_bug.cgi?id=36684 + + // Color filter SkSL requires an entry point that looks like: + // vec4 main(vec4 inColor) { ... } + static Result MakeForColorFilter(SkString sksl, const Options&); + static Result MakeForColorFilter(SkString sksl) { + return MakeForColorFilter(std::move(sksl), Options{}); + } + + // Shader SkSL requires an entry point that looks like: + // vec4 main(vec2 inCoords) { ... } + static Result MakeForShader(SkString sksl, const Options&); + static Result MakeForShader(SkString sksl) { + return MakeForShader(std::move(sksl), Options{}); + } + + // Blend SkSL requires an entry point that looks like: + // vec4 main(vec4 srcColor, vec4 dstColor) { ... } + static Result MakeForBlender(SkString sksl, const Options&); + static Result MakeForBlender(SkString sksl) { + return MakeForBlender(std::move(sksl), Options{}); + } + + // Object that allows passing a SkShader, SkColorFilter or SkBlender as a child + class SK_API ChildPtr { + public: + ChildPtr() = default; + ChildPtr(sk_sp s) : fChild(std::move(s)) {} + ChildPtr(sk_sp cf) : fChild(std::move(cf)) {} + ChildPtr(sk_sp b) : fChild(std::move(b)) {} + + // Asserts that the flattenable is either null, or one of the legal derived types + ChildPtr(sk_sp f); + + std::optional type() const; + + SkShader* shader() const; + SkColorFilter* colorFilter() const; + SkBlender* blender() const; + SkFlattenable* flattenable() const { return fChild.get(); } + + using sk_is_trivially_relocatable = std::true_type; + + private: + sk_sp fChild; + + static_assert(::sk_is_trivially_relocatable::value); + }; + + sk_sp makeShader(sk_sp uniforms, + sk_sp children[], + size_t childCount, + const SkMatrix* localMatrix = nullptr) const; + sk_sp makeShader(sk_sp uniforms, + SkSpan children, + const SkMatrix* localMatrix = nullptr) const; + + sk_sp makeColorFilter(sk_sp uniforms) const; + sk_sp makeColorFilter(sk_sp uniforms, + sk_sp children[], + size_t childCount) const; + sk_sp makeColorFilter(sk_sp uniforms, + SkSpan children) const; + + sk_sp makeBlender(sk_sp uniforms, + SkSpan children = {}) const; + + /** + * Creates a new Runtime Effect patterned after an already-existing one. The new shader behaves + * like the original, but also creates a debug trace of its execution at the requested + * coordinate. After painting with this shader, the associated DebugTrace object will contain a + * shader execution trace. Call `writeTrace` on the debug trace object to generate a full trace + * suitable for a debugger, or call `dump` to emit a human-readable trace. + * + * Debug traces are only supported on a raster (non-GPU) canvas. + + * Debug traces are currently only supported on shaders. Color filter and blender tracing is a + * work-in-progress. + */ + struct TracedShader { + sk_sp shader; + sk_sp debugTrace; + }; + static TracedShader MakeTraced(sk_sp shader, const SkIPoint& traceCoord); + + // Returns the SkSL source of the runtime effect shader. + const std::string& source() const; + + // Combined size of all 'uniform' variables. When calling makeColorFilter or makeShader, + // provide an SkData of this size, containing values for all of those variables. + size_t uniformSize() const; + + SkSpan uniforms() const { return SkSpan(fUniforms); } + SkSpan children() const { return SkSpan(fChildren); } + + // Returns pointer to the named uniform variable's description, or nullptr if not found + const Uniform* findUniform(std::string_view name) const; + + // Returns pointer to the named child's description, or nullptr if not found + const Child* findChild(std::string_view name) const; + + // Allows the runtime effect type to be identified. + bool allowShader() const { return (fFlags & kAllowShader_Flag); } + bool allowColorFilter() const { return (fFlags & kAllowColorFilter_Flag); } + bool allowBlender() const { return (fFlags & kAllowBlender_Flag); } + + static void RegisterFlattenables(); + ~SkRuntimeEffect() override; + +private: + enum Flags { + kUsesSampleCoords_Flag = 0x001, + kAllowColorFilter_Flag = 0x002, + kAllowShader_Flag = 0x004, + kAllowBlender_Flag = 0x008, + kSamplesOutsideMain_Flag = 0x010, + kUsesColorTransform_Flag = 0x020, + kAlwaysOpaque_Flag = 0x040, + kAlphaUnchanged_Flag = 0x080, + kDisableOptimization_Flag = 0x100, + }; + + SkRuntimeEffect(std::unique_ptr baseProgram, + const Options& options, + const SkSL::FunctionDefinition& main, + std::vector&& uniforms, + std::vector&& children, + std::vector&& sampleUsages, + uint32_t flags); + + sk_sp makeUnoptimizedClone(); + + static Result MakeFromSource(SkString sksl, const Options& options, SkSL::ProgramKind kind); + + static Result MakeInternal(std::unique_ptr program, + const Options& options, + SkSL::ProgramKind kind); + + static SkSL::ProgramSettings MakeSettings(const Options& options); + + uint32_t hash() const { return fHash; } + bool usesSampleCoords() const { return (fFlags & kUsesSampleCoords_Flag); } + bool samplesOutsideMain() const { return (fFlags & kSamplesOutsideMain_Flag); } + bool usesColorTransform() const { return (fFlags & kUsesColorTransform_Flag); } + bool alwaysOpaque() const { return (fFlags & kAlwaysOpaque_Flag); } + bool isAlphaUnchanged() const { return (fFlags & kAlphaUnchanged_Flag); } + + const SkSL::RP::Program* getRPProgram(SkSL::DebugTracePriv* debugTrace) const; + + friend class GrSkSLFP; // usesColorTransform + friend class SkRuntimeShader; // fBaseProgram, fMain, fSampleUsages, getRPProgram() + friend class SkRuntimeBlender; // + friend class SkRuntimeColorFilter; // + + friend class SkRuntimeEffectPriv; + + uint32_t fHash; + uint32_t fStableKey; + + std::unique_ptr fBaseProgram; + std::unique_ptr fRPProgram; + mutable SkOnce fCompileRPProgramOnce; + const SkSL::FunctionDefinition& fMain; + std::vector fUniforms; + std::vector fChildren; + std::vector fSampleUsages; + + uint32_t fFlags; // Flags +}; + +/** Base class for SkRuntimeShaderBuilder, defined below. */ +class SkRuntimeEffectBuilder { +public: + struct BuilderUniform { + // Copy 'val' to this variable. No type conversion is performed - 'val' must be same + // size as expected by the effect. Information about the variable can be queried by + // looking at fVar. If the size is incorrect, no copy will be performed, and debug + // builds will abort. If this is the result of querying a missing variable, fVar will + // be nullptr, and assigning will also do nothing (and abort in debug builds). + template + std::enable_if_t::value, BuilderUniform&> operator=( + const T& val) { + if (!fVar) { + SkDEBUGFAIL("Assigning to missing variable"); + } else if (sizeof(val) != fVar->sizeInBytes()) { + SkDEBUGFAIL("Incorrect value size"); + } else { + memcpy(SkTAddOffset(fOwner->writableUniformData(), fVar->offset), + &val, sizeof(val)); + } + return *this; + } + + BuilderUniform& operator=(const SkMatrix& val) { + if (!fVar) { + SkDEBUGFAIL("Assigning to missing variable"); + } else if (fVar->sizeInBytes() != 9 * sizeof(float)) { + SkDEBUGFAIL("Incorrect value size"); + } else { + float* data = SkTAddOffset(fOwner->writableUniformData(), + (ptrdiff_t)fVar->offset); + data[0] = val.get(0); data[1] = val.get(3); data[2] = val.get(6); + data[3] = val.get(1); data[4] = val.get(4); data[5] = val.get(7); + data[6] = val.get(2); data[7] = val.get(5); data[8] = val.get(8); + } + return *this; + } + + template + bool set(const T val[], const int count) { + static_assert(std::is_trivially_copyable::value, "Value must be trivial copyable"); + if (!fVar) { + SkDEBUGFAIL("Assigning to missing variable"); + return false; + } else if (sizeof(T) * count != fVar->sizeInBytes()) { + SkDEBUGFAIL("Incorrect value size"); + return false; + } else { + memcpy(SkTAddOffset(fOwner->writableUniformData(), fVar->offset), + val, sizeof(T) * count); + } + return true; + } + + SkRuntimeEffectBuilder* fOwner; + const SkRuntimeEffect::Uniform* fVar; // nullptr if the variable was not found + }; + + struct BuilderChild { + template BuilderChild& operator=(sk_sp val) { + if (!fChild) { + SkDEBUGFAIL("Assigning to missing child"); + } else { + fOwner->fChildren[(size_t)fChild->index] = std::move(val); + } + return *this; + } + + BuilderChild& operator=(std::nullptr_t) { + if (!fChild) { + SkDEBUGFAIL("Assigning to missing child"); + } else { + fOwner->fChildren[(size_t)fChild->index] = SkRuntimeEffect::ChildPtr{}; + } + return *this; + } + + SkRuntimeEffectBuilder* fOwner; + const SkRuntimeEffect::Child* fChild; // nullptr if the child was not found + }; + + const SkRuntimeEffect* effect() const { return fEffect.get(); } + + BuilderUniform uniform(std::string_view name) { return { this, fEffect->findUniform(name) }; } + BuilderChild child(std::string_view name) { return { this, fEffect->findChild(name) }; } + + // Get access to the collated uniforms and children (in the order expected by APIs like + // makeShader on the effect): + sk_sp uniforms() const { return fUniforms; } + SkSpan children() const { return fChildren; } + +protected: + SkRuntimeEffectBuilder() = delete; + explicit SkRuntimeEffectBuilder(sk_sp effect) + : fEffect(std::move(effect)) + , fUniforms(SkData::MakeZeroInitialized(fEffect->uniformSize())) + , fChildren(fEffect->children().size()) {} + explicit SkRuntimeEffectBuilder(sk_sp effect, sk_sp uniforms) + : fEffect(std::move(effect)) + , fUniforms(std::move(uniforms)) + , fChildren(fEffect->children().size()) {} + + SkRuntimeEffectBuilder(SkRuntimeEffectBuilder&&) = default; + SkRuntimeEffectBuilder(const SkRuntimeEffectBuilder&) = default; + + SkRuntimeEffectBuilder& operator=(SkRuntimeEffectBuilder&&) = delete; + SkRuntimeEffectBuilder& operator=(const SkRuntimeEffectBuilder&) = delete; + +private: + void* writableUniformData() { + if (!fUniforms->unique()) { + fUniforms = SkData::MakeWithCopy(fUniforms->data(), fUniforms->size()); + } + return fUniforms->writable_data(); + } + + sk_sp fEffect; + sk_sp fUniforms; + std::vector fChildren; +}; + +/** + * SkRuntimeShaderBuilder is a utility to simplify creating SkShader objects from SkRuntimeEffects. + * + * NOTE: Like SkRuntimeEffect, this API is experimental and subject to change! + * + * Given an SkRuntimeEffect, the SkRuntimeShaderBuilder manages creating an input data block and + * provides named access to the 'uniform' variables in that block, as well as named access + * to a list of child shader slots. Usage: + * + * sk_sp effect = ...; + * SkRuntimeShaderBuilder builder(effect); + * builder.uniform("some_uniform_float") = 3.14f; + * builder.uniform("some_uniform_matrix") = SkM44::Rotate(...); + * builder.child("some_child_effect") = mySkImage->makeShader(...); + * ... + * sk_sp shader = builder.makeShader(nullptr, false); + * + * Note that SkRuntimeShaderBuilder is built entirely on the public API of SkRuntimeEffect, + * so can be used as-is or serve as inspiration for other interfaces or binding techniques. + */ +class SK_API SkRuntimeShaderBuilder : public SkRuntimeEffectBuilder { +public: + explicit SkRuntimeShaderBuilder(sk_sp); + // This is currently required by Android Framework but may go away if that dependency + // can be removed. + SkRuntimeShaderBuilder(const SkRuntimeShaderBuilder&) = default; + ~SkRuntimeShaderBuilder(); + + sk_sp makeShader(const SkMatrix* localMatrix = nullptr) const; + +private: + explicit SkRuntimeShaderBuilder(sk_sp effect, sk_sp uniforms) + : SkRuntimeEffectBuilder(std::move(effect), std::move(uniforms)) {} + + friend class SkRuntimeImageFilter; +}; + +/** + * SkRuntimeColorFilterBuilder makes it easy to setup and assign uniforms to runtime color filters. + */ +class SK_API SkRuntimeColorFilterBuilder : public SkRuntimeEffectBuilder { +public: + explicit SkRuntimeColorFilterBuilder(sk_sp); + ~SkRuntimeColorFilterBuilder(); + + SkRuntimeColorFilterBuilder(const SkRuntimeColorFilterBuilder&) = delete; + SkRuntimeColorFilterBuilder& operator=(const SkRuntimeColorFilterBuilder&) = delete; + + sk_sp makeColorFilter() const; +}; + +/** + * SkRuntimeBlendBuilder is a utility to simplify creation and uniform setup of runtime blenders. + */ +class SK_API SkRuntimeBlendBuilder : public SkRuntimeEffectBuilder { +public: + explicit SkRuntimeBlendBuilder(sk_sp); + ~SkRuntimeBlendBuilder(); + + SkRuntimeBlendBuilder(const SkRuntimeBlendBuilder&) = delete; + SkRuntimeBlendBuilder& operator=(const SkRuntimeBlendBuilder&) = delete; + + sk_sp makeBlender() const; +}; + +#endif // SkRuntimeEffect_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkShaderMaskFilter.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkShaderMaskFilter.h new file mode 100644 index 0000000000..b0c269e869 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkShaderMaskFilter.h @@ -0,0 +1,28 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkShaderMaskFilter_DEFINED +#define SkShaderMaskFilter_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkTypes.h" + +class SkMaskFilter; +class SkShader; + +// (DEPRECATED) This factory function is deprecated. ShaderMaskFilters will be deleted entirely +// in an upcoming Skia release. +class SK_API SkShaderMaskFilter { +public: + static sk_sp Make(sk_sp shader); + +private: + static void RegisterFlattenables(); + friend class SkFlattenable; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkTableMaskFilter.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkTableMaskFilter.h new file mode 100644 index 0000000000..937037afa8 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkTableMaskFilter.h @@ -0,0 +1,43 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkTableMaskFilter_DEFINED +#define SkTableMaskFilter_DEFINED + +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" + +#include + +class SkMaskFilter; + +/** \class SkTableMaskFilter + + Applies a table lookup on each of the alpha values in the mask. + Helper methods create some common tables (e.g. gamma, clipping) + */ +// (DEPRECATED) These factory functions are deprecated. The TableMaskFilter will be +// removed entirely in an upcoming release of Skia. +class SK_API SkTableMaskFilter { +public: + /** Utility that sets the gamma table + */ + static void MakeGammaTable(uint8_t table[256], SkScalar gamma); + + /** Utility that creates a clipping table: clamps values below min to 0 + and above max to 255, and rescales the remaining into 0..255 + */ + static void MakeClipTable(uint8_t table[256], uint8_t min, uint8_t max); + + static SkMaskFilter* Create(const uint8_t table[256]); + static SkMaskFilter* CreateGamma(SkScalar gamma); + static SkMaskFilter* CreateClip(uint8_t min, uint8_t max); + + SkTableMaskFilter() = delete; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkTrimPathEffect.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkTrimPathEffect.h new file mode 100644 index 0000000000..3e6fb7c342 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/effects/SkTrimPathEffect.h @@ -0,0 +1,45 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkTrimPathEffect_DEFINED +#define SkTrimPathEffect_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" + +class SkPathEffect; + +class SK_API SkTrimPathEffect { +public: + enum class Mode { + kNormal, // return the subset path [start,stop] + kInverted, // return the complement/subset paths [0,start] + [stop,1] + }; + + /** + * Take start and stop "t" values (values between 0...1), and return a path that is that + * subset of the original path. + * + * e.g. + * Make(0.5, 1.0) --> return the 2nd half of the path + * Make(0.33333, 0.66667) --> return the middle third of the path + * + * The trim values apply to the entire path, so if it contains several contours, all of them + * are including in the calculation. + * + * startT and stopT must be 0..1 inclusive. If they are outside of that interval, they will + * be pinned to the nearest legal value. If either is NaN, null will be returned. + * + * Note: for Mode::kNormal, this will return one (logical) segment (even if it is spread + * across multiple contours). For Mode::kInverted, this will return 2 logical + * segments: stopT..1 and 0...startT, in this order. + */ + static sk_sp Make(SkScalar startT, SkScalar stopT, Mode = Mode::kNormal); +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/encode/SkEncoder.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/encode/SkEncoder.h new file mode 100644 index 0000000000..8f76e8016c --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/encode/SkEncoder.h @@ -0,0 +1,63 @@ +/* + * 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 SkEncoder_DEFINED +#define SkEncoder_DEFINED + +#include "include/core/SkPixmap.h" +#include "include/private/base/SkAPI.h" +#include "include/private/base/SkNoncopyable.h" +#include "include/private/base/SkTemplates.h" + +#include +#include + +class SK_API SkEncoder : SkNoncopyable { +public: + /** + * A single frame to be encoded into an animated image. + * + * If a frame does not fit in the canvas size, this is an error. + * TODO(skia:13705): Add offsets when we have support for an encoder that supports using + * offsets. + */ + struct SK_API Frame { + /** + * Pixmap of the frame. + */ + SkPixmap pixmap; + /** + * Duration of the frame in millseconds. + */ + int duration; + }; + + /** + * Encode |numRows| rows of input. If the caller requests more rows than are remaining + * in the src, this will encode all of the remaining rows. |numRows| must be greater + * than zero. + */ + bool encodeRows(int numRows); + + virtual ~SkEncoder() {} + +protected: + + virtual bool onEncodeRows(int numRows) = 0; + + SkEncoder(const SkPixmap& src, size_t storageBytes) + : fSrc(src) + , fCurrRow(0) + , fStorage(storageBytes) + {} + + const SkPixmap& fSrc; + int fCurrRow; + skia_private::AutoTMalloc fStorage; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/encode/SkICC.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/encode/SkICC.h new file mode 100644 index 0000000000..b14836b2ab --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/encode/SkICC.h @@ -0,0 +1,36 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkICC_DEFINED +#define SkICC_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/private/base/SkAPI.h" + +#include + +class SkData; +struct skcms_ICCProfile; +struct skcms_Matrix3x3; +struct skcms_TransferFunction; + +SK_API sk_sp SkWriteICCProfile(const skcms_TransferFunction&, + const skcms_Matrix3x3& toXYZD50); + +SK_API sk_sp SkWriteICCProfile(const skcms_ICCProfile*, const char* description); + +// Utility function for populating the grid_16 member of skcms_A2B and skcms_B2A +// structures. This converts a point in XYZD50 to its representation in grid_16_lab. +// It will write 6 bytes. The behavior of this function matches how skcms will decode +// values, but might not match the specification, see https://crbug.com/skia/13807. +SK_API void SkICCFloatXYZD50ToGrid16Lab(const float* float_xyz, uint8_t* grid16_lab); + +// Utility function for popluating the table_16 member of skcms_Curve structure. +// This converts a float to its representation in table_16. It will write 2 bytes. +SK_API void SkICCFloatToTable16(const float f, uint8_t* table_16); + +#endif//SkICC_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/encode/SkJpegEncoder.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/encode/SkJpegEncoder.h new file mode 100644 index 0000000000..f7e8effa7f --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/encode/SkJpegEncoder.h @@ -0,0 +1,128 @@ +/* + * 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 SkJpegEncoder_DEFINED +#define SkJpegEncoder_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/private/base/SkAPI.h" + +#include + +class SkColorSpace; +class SkData; +class SkEncoder; +class SkPixmap; +class SkWStream; +class SkImage; +class GrDirectContext; +class SkYUVAPixmaps; +struct skcms_ICCProfile; + +namespace SkJpegEncoder { + +enum class AlphaOption { + kIgnore, + kBlendOnBlack, +}; + +enum class Downsample { + /** + * Reduction by a factor of two in both the horizontal and vertical directions. + */ + k420, + + /** + * Reduction by a factor of two in the horizontal direction. + */ + k422, + + /** + * No downsampling. + */ + k444, +}; + +struct Options { + /** + * |fQuality| must be in [0, 100] where 0 corresponds to the lowest quality. + */ + int fQuality = 100; + + /** + * Choose the downsampling factor for the U and V components. This is only + * meaningful if the |src| is not kGray, since kGray will not be encoded as YUV. + * This is ignored in favor of |src|'s subsampling when |src| is an SkYUVAPixmaps. + * + * Our default value matches the libjpeg-turbo default. + */ + Downsample fDownsample = Downsample::k420; + + /** + * Jpegs must be opaque. This instructs the encoder on how to handle input + * images with alpha. + * + * The default is to ignore the alpha channel and treat the image as opaque. + * Another option is to blend the pixels onto a black background before encoding. + * In the second case, the encoder supports linear or legacy blending. + */ + AlphaOption fAlphaOption = AlphaOption::kIgnore; + + /** + * Optional XMP metadata. + */ + const SkData* xmpMetadata = nullptr; + + /** + * An optional ICC profile to override the default behavior. + * + * The default behavior is to generate an ICC profile using a primary matrix and + * analytic transfer function. If the color space of |src| cannot be represented + * in this way (e.g, it is HLG or PQ), then no profile will be embedded. + */ + const skcms_ICCProfile* fICCProfile = nullptr; + const char* fICCProfileDescription = nullptr; +}; + +/** + * Encode the |src| pixels to the |dst| stream. + * |options| may be used to control the encoding behavior. + * + * Returns true on success. Returns false on an invalid or unsupported |src|. + */ +SK_API bool Encode(SkWStream* dst, const SkPixmap& src, const Options& options); +SK_API bool Encode(SkWStream* dst, + const SkYUVAPixmaps& src, + const SkColorSpace* srcColorSpace, + const Options& options); + +/** +* Encode the provided image and return the resulting bytes. If the image was created as +* a texture-backed image on a GPU context, that |ctx| must be provided so the pixels +* can be read before being encoded. For raster-backed images, |ctx| can be nullptr. +* |options| may be used to control the encoding behavior. +* +* Returns nullptr if the pixels could not be read or encoding otherwise fails. +*/ +SK_API sk_sp Encode(GrDirectContext* ctx, const SkImage* img, const Options& options); + +/** + * Create a jpeg encoder that will encode the |src| pixels to the |dst| stream. + * |options| may be used to control the encoding behavior. + * + * |dst| is unowned but must remain valid for the lifetime of the object. + * + * This returns nullptr on an invalid or unsupported |src|. + */ +SK_API std::unique_ptr Make(SkWStream* dst, const SkPixmap& src, const Options& options); +SK_API std::unique_ptr Make(SkWStream* dst, + const SkYUVAPixmaps& src, + const SkColorSpace* srcColorSpace, + const Options& options); +} // namespace SkJpegEncoder + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/encode/SkPngEncoder.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/encode/SkPngEncoder.h new file mode 100644 index 0000000000..b26befa323 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/encode/SkPngEncoder.h @@ -0,0 +1,117 @@ +/* + * 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 SkPngEncoder_DEFINED +#define SkPngEncoder_DEFINED + +#include "include/core/SkDataTable.h" +#include "include/core/SkRefCnt.h" +#include "include/private/base/SkAPI.h" + +// TODO(kjlubick) update clients to directly include this +#include "include/encode/SkEncoder.h" // IWYU pragma: keep + +#include + +class GrDirectContext; +class SkData; +class SkImage; +class SkPixmap; +class SkWStream; +struct skcms_ICCProfile; + +namespace SkPngEncoder { + +enum class FilterFlag : int { + kZero = 0x00, + kNone = 0x08, + kSub = 0x10, + kUp = 0x20, + kAvg = 0x40, + kPaeth = 0x80, + kAll = kNone | kSub | kUp | kAvg | kPaeth, +}; + +inline FilterFlag operator|(FilterFlag x, FilterFlag y) { return (FilterFlag)((int)x | (int)y); } + +struct Options { + /** + * Selects which filtering strategies to use. + * + * If a single filter is chosen, libpng will use that filter for every row. + * + * If multiple filters are chosen, libpng will use a heuristic to guess which filter + * will encode smallest, then apply that filter. This happens on a per row basis, + * different rows can use different filters. + * + * Using a single filter (or less filters) is typically faster. Trying all of the + * filters may help minimize the output file size. + * + * Our default value matches libpng's default. + */ + FilterFlag fFilterFlags = FilterFlag::kAll; + + /** + * Must be in [0, 9] where 9 corresponds to maximal compression. This value is passed + * directly to zlib. 0 is a special case to skip zlib entirely, creating dramatically + * larger pngs. + * + * Our default value matches libpng's default. + */ + int fZLibLevel = 6; + + /** + * Represents comments in the tEXt ancillary chunk of the png. + * The 2i-th entry is the keyword for the i-th comment, + * and the (2i + 1)-th entry is the text for the i-th comment. + */ + sk_sp fComments; + + /** + * An optional ICC profile to override the default behavior. + * + * The default behavior is to generate an ICC profile using a primary matrix and + * analytic transfer function. If the color space of |src| cannot be represented + * in this way (e.g, it is HLG or PQ), then no profile will be embedded. + */ + const skcms_ICCProfile* fICCProfile = nullptr; + const char* fICCProfileDescription = nullptr; +}; + +/** + * Encode the |src| pixels to the |dst| stream. + * |options| may be used to control the encoding behavior. + * + * Returns true on success. Returns false on an invalid or unsupported |src|. + */ +SK_API bool Encode(SkWStream* dst, const SkPixmap& src, const Options& options); + +/** +* Encode the provided image and return the resulting bytes. If the image was created as +* a texture-backed image on a GPU context, that |ctx| must be provided so the pixels +* can be read before being encoded. For raster-backed images, |ctx| can be nullptr. +* |options| may be used to control the encoding behavior. +* +* Returns nullptr if the pixels could not be read or encoding otherwise fails. +*/ +SK_API sk_sp Encode(GrDirectContext* ctx, const SkImage* img, const Options& options); + +/** + * Create a png encoder that will encode the |src| pixels to the |dst| stream. + * |options| may be used to control the encoding behavior. + * + * The primary use of this is incremental encoding of the pixels. + * + * |dst| is unowned but must remain valid for the lifetime of the object. + * + * This returns nullptr on an invalid or unsupported |src|. + */ +SK_API std::unique_ptr Make(SkWStream* dst, const SkPixmap& src, const Options& options); + +} // namespace SkPngEncoder + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/encode/SkWebpEncoder.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/encode/SkWebpEncoder.h new file mode 100644 index 0000000000..fe11044e73 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/encode/SkWebpEncoder.h @@ -0,0 +1,92 @@ +/* + * 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 SkWebpEncoder_DEFINED +#define SkWebpEncoder_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkSpan.h" // IWYU pragma: keep +#include "include/encode/SkEncoder.h" +#include "include/private/base/SkAPI.h" + +class SkPixmap; +class SkWStream; +class SkData; +class GrDirectContext; +class SkImage; +struct skcms_ICCProfile; + +namespace SkWebpEncoder { + +enum class Compression { + kLossy, + kLossless, +}; + +struct SK_API Options { + /** + * |fCompression| determines whether we will use webp lossy or lossless compression. + * + * |fQuality| must be in [0.0f, 100.0f]. + * If |fCompression| is kLossy, |fQuality| corresponds to the visual quality of the + * encoding. Decreasing the quality will result in a smaller encoded image. + * If |fCompression| is kLossless, |fQuality| corresponds to the amount of effort + * put into the encoding. Lower values will compress faster into larger files, + * while larger values will compress slower into smaller files. + * + * This scheme is designed to match the libwebp API. + */ + Compression fCompression = Compression::kLossy; + float fQuality = 100.0f; + + /** + * An optional ICC profile to override the default behavior. + * + * The default behavior is to generate an ICC profile using a primary matrix and + * analytic transfer function. If the color space of |src| cannot be represented + * in this way (e.g, it is HLG or PQ), then no profile will be embedded. + */ + const skcms_ICCProfile* fICCProfile = nullptr; + const char* fICCProfileDescription = nullptr; +}; + +/** + * Encode the |src| pixels to the |dst| stream. + * |options| may be used to control the encoding behavior. + * + * Returns true on success. Returns false on an invalid or unsupported |src|. + */ +SK_API bool Encode(SkWStream* dst, const SkPixmap& src, const Options& options); + +/** +* Encode the provided image and return the resulting bytes. If the image was created as +* a texture-backed image on a GPU context, that |ctx| must be provided so the pixels +* can be read before being encoded. For raster-backed images, |ctx| can be nullptr. +* |options| may be used to control the encoding behavior. +* +* Returns nullptr if the pixels could not be read or encoding otherwise fails. +*/ +SK_API sk_sp Encode(GrDirectContext* ctx, const SkImage* img, const Options& options); + +/** + * Encode the |src| frames to the |dst| stream. + * |options| may be used to control the encoding behavior. + * + * The size of the first frame will be used as the canvas size. If any other frame does + * not match the canvas size, this is an error. + * + * Returns true on success. Returns false on an invalid or unsupported |src|. + * + * Note: libwebp API also supports set background color, loop limit and customize + * lossy/lossless for each frame. These could be added later as needed. + */ +SK_API bool EncodeAnimated(SkWStream* dst, + SkSpan src, + const Options& options); +} // namespace SkWebpEncoder + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/pathops/SkPathOps.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/pathops/SkPathOps.h new file mode 100644 index 0000000000..47d2b3118f --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/pathops/SkPathOps.h @@ -0,0 +1,113 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SkPathOps_DEFINED +#define SkPathOps_DEFINED + +#include "include/core/SkPath.h" +#include "include/core/SkTypes.h" +#include "include/private/base/SkTArray.h" +#include "include/private/base/SkTDArray.h" + +struct SkRect; + + +// FIXME: move everything below into the SkPath class +/** + * The logical operations that can be performed when combining two paths. + */ +enum SkPathOp { + kDifference_SkPathOp, //!< subtract the op path from the first path + kIntersect_SkPathOp, //!< intersect the two paths + kUnion_SkPathOp, //!< union (inclusive-or) the two paths + kXOR_SkPathOp, //!< exclusive-or the two paths + kReverseDifference_SkPathOp, //!< subtract the first path from the op path +}; + +/** Set this path to the result of applying the Op to this path and the + specified path: this = (this op operand). + The resulting path will be constructed from non-overlapping contours. + The curve order is reduced where possible so that cubics may be turned + into quadratics, and quadratics maybe turned into lines. + + Returns true if operation was able to produce a result; + otherwise, result is unmodified. + + @param one The first operand (for difference, the minuend) + @param two The second operand (for difference, the subtrahend) + @param op The operator to apply. + @param result The product of the operands. The result may be one of the + inputs. + @return True if the operation succeeded. + */ +bool SK_API Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result); + +/** Set this path to a set of non-overlapping contours that describe the + same area as the original path. + The curve order is reduced where possible so that cubics may + be turned into quadratics, and quadratics maybe turned into lines. + + Returns true if operation was able to produce a result; + otherwise, result is unmodified. + + @param path The path to simplify. + @param result The simplified path. The result may be the input. + @return True if simplification succeeded. + */ +bool SK_API Simplify(const SkPath& path, SkPath* result); + +/** Set the resulting rectangle to the tight bounds of the path. + + @param path The path measured. + @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); + +/** Set the result with fill type winding to area equivalent to path. + Returns true if successful. Does not detect if path contains contours which + contain self-crossings or cross other contours; in these cases, may return + true even though result does not fill same area as path. + + Returns true if operation was able to produce a result; + otherwise, result is unmodified. The result may be the input. + + @param path The path typically with fill type set to even odd. + @param result The equivalent path with fill type set to winding. + @return True if winding path was set. + */ +bool SK_API AsWinding(const SkPath& path, SkPath* result); + +/** Perform a series of path operations, optimized for unioning many paths together. + */ +class SK_API SkOpBuilder { +public: + /** Add one or more paths and their operand. The builder is empty before the first + path is added, so the result of a single add is (emptyPath OP path). + + @param path The second operand. + @param _operator The operator to apply to the existing and supplied paths. + */ + void add(const SkPath& path, SkPathOp _operator); + + /** Computes the sum of all paths and operands, and resets the builder to its + initial state. + + @param result The product of the operands. + @return True if the operation succeeded. + */ + bool resolve(SkPath* result); + +private: + skia_private::TArray fPathRefs; + SkTDArray fOps; + + static bool FixWinding(SkPath* path); + static void ReversePath(SkPath* path); + void reset(); +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkCFObject.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkCFObject.h new file mode 100644 index 0000000000..4e56d06d06 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkCFObject.h @@ -0,0 +1,180 @@ +/* + * Copyright 2019 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkCFObject_DEFINED +#define SkCFObject_DEFINED + +#ifdef __APPLE__ + +#include "include/core/SkTypes.h" + +#include // std::nullptr_t + +#import + +/** + * Wrapper class for managing lifetime of CoreFoundation objects. It will call + * CFRetain and CFRelease appropriately on creation, assignment, and deletion. + * Based on sk_sp<>. + */ +template static inline T SkCFSafeRetain(T obj) { + if (obj) { + CFRetain(obj); + } + return obj; +} + +template static inline void SkCFSafeRelease(T obj) { + if (obj) { + CFRelease(obj); + } +} + +template class sk_cfp { +public: + using element_type = T; + + constexpr sk_cfp() {} + constexpr sk_cfp(std::nullptr_t) {} + + /** + * Shares the underlying object by calling CFRetain(), so that both the argument and the newly + * created sk_cfp both have a reference to it. + */ + sk_cfp(const sk_cfp& that) : fObject(SkCFSafeRetain(that.get())) {} + + /** + * Move the underlying object from the argument to the newly created sk_cfp. Afterwards only + * the new sk_cfp will have a reference to the object, and the argument will point to null. + * No call to CFRetain() or CFRelease() will be made. + */ + sk_cfp(sk_cfp&& that) : fObject(that.release()) {} + + /** + * Adopt the bare object into the newly created sk_cfp. + * No call to CFRetain() or CFRelease() will be made. + */ + explicit sk_cfp(T obj) { + fObject = obj; + } + + /** + * Calls CFRelease() on the underlying object pointer. + */ + ~sk_cfp() { + SkCFSafeRelease(fObject); + SkDEBUGCODE(fObject = nil); + } + + sk_cfp& operator=(std::nullptr_t) { this->reset(); return *this; } + + /** + * Shares the underlying object referenced by the argument by calling CFRetain() on it. If this + * sk_cfp previously had a reference to an object (i.e. not null) it will call CFRelease() + * on that object. + */ + sk_cfp& operator=(const sk_cfp& that) { + if (this != &that) { + this->reset(SkCFSafeRetain(that.get())); + } + return *this; + } + + /** + * Move the underlying object from the argument to the sk_cfp. If the sk_cfp + * previously held a reference to another object, CFRelease() will be called on that object. + * No call to CFRetain() will be made. + */ + sk_cfp& operator=(sk_cfp&& that) { + this->reset(that.release()); + return *this; + } + + explicit operator bool() const { return this->get() != nil; } + + T get() const { return fObject; } + T operator*() const { + SkASSERT(fObject); + return fObject; + } + + /** + * Adopt the new object, and call CFRelease() on any previously held object (if not null). + * No call to CFRetain() will be made. + */ + void reset(T object = nil) { + // Need to unref after assigning, see + // http://wg21.cmeerw.net/lwg/issue998 + // http://wg21.cmeerw.net/lwg/issue2262 + T oldObject = fObject; + fObject = object; + SkCFSafeRelease(oldObject); + } + + /** + * Shares the new object by calling CFRetain() on it. If this sk_cfp previously had a + * reference to an object (i.e. not null) it will call CFRelease() on that object. + */ + void retain(T object) { + if (fObject != object) { + this->reset(SkCFSafeRetain(object)); + } + } + + /** + * Return the original object, and set the internal object to nullptr. + * The caller must assume ownership of the object, and manage its reference count directly. + * No call to CFRelease() will be made. + */ + [[nodiscard]] T release() { + T obj = fObject; + fObject = nil; + return obj; + } + +private: + T fObject = nil; +}; + +template inline bool operator==(const sk_cfp& a, + const sk_cfp& b) { + return a.get() == b.get(); +} +template inline bool operator==(const sk_cfp& a, + std::nullptr_t) { + return !a; +} +template inline bool operator==(std::nullptr_t, + const sk_cfp& b) { + return !b; +} + +template inline bool operator!=(const sk_cfp& a, + const sk_cfp& b) { + return a.get() != b.get(); +} +template inline bool operator!=(const sk_cfp& a, + std::nullptr_t) { + return static_cast(a); +} +template inline bool operator!=(std::nullptr_t, + const sk_cfp& b) { + return static_cast(b); +} + +/* + * Returns a sk_cfp wrapping the provided object AND calls retain on it (if not null). + * + * This is different than the semantics of the constructor for sk_cfp, which just wraps the + * object, effectively "adopting" it. + */ +template sk_cfp sk_ret_cfp(T obj) { + return sk_cfp(SkCFSafeRetain(obj)); +} + +#endif // __APPLE__ +#endif // SkCFObject_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontConfigInterface.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontConfigInterface.h new file mode 100644 index 0000000000..f8fdca53f9 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontConfigInterface.h @@ -0,0 +1,112 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkFontConfigInterface_DEFINED +#define SkFontConfigInterface_DEFINED + +#include "include/core/SkFontStyle.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkStream.h" +#include "include/core/SkTypeface.h" + +class SkFontMgr; + +/** + * \class SkFontConfigInterface + * + * A simple interface for remotable font management. + * The global instance can be found with RefGlobal(). + */ +class SK_API SkFontConfigInterface : public SkRefCnt { +public: + + /** + * Returns the global SkFontConfigInterface instance. If it is not + * nullptr, calls ref() on it. The caller must balance this with a call to + * unref(). The default SkFontConfigInterface is the result of calling + * GetSingletonDirectInterface. + */ + static sk_sp RefGlobal(); + + /** + * Replace the current global instance with the specified one. + */ + static void SetGlobal(sk_sp fc); + + /** + * This should be treated as private to the impl of SkFontConfigInterface. + * Callers should not change or expect any particular values. It is meant + * to be a union of possible storage types to aid the impl. + */ + struct FontIdentity { + FontIdentity() : fID(0), fTTCIndex(0) {} + + bool operator==(const FontIdentity& other) const { + return fID == other.fID && + fTTCIndex == other.fTTCIndex && + fString == other.fString; + } + bool operator!=(const FontIdentity& other) const { + return !(*this == other); + } + + uint32_t fID; + int32_t fTTCIndex; + SkString fString; + SkFontStyle fStyle; + + // If buffer is NULL, just return the number of bytes that would have + // been written. Will pad contents to a multiple of 4. + size_t writeToMemory(void* buffer = nullptr) const; + + // Recreate from a flattened buffer, returning the number of bytes read. + size_t readFromMemory(const void* buffer, size_t length); + }; + + /** + * Given a familyName and style, find the best match. + * + * If a match is found, return true and set its outFontIdentifier. + * If outFamilyName is not null, assign the found familyName to it + * (which may differ from the requested familyName). + * If outStyle is not null, assign the found style to it + * (which may differ from the requested style). + * + * If a match is not found, return false, and ignore all out parameters. + */ + virtual bool matchFamilyName(const char familyName[], + SkFontStyle requested, + FontIdentity* outFontIdentifier, + SkString* outFamilyName, + SkFontStyle* outStyle) = 0; + + /** + * Given a FontRef, open a stream to access its data, or return null + * if the FontRef's data is not available. The caller is responsible for + * deleting the stream when it is done accessing the data. + */ + virtual SkStreamAsset* openStream(const FontIdentity&) = 0; + + /** + * Return an SkTypeface for the given FontIdentity. + * + * The default implementation simply returns a new typeface built using data obtained from + * openStream() using the provided SkFontMgr, but derived classes may implement more + * complex caching schemes. + */ + virtual sk_sp makeTypeface(const FontIdentity& identity, sk_sp mgr); + + /** + * Return a singleton instance of a direct subclass that calls into + * libfontconfig. This does not affect the refcnt of the returned instance. + */ + static SkFontConfigInterface* GetSingletonDirectInterface(); + + using INHERITED = SkRefCnt; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_FontConfigInterface.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_FontConfigInterface.h new file mode 100644 index 0000000000..05771257d2 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_FontConfigInterface.h @@ -0,0 +1,20 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkFontMgr_FontConfigInterface_DEFINED +#define SkFontMgr_FontConfigInterface_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkTypes.h" + +class SkFontMgr; +class SkFontConfigInterface; + +/** Creates a SkFontMgr which wraps a SkFontConfigInterface. */ +SK_API sk_sp SkFontMgr_New_FCI(sk_sp fci); + +#endif // #ifndef SkFontMgr_FontConfigInterface_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_Fontations.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_Fontations.h new file mode 100644 index 0000000000..c5b546c367 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_Fontations.h @@ -0,0 +1,20 @@ +/* + * Copyright 2024 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkFontMgr_fontations_DEFINED +#define SkFontMgr_fontations_DEFINED + +#include "include/core/SkRefCnt.h" + +class SkFontMgr; + +/** Create a font manager instantiating fonts using the Rust Fontations backend. + * This font manager does not support matching fonts, only instantiation. + */ +SK_API sk_sp SkFontMgr_New_Fontations_Empty(); + +#endif // #ifndef SkFontMgr_fontations_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_android.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_android.h new file mode 100644 index 0000000000..cb3bea29a2 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_android.h @@ -0,0 +1,50 @@ +/* + * 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 SkFontMgr_android_DEFINED +#define SkFontMgr_android_DEFINED + +#include "include/core/SkRefCnt.h" + +class SkFontMgr; +class SkFontScanner; + +struct SkFontMgr_Android_CustomFonts { + /** When specifying custom fonts, indicates how to use system fonts. */ + enum SystemFontUse { + kOnlyCustom, /** Use only custom fonts. NDK compliant. */ + kPreferCustom, /** Use custom fonts before system fonts. */ + kPreferSystem /** Use system fonts before custom fonts. */ + }; + /** Whether or not to use system fonts. */ + SystemFontUse fSystemFontUse; + + /** Base path to resolve relative font file names. If a directory, should end with '/'. */ + const char* fBasePath; + + /** Optional custom configuration file to use. */ + const char* fFontsXml; + + /** Optional custom configuration file for fonts which provide fallback. + * In the new style (version > 21) fontsXml format is used, this should be NULL. + */ + const char* fFallbackFontsXml; + + /** Optional custom flag. If set to true the SkFontMgr will acquire all requisite + * system IO resources on initialization. + */ + bool fIsolated; +}; + +/** Create a font manager for Android. If 'custom' is NULL, use only system fonts. */ + +// Deprecated +SK_API sk_sp SkFontMgr_New_Android(const SkFontMgr_Android_CustomFonts* custom); + +SK_API sk_sp SkFontMgr_New_Android(const SkFontMgr_Android_CustomFonts* custom, + std::unique_ptr scanner); +#endif // SkFontMgr_android_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_data.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_data.h new file mode 100644 index 0000000000..6a22365af4 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_data.h @@ -0,0 +1,22 @@ +/* + * Copyright 2023 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SkFontMgr_data_DEFINED +#define SkFontMgr_data_DEFINED + +#include "include/core/SkData.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkSpan.h" +#include "include/core/SkTypes.h" + +class SkFontMgr; + +/** Create a custom font manager which wraps a collection of SkData-stored fonts. + * This font manager uses FreeType for rendering. + */ +SK_API sk_sp SkFontMgr_New_Custom_Data(SkSpan>); + +#endif // SkFontMgr_data_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_directory.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_directory.h new file mode 100644 index 0000000000..b1a60fb4da --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_directory.h @@ -0,0 +1,21 @@ +/* + * 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 SkFontMgr_directory_DEFINED +#define SkFontMgr_directory_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkTypes.h" + +class SkFontMgr; + +/** Create a custom font manager which scans a given directory for font files. + * This font manager uses FreeType for rendering. + */ +SK_API sk_sp SkFontMgr_New_Custom_Directory(const char* dir); + +#endif // SkFontMgr_directory_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_empty.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_empty.h new file mode 100644 index 0000000000..e5756421d0 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_empty.h @@ -0,0 +1,21 @@ +/* + * 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 SkFontMgr_empty_DEFINED +#define SkFontMgr_empty_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkTypes.h" + +class SkFontMgr; + +/** Create a custom font manager that contains no built-in fonts. + * This font manager uses FreeType for rendering. + */ +SK_API sk_sp SkFontMgr_New_Custom_Empty(); + +#endif // SkFontMgr_empty_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_fontconfig.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_fontconfig.h new file mode 100644 index 0000000000..4b2bb2d297 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_fontconfig.h @@ -0,0 +1,22 @@ +/* + * 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 SkFontMgr_fontconfig_DEFINED +#define SkFontMgr_fontconfig_DEFINED + +#include "include/core/SkRefCnt.h" +#include + +class SkFontMgr; + +/** Create a font manager around a FontConfig instance. + * If 'fc' is NULL, will use a new default config. + * Takes ownership of 'fc' and will call FcConfigDestroy on it. + */ +SK_API sk_sp SkFontMgr_New_FontConfig(FcConfig* fc); + +#endif // #ifndef SkFontMgr_fontconfig_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_fuchsia.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_fuchsia.h new file mode 100644 index 0000000000..d20530af72 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_fuchsia.h @@ -0,0 +1,19 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkFontMgr_fuchsia_DEFINED +#define SkFontMgr_fuchsia_DEFINED + +#include + +#include "include/core/SkRefCnt.h" + +class SkFontMgr; + +SK_API sk_sp SkFontMgr_New_Fuchsia(fuchsia::fonts::ProviderSyncPtr provider); + +#endif // SkFontMgr_fuchsia_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_mac_ct.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_mac_ct.h new file mode 100644 index 0000000000..45cba65b5d --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkFontMgr_mac_ct.h @@ -0,0 +1,27 @@ +/* + * 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 SkFontMgr_mac_ct_DEFINED +#define SkFontMgr_mac_ct_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkTypes.h" + +#ifdef SK_BUILD_FOR_MAC +#import +#endif + +#ifdef SK_BUILD_FOR_IOS +#include +#endif + +class SkFontMgr; + +/** Create a font manager for CoreText. If the collection is nullptr the system default will be used. */ +SK_API extern sk_sp SkFontMgr_New_CoreText(CTFontCollectionRef); + +#endif // SkFontMgr_mac_ct_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkImageGeneratorCG.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkImageGeneratorCG.h new file mode 100644 index 0000000000..6fa775db49 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkImageGeneratorCG.h @@ -0,0 +1,28 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SkImageGeneratorCG_DEFINED +#define SkImageGeneratorCG_DEFINED + +// This is needed as clients may override the target platform +// using SkUserConfig +#include "include/private/base/SkLoadUserConfig.h" + +#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) + +#include "include/core/SkData.h" +#include "include/core/SkImageGenerator.h" +#include "include/core/SkRefCnt.h" +#include "include/private/base/SkAPI.h" + +#include + +namespace SkImageGeneratorCG { +SK_API std::unique_ptr MakeFromEncodedCG(sk_sp); +} // namespace SkImageGeneratorCG + +#endif // defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) +#endif // SkImageGeneratorCG_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkImageGeneratorNDK.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkImageGeneratorNDK.h new file mode 100644 index 0000000000..739a586f0d --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkImageGeneratorNDK.h @@ -0,0 +1,40 @@ +/* + * Copyright 2020 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkImageGeneratorNDK_DEFINED +#define SkImageGeneratorNDK_DEFINED + +#include "include/core/SkTypes.h" +#ifdef SK_ENABLE_NDK_IMAGES + +#include "include/core/SkData.h" +#include "include/core/SkImageGenerator.h" + +#include + +namespace SkImageGeneratorNDK { +/** + * Create a generator that uses the Android NDK's APIs for decoding images. + * + * Only supported on devices where __ANDROID_API__ >= 30. + * + * As with SkCodec, the SkColorSpace passed to getPixels() determines the + * type of color space transformations to apply. A null SkColorSpace means to + * apply none. + * + * A note on scaling: Calling getPixels() on the resulting SkImageGenerator + * with dimensions that do not match getInfo() requests a scale. For WebP + * files, dimensions smaller than those of getInfo are supported. For Jpeg + * files, dimensions of 1/2, 1/4, and 1/8 are supported. TODO: Provide an + * API like SkCodecImageGenerator::getScaledDimensions() to report which + * dimensions are supported? + */ +SK_API std::unique_ptr MakeFromEncodedNDK(sk_sp); +} + +#endif // SK_ENABLE_NDK_IMAGES +#endif // SkImageGeneratorNDK_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkImageGeneratorWIC.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkImageGeneratorWIC.h new file mode 100644 index 0000000000..5ea3b83e24 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkImageGeneratorWIC.h @@ -0,0 +1,41 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkImageGeneratorWIC_DEFINED +#define SkImageGeneratorWIC_DEFINED + +#include "include/private/base/SkFeatures.h" + +#if defined(SK_BUILD_FOR_WIN) + +#include "include/core/SkData.h" +#include "include/core/SkImageGenerator.h" +#include "include/core/SkRefCnt.h" +#include "include/private/base/SkAPI.h" + +#include + +/* + * Any Windows program that uses COM must initialize the COM library by calling + * the CoInitializeEx function. In addition, each thread that uses a COM + * interface must make a separate call to this function. + * + * For every successful call to CoInitializeEx, the thread must call + * CoUninitialize before it exits. + * + * SkImageGeneratorWIC requires the COM library and leaves it to the client to + * initialize COM for their application. + * + * For more information on initializing COM, please see: + * https://msdn.microsoft.com/en-us/library/windows/desktop/ff485844.aspx + */ +namespace SkImageGeneratorWIC { +SK_API std::unique_ptr MakeFromEncodedWIC(sk_sp); +} + +#endif // SK_BUILD_FOR_WIN +#endif // SkImageGeneratorWIC_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkTypeface_fontations.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkTypeface_fontations.h new file mode 100644 index 0000000000..cd6531ab64 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkTypeface_fontations.h @@ -0,0 +1,21 @@ +/* + * Copyright 2023 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkTypeface_fontations_DEFINED +#define SkTypeface_fontations_DEFINED + +#include "include/core/SkFontArguments.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkTypeface.h" +#include "include/core/SkTypes.h" + +#include + +SK_API sk_sp SkTypeface_Make_Fontations(std::unique_ptr fontData, + const SkFontArguments& args); + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkTypeface_mac.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkTypeface_mac.h new file mode 100644 index 0000000000..ec68e05492 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkTypeface_mac.h @@ -0,0 +1,44 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkTypeface_mac_DEFINED +#define SkTypeface_mac_DEFINED + +#include "include/core/SkTypeface.h" + +#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) + +#include + +#ifdef SK_BUILD_FOR_MAC +#import +#endif + +#ifdef SK_BUILD_FOR_IOS +#include +#endif + +/** + * Like the other Typeface make methods, this returns a new reference to the + * corresponding typeface for the specified CTFontRef. + */ +SK_API extern sk_sp SkMakeTypefaceFromCTFont(CTFontRef); + +/** + * Returns the platform-specific CTFontRef handle for a + * given SkTypeface. Note that the returned CTFontRef gets + * released when the source SkTypeface is destroyed. + * + * This method is deprecated. It may only be used by Blink Mac + * legacy code in special cases related to text-shaping + * with AAT fonts, clipboard handling and font fallback. + * See https://code.google.com/p/skia/issues/detail?id=3408 + */ +SK_API extern CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face); + +#endif // defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) +#endif // SkTypeface_mac_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkTypeface_win.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkTypeface_win.h new file mode 100644 index 0000000000..45711ba231 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/ports/SkTypeface_win.h @@ -0,0 +1,63 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkTypeface_win_DEFINED +#define SkTypeface_win_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkTypeface.h" +#include "include/core/SkTypes.h" + +#ifdef SK_BUILD_FOR_WIN + +#ifdef UNICODE +typedef struct tagLOGFONTW LOGFONTW; +typedef LOGFONTW LOGFONT; +#else +typedef struct tagLOGFONTA LOGFONTA; +typedef LOGFONTA LOGFONT; +#endif // UNICODE + +/** + * Like the other Typeface create methods, this returns a new reference to the + * corresponding typeface for the specified logfont. The caller is responsible + * for calling unref() when it is finished. + */ +SK_API sk_sp SkCreateTypefaceFromLOGFONT(const LOGFONT&); + +/** + * Copy the LOGFONT associated with this typeface into the lf parameter. Note + * that the lfHeight will need to be set afterwards, since the typeface does + * not track this (the paint does). + * typeface may be NULL, in which case we return the logfont for the default font. + */ +SK_API void SkLOGFONTFromTypeface(const SkTypeface* typeface, LOGFONT* lf); + +/** + * Set an optional callback to ensure that the data behind a LOGFONT is loaded. + * This will get called if Skia tries to access the data but hits a failure. + * Normally this is null, and is only required if the font data needs to be + * remotely (re)loaded. + */ +SK_API void SkTypeface_SetEnsureLOGFONTAccessibleProc(void (*)(const LOGFONT&)); + +// Experimental! +// +class SkFontMgr; +struct IDWriteFactory; +struct IDWriteFontCollection; +struct IDWriteFontFallback; + +SK_API sk_sp SkFontMgr_New_GDI(); +SK_API sk_sp SkFontMgr_New_DirectWrite(IDWriteFactory* factory = nullptr, + IDWriteFontCollection* collection = nullptr); +SK_API sk_sp SkFontMgr_New_DirectWrite(IDWriteFactory* factory, + IDWriteFontCollection* collection, + IDWriteFontFallback* fallback); + +#endif // SK_BUILD_FOR_WIN +#endif // SkTypeface_win_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/OWNERS b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/OWNERS new file mode 100644 index 0000000000..7cf12a2a7f --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/OWNERS @@ -0,0 +1,4 @@ +# include/ has a restricted set of reviewers (to limit changes to public API) +# Files in this directory follow the same rules as the rest of Skia, though: + +file:../../OWNERS diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkColorData.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkColorData.h new file mode 100644 index 0000000000..0f6a3e9aa5 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkColorData.h @@ -0,0 +1,385 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkColorData_DEFINED +#define SkColorData_DEFINED + +#include "include/core/SkColor.h" +#include "include/core/SkColorPriv.h" +#include "include/private/base/SkTo.h" + +//////////////////////////////////////////////////////////////////////////////////////////// +// Convert a 16bit pixel to a 32bit pixel + +#define SK_R16_BITS 5 +#define SK_G16_BITS 6 +#define SK_B16_BITS 5 + +#define SK_R16_SHIFT (SK_B16_BITS + SK_G16_BITS) +#define SK_G16_SHIFT (SK_B16_BITS) +#define SK_B16_SHIFT 0 + +#define SK_R16_MASK ((1 << SK_R16_BITS) - 1) +#define SK_G16_MASK ((1 << SK_G16_BITS) - 1) +#define SK_B16_MASK ((1 << SK_B16_BITS) - 1) + +#define SkGetPackedR16(color) (((unsigned)(color) >> SK_R16_SHIFT) & SK_R16_MASK) +#define SkGetPackedG16(color) (((unsigned)(color) >> SK_G16_SHIFT) & SK_G16_MASK) +#define SkGetPackedB16(color) (((unsigned)(color) >> SK_B16_SHIFT) & SK_B16_MASK) + +static inline unsigned SkR16ToR32(unsigned r) { + return (r << (8 - SK_R16_BITS)) | (r >> (2 * SK_R16_BITS - 8)); +} + +static inline unsigned SkG16ToG32(unsigned g) { + return (g << (8 - SK_G16_BITS)) | (g >> (2 * SK_G16_BITS - 8)); +} + +static inline unsigned SkB16ToB32(unsigned b) { + return (b << (8 - SK_B16_BITS)) | (b >> (2 * SK_B16_BITS - 8)); +} + +#define SkPacked16ToR32(c) SkR16ToR32(SkGetPackedR16(c)) +#define SkPacked16ToG32(c) SkG16ToG32(SkGetPackedG16(c)) +#define SkPacked16ToB32(c) SkB16ToB32(SkGetPackedB16(c)) + +////////////////////////////////////////////////////////////////////////////// + +#define SkASSERT_IS_BYTE(x) SkASSERT(0 == ((x) & ~0xFFu)) + +// Reverse the bytes coorsponding to RED and BLUE in a packed pixels. Note the +// pair of them are in the same 2 slots in both RGBA and BGRA, thus there is +// no need to pass in the colortype to this function. +static inline uint32_t SkSwizzle_RB(uint32_t c) { + static const uint32_t kRBMask = (0xFF << SK_R32_SHIFT) | (0xFF << SK_B32_SHIFT); + + unsigned c0 = (c >> SK_R32_SHIFT) & 0xFF; + unsigned c1 = (c >> SK_B32_SHIFT) & 0xFF; + return (c & ~kRBMask) | (c0 << SK_B32_SHIFT) | (c1 << SK_R32_SHIFT); +} + +static inline uint32_t SkPackARGB_as_RGBA(U8CPU a, U8CPU r, U8CPU g, U8CPU b) { + SkASSERT_IS_BYTE(a); + SkASSERT_IS_BYTE(r); + SkASSERT_IS_BYTE(g); + SkASSERT_IS_BYTE(b); + return (a << SK_RGBA_A32_SHIFT) | (r << SK_RGBA_R32_SHIFT) | + (g << SK_RGBA_G32_SHIFT) | (b << SK_RGBA_B32_SHIFT); +} + +static inline uint32_t SkPackARGB_as_BGRA(U8CPU a, U8CPU r, U8CPU g, U8CPU b) { + SkASSERT_IS_BYTE(a); + SkASSERT_IS_BYTE(r); + SkASSERT_IS_BYTE(g); + SkASSERT_IS_BYTE(b); + return (a << SK_BGRA_A32_SHIFT) | (r << SK_BGRA_R32_SHIFT) | + (g << SK_BGRA_G32_SHIFT) | (b << SK_BGRA_B32_SHIFT); +} + +static inline SkPMColor SkSwizzle_RGBA_to_PMColor(uint32_t c) { +#ifdef SK_PMCOLOR_IS_RGBA + return c; +#else + return SkSwizzle_RB(c); +#endif +} + +static inline SkPMColor SkSwizzle_BGRA_to_PMColor(uint32_t c) { +#ifdef SK_PMCOLOR_IS_BGRA + return c; +#else + return SkSwizzle_RB(c); +#endif +} + +////////////////////////////////////////////////////////////////////////////// + +///@{ +/** See ITU-R Recommendation BT.709 at http://www.itu.int/rec/R-REC-BT.709/ .*/ +#define SK_ITU_BT709_LUM_COEFF_R (0.2126f) +#define SK_ITU_BT709_LUM_COEFF_G (0.7152f) +#define SK_ITU_BT709_LUM_COEFF_B (0.0722f) +///@} + +///@{ +/** A float value which specifies this channel's contribution to luminance. */ +#define SK_LUM_COEFF_R SK_ITU_BT709_LUM_COEFF_R +#define SK_LUM_COEFF_G SK_ITU_BT709_LUM_COEFF_G +#define SK_LUM_COEFF_B SK_ITU_BT709_LUM_COEFF_B +///@} + +/** Computes the luminance from the given r, g, and b in accordance with + SK_LUM_COEFF_X. For correct results, r, g, and b should be in linear space. +*/ +static inline U8CPU SkComputeLuminance(U8CPU r, U8CPU g, U8CPU b) { + //The following is + //r * SK_LUM_COEFF_R + g * SK_LUM_COEFF_G + b * SK_LUM_COEFF_B + //with SK_LUM_COEFF_X in 1.8 fixed point (rounding adjusted to sum to 256). + return (r * 54 + g * 183 + b * 19) >> 8; +} + +/** Calculates 256 - (value * alpha256) / 255 in range [0,256], + * for [0,255] value and [0,256] alpha256. + */ +static inline U16CPU SkAlphaMulInv256(U16CPU value, U16CPU alpha256) { + unsigned prod = 0xFFFF - value * alpha256; + return (prod + (prod >> 8)) >> 8; +} + +// The caller may want negative values, so keep all params signed (int) +// so we don't accidentally slip into unsigned math and lose the sign +// extension when we shift (in SkAlphaMul) +static inline int SkAlphaBlend(int src, int dst, int scale256) { + SkASSERT((unsigned)scale256 <= 256); + return dst + SkAlphaMul(src - dst, scale256); +} + +static inline uint16_t SkPackRGB16(unsigned r, unsigned g, unsigned b) { + SkASSERT(r <= SK_R16_MASK); + SkASSERT(g <= SK_G16_MASK); + SkASSERT(b <= SK_B16_MASK); + + return SkToU16((r << SK_R16_SHIFT) | (g << SK_G16_SHIFT) | (b << SK_B16_SHIFT)); +} + +#define SK_R16_MASK_IN_PLACE (SK_R16_MASK << SK_R16_SHIFT) +#define SK_G16_MASK_IN_PLACE (SK_G16_MASK << SK_G16_SHIFT) +#define SK_B16_MASK_IN_PLACE (SK_B16_MASK << SK_B16_SHIFT) + +/////////////////////////////////////////////////////////////////////////////// + +/** + * Abstract 4-byte interpolation, implemented on top of SkPMColor + * utility functions. Third parameter controls blending of the first two: + * (src, dst, 0) returns dst + * (src, dst, 0xFF) returns src + * scale is [0..256], unlike SkFourByteInterp which takes [0..255] + */ +static inline SkPMColor SkFourByteInterp256(SkPMColor src, SkPMColor dst, int scale) { + unsigned a = SkTo(SkAlphaBlend(SkGetPackedA32(src), SkGetPackedA32(dst), scale)); + unsigned r = SkTo(SkAlphaBlend(SkGetPackedR32(src), SkGetPackedR32(dst), scale)); + unsigned g = SkTo(SkAlphaBlend(SkGetPackedG32(src), SkGetPackedG32(dst), scale)); + unsigned b = SkTo(SkAlphaBlend(SkGetPackedB32(src), SkGetPackedB32(dst), scale)); + + return SkPackARGB32(a, r, g, b); +} + +/** + * Abstract 4-byte interpolation, implemented on top of SkPMColor + * utility functions. Third parameter controls blending of the first two: + * (src, dst, 0) returns dst + * (src, dst, 0xFF) returns src + */ +static inline SkPMColor SkFourByteInterp(SkPMColor src, SkPMColor dst, U8CPU srcWeight) { + int scale = (int)SkAlpha255To256(srcWeight); + return SkFourByteInterp256(src, dst, scale); +} + +/** + * 0xAARRGGBB -> 0x00AA00GG, 0x00RR00BB + */ +static inline void SkSplay(uint32_t color, uint32_t* ag, uint32_t* rb) { + const uint32_t mask = 0x00FF00FF; + *ag = (color >> 8) & mask; + *rb = color & mask; +} + +/** + * 0xAARRGGBB -> 0x00AA00GG00RR00BB + * (note, ARGB -> AGRB) + */ +static inline uint64_t SkSplay(uint32_t color) { + const uint32_t mask = 0x00FF00FF; + uint64_t agrb = (color >> 8) & mask; // 0x0000000000AA00GG + agrb <<= 32; // 0x00AA00GG00000000 + agrb |= color & mask; // 0x00AA00GG00RR00BB + return agrb; +} + +/** + * 0xAAxxGGxx, 0xRRxxBBxx-> 0xAARRGGBB + */ +static inline uint32_t SkUnsplay(uint32_t ag, uint32_t rb) { + const uint32_t mask = 0xFF00FF00; + return (ag & mask) | ((rb & mask) >> 8); +} + +/** + * 0xAAxxGGxxRRxxBBxx -> 0xAARRGGBB + * (note, AGRB -> ARGB) + */ +static inline uint32_t SkUnsplay(uint64_t agrb) { + const uint32_t mask = 0xFF00FF00; + return SkPMColor( + ((agrb & mask) >> 8) | // 0x00RR00BB + ((agrb >> 32) & mask)); // 0xAARRGGBB +} + +static inline SkPMColor SkFastFourByteInterp256_32(SkPMColor src, SkPMColor dst, unsigned scale) { + SkASSERT(scale <= 256); + + // Two 8-bit blends per two 32-bit registers, with space to make sure the math doesn't collide. + uint32_t src_ag, src_rb, dst_ag, dst_rb; + SkSplay(src, &src_ag, &src_rb); + SkSplay(dst, &dst_ag, &dst_rb); + + const uint32_t ret_ag = src_ag * scale + (256 - scale) * dst_ag; + const uint32_t ret_rb = src_rb * scale + (256 - scale) * dst_rb; + + return SkUnsplay(ret_ag, ret_rb); +} + +static inline SkPMColor SkFastFourByteInterp256_64(SkPMColor src, SkPMColor dst, unsigned scale) { + SkASSERT(scale <= 256); + // Four 8-bit blends in one 64-bit register, with space to make sure the math doesn't collide. + return SkUnsplay(SkSplay(src) * scale + (256-scale) * SkSplay(dst)); +} + +// TODO(mtklein): Replace slow versions with fast versions, using scale + (scale>>7) everywhere. + +/** + * Same as SkFourByteInterp256, but faster. + */ +static inline SkPMColor SkFastFourByteInterp256(SkPMColor src, SkPMColor dst, unsigned scale) { + // On a 64-bit machine, _64 is about 10% faster than _32, but ~40% slower on a 32-bit machine. + if (sizeof(void*) == 4) { + return SkFastFourByteInterp256_32(src, dst, scale); + } else { + return SkFastFourByteInterp256_64(src, dst, scale); + } +} + +/** + * Nearly the same as SkFourByteInterp, but faster and a touch more accurate, due to better + * srcWeight scaling to [0, 256]. + */ +static inline SkPMColor SkFastFourByteInterp(SkPMColor src, SkPMColor dst, U8CPU srcWeight) { + SkASSERT(srcWeight <= 255); + // scale = srcWeight + (srcWeight >> 7) is more accurate than + // scale = srcWeight + 1, but 7% slower + return SkFastFourByteInterp256(src, dst, srcWeight + (srcWeight >> 7)); +} + +/** + * Interpolates between colors src and dst using [0,256] scale. + */ +static inline SkPMColor SkPMLerp(SkPMColor src, SkPMColor dst, unsigned scale) { + return SkFastFourByteInterp256(src, dst, scale); +} + +static inline SkPMColor SkBlendARGB32(SkPMColor src, SkPMColor dst, U8CPU aa) { + SkASSERT((unsigned)aa <= 255); + + unsigned src_scale = SkAlpha255To256(aa); + unsigned dst_scale = SkAlphaMulInv256(SkGetPackedA32(src), src_scale); + + const uint32_t mask = 0xFF00FF; + + uint32_t src_rb = (src & mask) * src_scale; + uint32_t src_ag = ((src >> 8) & mask) * src_scale; + + uint32_t dst_rb = (dst & mask) * dst_scale; + uint32_t dst_ag = ((dst >> 8) & mask) * dst_scale; + + return (((src_rb + dst_rb) >> 8) & mask) | ((src_ag + dst_ag) & ~mask); +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// Convert a 32bit pixel to a 16bit pixel (no dither) + +#define SkR32ToR16_MACRO(r) ((unsigned)(r) >> (SK_R32_BITS - SK_R16_BITS)) +#define SkG32ToG16_MACRO(g) ((unsigned)(g) >> (SK_G32_BITS - SK_G16_BITS)) +#define SkB32ToB16_MACRO(b) ((unsigned)(b) >> (SK_B32_BITS - SK_B16_BITS)) + +#ifdef SK_DEBUG + static inline unsigned SkR32ToR16(unsigned r) { + SkR32Assert(r); + return SkR32ToR16_MACRO(r); + } + static inline unsigned SkG32ToG16(unsigned g) { + SkG32Assert(g); + return SkG32ToG16_MACRO(g); + } + static inline unsigned SkB32ToB16(unsigned b) { + SkB32Assert(b); + return SkB32ToB16_MACRO(b); + } +#else + #define SkR32ToR16(r) SkR32ToR16_MACRO(r) + #define SkG32ToG16(g) SkG32ToG16_MACRO(g) + #define SkB32ToB16(b) SkB32ToB16_MACRO(b) +#endif + +static inline U16CPU SkPixel32ToPixel16(SkPMColor c) { + unsigned r = ((c >> (SK_R32_SHIFT + (8 - SK_R16_BITS))) & SK_R16_MASK) << SK_R16_SHIFT; + unsigned g = ((c >> (SK_G32_SHIFT + (8 - SK_G16_BITS))) & SK_G16_MASK) << SK_G16_SHIFT; + unsigned b = ((c >> (SK_B32_SHIFT + (8 - SK_B16_BITS))) & SK_B16_MASK) << SK_B16_SHIFT; + return r | g | b; +} + +static inline U16CPU SkPack888ToRGB16(U8CPU r, U8CPU g, U8CPU b) { + return (SkR32ToR16(r) << SK_R16_SHIFT) | + (SkG32ToG16(g) << SK_G16_SHIFT) | + (SkB32ToB16(b) << SK_B16_SHIFT); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static inline SkColor SkPixel16ToColor(U16CPU src) { + SkASSERT(src == SkToU16(src)); + + unsigned r = SkPacked16ToR32(src); + unsigned g = SkPacked16ToG32(src); + unsigned b = SkPacked16ToB32(src); + + SkASSERT((r >> (8 - SK_R16_BITS)) == SkGetPackedR16(src)); + SkASSERT((g >> (8 - SK_G16_BITS)) == SkGetPackedG16(src)); + SkASSERT((b >> (8 - SK_B16_BITS)) == SkGetPackedB16(src)); + + return SkColorSetRGB(r, g, b); +} + +/////////////////////////////////////////////////////////////////////////////// + +typedef uint16_t SkPMColor16; + +// Put in OpenGL order (r g b a) +#define SK_A4444_SHIFT 0 +#define SK_R4444_SHIFT 12 +#define SK_G4444_SHIFT 8 +#define SK_B4444_SHIFT 4 + +static inline U8CPU SkReplicateNibble(unsigned nib) { + SkASSERT(nib <= 0xF); + return (nib << 4) | nib; +} + +#define SkGetPackedA4444(c) (((unsigned)(c) >> SK_A4444_SHIFT) & 0xF) +#define SkGetPackedR4444(c) (((unsigned)(c) >> SK_R4444_SHIFT) & 0xF) +#define SkGetPackedG4444(c) (((unsigned)(c) >> SK_G4444_SHIFT) & 0xF) +#define SkGetPackedB4444(c) (((unsigned)(c) >> SK_B4444_SHIFT) & 0xF) + +#define SkPacked4444ToA32(c) SkReplicateNibble(SkGetPackedA4444(c)) + +static inline SkPMColor SkPixel4444ToPixel32(U16CPU c) { + uint32_t d = (SkGetPackedA4444(c) << SK_A32_SHIFT) | + (SkGetPackedR4444(c) << SK_R32_SHIFT) | + (SkGetPackedG4444(c) << SK_G32_SHIFT) | + (SkGetPackedB4444(c) << SK_B32_SHIFT); + return d | (d << 4); +} + +using SkPMColor4f = SkRGBA4f; + +constexpr SkPMColor4f SK_PMColor4fTRANSPARENT = { 0, 0, 0, 0 }; +constexpr SkPMColor4f SK_PMColor4fBLACK = { 0, 0, 0, 1 }; +constexpr SkPMColor4f SK_PMColor4fWHITE = { 1, 1, 1, 1 }; +constexpr SkPMColor4f SK_PMColor4fILLEGAL = { SK_FloatNegativeInfinity, + SK_FloatNegativeInfinity, + SK_FloatNegativeInfinity, + SK_FloatNegativeInfinity }; +#endif // SkColorData_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkEncodedInfo.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkEncodedInfo.h new file mode 100644 index 0000000000..f1cbc5050c --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkEncodedInfo.h @@ -0,0 +1,283 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkEncodedInfo_DEFINED +#define SkEncodedInfo_DEFINED + +#include "include/core/SkAlphaType.h" +#include "include/core/SkColorSpace.h" +#include "include/core/SkColorType.h" +#include "include/core/SkData.h" +#include "include/core/SkImageInfo.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkTypes.h" +#include "include/private/base/SkTo.h" +#include "modules/skcms/skcms.h" + +#include +#include +#include + +struct SkEncodedInfo { +public: + class ICCProfile { + public: + static std::unique_ptr Make(sk_sp); + static std::unique_ptr Make(const skcms_ICCProfile&); + + const skcms_ICCProfile* profile() const { return &fProfile; } + sk_sp data() const { return fData; } + private: + ICCProfile(const skcms_ICCProfile&, sk_sp = nullptr); + + skcms_ICCProfile fProfile; + sk_sp fData; + }; + + enum Alpha { + kOpaque_Alpha, + kUnpremul_Alpha, + + // Each pixel is either fully opaque or fully transparent. + // There is no difference between requesting kPremul or kUnpremul. + kBinary_Alpha, + }; + + /* + * We strive to make the number of components per pixel obvious through + * our naming conventions. + * Ex: kRGB has 3 components. kRGBA has 4 components. + * + * This sometimes results in redundant Alpha and Color information. + * Ex: kRGB images must also be kOpaque. + */ + enum Color { + // PNG, WBMP + kGray_Color, + + // PNG + kGrayAlpha_Color, + + // PNG with Skia-specific sBIT + // Like kGrayAlpha, except this expects to be treated as + // kAlpha_8_SkColorType, which ignores the gray component. If + // decoded to full color (e.g. kN32), the gray component is respected + // (so it can share code with kGrayAlpha). + kXAlpha_Color, + + // PNG + // 565 images may be encoded to PNG by specifying the number of + // significant bits for each channel. This is a strange 565 + // representation because the image is still encoded with 8 bits per + // component. + k565_Color, + + // PNG, GIF, BMP + kPalette_Color, + + // PNG, RAW + kRGB_Color, + kRGBA_Color, + + // BMP + kBGR_Color, + kBGRX_Color, + kBGRA_Color, + + // JPEG, WEBP + kYUV_Color, + + // WEBP + kYUVA_Color, + + // JPEG + // Photoshop actually writes inverted CMYK data into JPEGs, where zero + // represents 100% ink coverage. For this reason, we treat CMYK JPEGs + // as having inverted CMYK. libjpeg-turbo warns that this may break + // other applications, but the CMYK JPEGs we see on the web expect to + // be treated as inverted CMYK. + kInvertedCMYK_Color, + kYCCK_Color, + }; + + static SkEncodedInfo Make(int width, int height, Color color, Alpha alpha, + int bitsPerComponent) { + return Make(width, height, color, alpha, bitsPerComponent, nullptr); + } + + static SkEncodedInfo Make(int width, int height, Color color, + Alpha alpha, int bitsPerComponent, std::unique_ptr profile) { + return Make(width, height, color, alpha, /*bitsPerComponent*/ bitsPerComponent, + std::move(profile), /*colorDepth*/ bitsPerComponent); + } + + static SkEncodedInfo Make(int width, int height, Color color, + Alpha alpha, int bitsPerComponent, std::unique_ptr profile, + int colorDepth) { + SkASSERT(1 == bitsPerComponent || + 2 == bitsPerComponent || + 4 == bitsPerComponent || + 8 == bitsPerComponent || + 16 == bitsPerComponent); + + switch (color) { + case kGray_Color: + SkASSERT(kOpaque_Alpha == alpha); + break; + case kGrayAlpha_Color: + SkASSERT(kOpaque_Alpha != alpha); + break; + case kPalette_Color: + SkASSERT(16 != bitsPerComponent); + break; + case kRGB_Color: + case kBGR_Color: + case kBGRX_Color: + SkASSERT(kOpaque_Alpha == alpha); + SkASSERT(bitsPerComponent >= 8); + break; + case kYUV_Color: + case kInvertedCMYK_Color: + case kYCCK_Color: + SkASSERT(kOpaque_Alpha == alpha); + SkASSERT(8 == bitsPerComponent); + break; + case kRGBA_Color: + SkASSERT(bitsPerComponent >= 8); + break; + case kBGRA_Color: + case kYUVA_Color: + SkASSERT(8 == bitsPerComponent); + break; + case kXAlpha_Color: + SkASSERT(kUnpremul_Alpha == alpha); + SkASSERT(8 == bitsPerComponent); + break; + case k565_Color: + SkASSERT(kOpaque_Alpha == alpha); + SkASSERT(8 == bitsPerComponent); + break; + default: + SkASSERT(false); + break; + } + + return SkEncodedInfo(width, + height, + color, + alpha, + SkToU8(bitsPerComponent), + SkToU8(colorDepth), + std::move(profile)); + } + + /* + * Returns a recommended SkImageInfo. + * + * TODO: Leave this up to the client. + */ + SkImageInfo makeImageInfo() const { + auto ct = kGray_Color == fColor ? kGray_8_SkColorType : + kXAlpha_Color == fColor ? kAlpha_8_SkColorType : + k565_Color == fColor ? kRGB_565_SkColorType : + kN32_SkColorType ; + auto alpha = kOpaque_Alpha == fAlpha ? kOpaque_SkAlphaType + : kUnpremul_SkAlphaType; + sk_sp cs = fProfile ? SkColorSpace::Make(*fProfile->profile()) + : nullptr; + if (!cs) { + cs = SkColorSpace::MakeSRGB(); + } + return SkImageInfo::Make(fWidth, fHeight, ct, alpha, std::move(cs)); + } + + int width() const { return fWidth; } + int height() const { return fHeight; } + Color color() const { return fColor; } + Alpha alpha() const { return fAlpha; } + bool opaque() const { return fAlpha == kOpaque_Alpha; } + const skcms_ICCProfile* profile() const { + if (!fProfile) return nullptr; + return fProfile->profile(); + } + sk_sp profileData() const { + if (!fProfile) return nullptr; + return fProfile->data(); + } + + uint8_t bitsPerComponent() const { return fBitsPerComponent; } + + uint8_t bitsPerPixel() const { + switch (fColor) { + case kGray_Color: + return fBitsPerComponent; + case kXAlpha_Color: + case kGrayAlpha_Color: + return 2 * fBitsPerComponent; + case kPalette_Color: + return fBitsPerComponent; + case kRGB_Color: + case kBGR_Color: + case kYUV_Color: + case k565_Color: + return 3 * fBitsPerComponent; + case kRGBA_Color: + case kBGRA_Color: + case kBGRX_Color: + case kYUVA_Color: + case kInvertedCMYK_Color: + case kYCCK_Color: + return 4 * fBitsPerComponent; + default: + SkASSERT(false); + return 0; + } + } + + SkEncodedInfo(const SkEncodedInfo& orig) = delete; + SkEncodedInfo& operator=(const SkEncodedInfo&) = delete; + + SkEncodedInfo(SkEncodedInfo&& orig) = default; + SkEncodedInfo& operator=(SkEncodedInfo&&) = default; + + // Explicit copy method, to avoid accidental copying. + SkEncodedInfo copy() const { + auto copy = SkEncodedInfo::Make( + fWidth, fHeight, fColor, fAlpha, fBitsPerComponent, nullptr, fColorDepth); + if (fProfile) { + copy.fProfile = std::make_unique(*fProfile); + } + return copy; + } + + // Return number of bits of R/G/B channel + uint8_t getColorDepth() const { + return fColorDepth; + } + +private: + SkEncodedInfo(int width, int height, Color color, Alpha alpha, + uint8_t bitsPerComponent, uint8_t colorDepth, std::unique_ptr profile) + : fWidth(width) + , fHeight(height) + , fColor(color) + , fAlpha(alpha) + , fBitsPerComponent(bitsPerComponent) + , fColorDepth(colorDepth) + , fProfile(std::move(profile)) + {} + + int fWidth; + int fHeight; + Color fColor; + Alpha fAlpha; + uint8_t fBitsPerComponent; + uint8_t fColorDepth; + std::unique_ptr fProfile; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkExif.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkExif.h new file mode 100644 index 0000000000..41abad3432 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkExif.h @@ -0,0 +1,55 @@ +/* + * Copyright 2023 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkExif_DEFINED +#define SkExif_DEFINED + +#include "include/codec/SkEncodedOrigin.h" +#include "include/private/base/SkAPI.h" + +#include +#include + +class SkData; + +namespace SkExif { + +// Tag values that are parsed by Parse and stored in Metadata. +static constexpr uint16_t kOriginTag = 0x112; +static constexpr uint16_t kResolutionUnitTag = 0x0128; +static constexpr uint16_t kXResolutionTag = 0x011a; +static constexpr uint16_t kYResolutionTag = 0x011b; +static constexpr uint16_t kPixelXDimensionTag = 0xa002; +static constexpr uint16_t kPixelYDimensionTag = 0xa003; + +struct Metadata { + // The image orientation. + std::optional fOrigin; + + // The HDR headroom property. + // https://developer.apple.com/documentation/appkit/images_and_pdf/applying_apple_hdr_effect_to_your_photos + std::optional fHdrHeadroom; + + // Resolution. + std::optional fResolutionUnit; + std::optional fXResolution; + std::optional fYResolution; + + // Size in pixels. + std::optional fPixelXDimension; + std::optional fPixelYDimension; +}; + +/* + * Parse the metadata specified in |data| and write them to |metadata|. Stop only at an + * unrecoverable error (allow truncated input). + */ +void SK_API Parse(Metadata& metadata, const SkData* data); + +} // namespace SkExif + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkGainmapInfo.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkGainmapInfo.h new file mode 100644 index 0000000000..033c9333cb --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkGainmapInfo.h @@ -0,0 +1,140 @@ +/* + * Copyright 2023 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkGainmapInfo_DEFINED +#define SkGainmapInfo_DEFINED + +#include "include/core/SkColor.h" +#include "include/core/SkColorSpace.h" +#include "include/core/SkRefCnt.h" +class SkData; + +/** + * Gainmap rendering parameters. Suppose our display has HDR to SDR ratio of H and we wish to + * display an image with gainmap on this display. Let B be the pixel value from the base image + * in a color space that has the primaries of the base image and a linear transfer function. Let + * G be the pixel value from the gainmap. Let D be the output pixel in the same color space as B. + * The value of D is computed as follows: + * + * First, let W be a weight parameter determing how much the gainmap will be applied. + * W = clamp((log(H) - log(fDisplayRatioSdr)) / + * (log(fDisplayRatioHdr) - log(fDisplayRatioSdr), 0, 1) + * + * Next, let L be the gainmap value in log space. We compute this from the value G that was + * sampled from the texture as follows: + * L = mix(log(fGainmapRatioMin), log(fGainmapRatioMax), pow(G, fGainmapGamma)) + * + * Finally, apply the gainmap to compute D, the displayed pixel. If the base image is SDR then + * compute: + * D = (B + fEpsilonSdr) * exp(L * W) - fEpsilonHdr + * If the base image is HDR then compute: + * D = (B + fEpsilonHdr) * exp(L * (W - 1)) - fEpsilonSdr + * + * In the above math, log() is a natural logarithm and exp() is natural exponentiation. Note, + * however, that the base used for the log() and exp() functions does not affect the results of + * the computation (it cancels out, as long as the same base is used throughout). + * + * This product includes Gain Map technology under license by Adobe. + */ +struct SkGainmapInfo { + /** + * Parameters for converting the gainmap from its image encoding to log space. These are + * specified per color channel. The alpha value is unused. + */ + SkColor4f fGainmapRatioMin = {1.f, 1.f, 1.f, 1.0}; + SkColor4f fGainmapRatioMax = {2.f, 2.f, 2.f, 1.0}; + SkColor4f fGainmapGamma = {1.f, 1.f, 1.f, 1.f}; + + /** + * Parameters sometimes used in gainmap computation to avoid numerical instability. + */ + SkColor4f fEpsilonSdr = {0.f, 0.f, 0.f, 1.0}; + SkColor4f fEpsilonHdr = {0.f, 0.f, 0.f, 1.0}; + + /** + * If the output display's HDR to SDR ratio is less or equal than fDisplayRatioSdr then the SDR + * rendition is displayed. If the output display's HDR to SDR ratio is greater or equal than + * fDisplayRatioHdr then the HDR rendition is displayed. If the output display's HDR to SDR + * ratio is between these values then an interpolation between the two is displayed using the + * math above. + */ + float fDisplayRatioSdr = 1.f; + float fDisplayRatioHdr = 2.f; + + /** + * Whether the base image is the SDR image or the HDR image. + */ + enum class BaseImageType { + kSDR, + kHDR, + }; + BaseImageType fBaseImageType = BaseImageType::kSDR; + + /** + * The type of the gainmap image. If the type is kApple, then the gainmap image was originally + * encoded according to the specification at [0], and can be converted to the kDefault type by + * applying the transformation described at [1]. + * [0] https://developer.apple.com/documentation/appkit/images_and_pdf/ + * applying_apple_hdr_effect_to_your_photos + * [1] https://docs.google.com/document/d/1iUpYAThVV_FuDdeiO3t0vnlfoA1ryq0WfGS9FuydwKc + */ + enum class Type { + kDefault, + kApple, + }; + Type fType = Type::kDefault; + + /** + * If specified, color space to apply the gainmap in, otherwise the base image's color space + * is used. Only the color primaries are used, the transfer function is irrelevant. + */ + sk_sp fGainmapMathColorSpace = nullptr; + + /** + * Return true if this can be encoded as an UltraHDR v1 image. + */ + bool isUltraHDRv1Compatible() const; + + /** + * If |data| contains an ISO 21496-1 version that is supported, return true. Otherwise return + * false. + */ + static bool ParseVersion(const SkData* data); + + /** + * If |data| constains ISO 21496-1 metadata then parse that metadata then use it to populate + * |info| and return true, otherwise return false. If |data| indicates that that the base image + * color space primaries should be used for gainmap application then set + * |fGainmapMathColorSpace| to nullptr, otherwise set |fGainmapMathColorSpace| to sRGB (the + * default, to be overwritten by the image decoder). + */ + static bool Parse(const SkData* data, SkGainmapInfo& info); + + /** + * Serialize an ISO 21496-1 version 0 blob containing only the version structure. + */ + static sk_sp SerializeVersion(); + + /** + * Serialize an ISO 21496-1 version 0 blob containing this' gainmap parameters. + */ + sk_sp serialize() const; + + inline bool operator==(const SkGainmapInfo& other) const { + return fGainmapRatioMin == other.fGainmapRatioMin && + fGainmapRatioMax == other.fGainmapRatioMax && fGainmapGamma == other.fGainmapGamma && + fEpsilonSdr == other.fEpsilonSdr && fEpsilonHdr == other.fEpsilonHdr && + fDisplayRatioSdr == other.fDisplayRatioSdr && + fDisplayRatioHdr == other.fDisplayRatioHdr && + fBaseImageType == other.fBaseImageType && fType == other.fType && + SkColorSpace::Equals(fGainmapMathColorSpace.get(), + other.fGainmapMathColorSpace.get()); + } + inline bool operator!=(const SkGainmapInfo& other) const { return !(*this == other); } +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkGainmapShader.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkGainmapShader.h new file mode 100644 index 0000000000..bc531124c7 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkGainmapShader.h @@ -0,0 +1,54 @@ +/* + * Copyright 2023 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkGainmapShader_DEFINED +#define SkGainmapShader_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/private/base/SkAPI.h" + +class SkColorSpace; +class SkShader; +class SkImage; +struct SkGainmapInfo; +struct SkRect; +struct SkSamplingOptions; + +/** + * A gainmap shader will apply a gainmap to an base image using the math described alongside the + * definition of SkGainmapInfo. + */ +class SK_API SkGainmapShader { +public: + /** + * Make a gainmap shader. + * + * When sampling the base image baseImage, the rectangle baseRect will be sampled to map to + * the rectangle dstRect. Sampling will be done according to baseSamplingOptions. + * + * When sampling the gainmap image gainmapImage, the rectangle gainmapRect will be sampled to + * map to the rectangle dstRect. Sampling will be done according to gainmapSamplingOptions. + * + * The gainmap will be applied according to the HDR to SDR ratio specified in dstHdrRatio. + * + * This shader must know the color space of the canvas that it will be rendered to. This color + * space must be specified in dstColorSpace. + * TODO(ccameron): Remove the need for dstColorSpace. + */ + static sk_sp Make(const sk_sp& baseImage, + const SkRect& baseRect, + const SkSamplingOptions& baseSamplingOptions, + const sk_sp& gainmapImage, + const SkRect& gainmapRect, + const SkSamplingOptions& gainmapSamplingOptions, + const SkGainmapInfo& gainmapInfo, + const SkRect& dstRect, + float dstHdrRatio, + sk_sp dstColorSpace); +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkIDChangeListener.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkIDChangeListener.h new file mode 100644 index 0000000000..8ebb6ca18e --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkIDChangeListener.h @@ -0,0 +1,76 @@ +/* + * Copyright 2020 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkIDChangeListener_DEFINED +#define SkIDChangeListener_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/private/base/SkMutex.h" +#include "include/private/base/SkTArray.h" +#include "include/private/base/SkThreadAnnotations.h" + +#include + +/** + * Used to be notified when a gen/unique ID is invalidated, typically to preemptively purge + * associated items from a cache that are no longer reachable. The listener can + * be marked for deregistration if the cached item is remove before the listener is + * triggered. This prevents unbounded listener growth when cache items are routinely + * removed before the gen ID/unique ID is invalidated. + */ +class SkIDChangeListener : public SkRefCnt { +public: + SkIDChangeListener(); + + ~SkIDChangeListener() override; + + virtual void changed() = 0; + + /** + * Mark the listener is no longer needed. It should be removed and changed() should not be + * called. + */ + void markShouldDeregister() { fShouldDeregister.store(true, std::memory_order_relaxed); } + + /** Indicates whether markShouldDeregister was called. */ + bool shouldDeregister() { return fShouldDeregister.load(std::memory_order_acquire); } + + /** Manages a list of SkIDChangeListeners. */ + class List { + public: + List(); + + ~List(); + + /** + * Add a new listener to the list. It must not already be deregistered. Also clears out + * previously deregistered listeners. + */ + void add(sk_sp listener) SK_EXCLUDES(fMutex); + + /** + * The number of registered listeners (including deregisterd listeners that are yet-to-be + * removed. + */ + int count() const SK_EXCLUDES(fMutex); + + /** Calls changed() on all listeners that haven't been deregistered and resets the list. */ + void changed() SK_EXCLUDES(fMutex); + + /** Resets without calling changed() on the listeners. */ + void reset() SK_EXCLUDES(fMutex); + + private: + mutable SkMutex fMutex; + skia_private::STArray<1, sk_sp> fListeners SK_GUARDED_BY(fMutex); + }; + +private: + std::atomic fShouldDeregister; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkJpegGainmapEncoder.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkJpegGainmapEncoder.h new file mode 100644 index 0000000000..0b4d4babc8 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkJpegGainmapEncoder.h @@ -0,0 +1,48 @@ +/* + * Copyright 2023 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkJpegGainmapEncoder_DEFINED +#define SkJpegGainmapEncoder_DEFINED + +#include "include/encode/SkJpegEncoder.h" + +class SkPixmap; +class SkWStream; +struct SkGainmapInfo; + +class SK_API SkJpegGainmapEncoder { +public: + /** + * Encode an UltraHDR image to |dst|. + * + * The base image is specified by |base|, and |baseOptions| controls the encoding behavior for + * the base image. + * + * The gainmap image is specified by |gainmap|, and |gainmapOptions| controls the encoding + * behavior for the gainmap image. + * + * The rendering behavior of the gainmap image is provided in |gainmapInfo|. + * + * If |baseOptions| or |gainmapOptions| specify XMP metadata, then that metadata will be + * overwritten. + * + * Returns true on success. Returns false on an invalid or unsupported |src|. + */ + static bool EncodeHDRGM(SkWStream* dst, + const SkPixmap& base, + const SkJpegEncoder::Options& baseOptions, + const SkPixmap& gainmap, + const SkJpegEncoder::Options& gainmapOptions, + const SkGainmapInfo& gainmapInfo); + + /** + * Write a Multi Picture Format containing the |imageCount| images specified by |images|. + */ + static bool MakeMPF(SkWStream* dst, const SkData** images, size_t imageCount); +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkJpegMetadataDecoder.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkJpegMetadataDecoder.h new file mode 100644 index 0000000000..c9e0b3406d --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkJpegMetadataDecoder.h @@ -0,0 +1,86 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkJpegMetadataDecoder_DEFINED +#define SkJpegMetadataDecoder_DEFINED + +#include "include/core/SkData.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkTypes.h" + +#include +#include + +struct SkGainmapInfo; + +/** + * An interface that can be used to extract metadata from an encoded JPEG file. + */ +class SK_API SkJpegMetadataDecoder { +public: + SkJpegMetadataDecoder() {} + virtual ~SkJpegMetadataDecoder() {} + + SkJpegMetadataDecoder(const SkJpegMetadataDecoder&) = delete; + SkJpegMetadataDecoder& operator=(const SkJpegMetadataDecoder&) = delete; + + /** + * A segment from a JPEG file. This is usually populated from a jpeg_marker_struct. + */ + struct SK_API Segment { + Segment(uint8_t marker, sk_sp data) : fMarker(marker), fData(std::move(data)) {} + + // The segment's marker. + uint8_t fMarker = 0; + + // The segment's parameters (not including the marker and parameter length). + sk_sp fData; + }; + + /** + * Create metadata for the specified segments from a JPEG file's header (defined as all segments + * before the first StartOfScan). This may return nullptr. + */ + static std::unique_ptr Make(std::vector headerSegments); + + /** + * Return the Exif data attached to the image (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 getExifMetadata(bool copyData) const = 0; + + /** + * Return the ICC profile of the image 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 getICCProfileData(bool copyData) const = 0; + + /** + * Return the ISO 21496-1 metadata, 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 getISOGainmapMetadata(bool copyData) const = 0; + + /** + * Return true if there is a possibility that this image contains a gainmap image. + */ + virtual bool mightHaveGainmapImage() const = 0; + + /** + * Given a JPEG encoded image |baseImageData|, return in |outGainmapImageData| the JPEG encoded + * gainmap image and return in |outGainmapInfo| its gainmap rendering parameters. Return true if + * both output variables were successfully populated, otherwise return false. + */ + virtual bool findGainmapImage(sk_sp baseImageData, + sk_sp& outGainmapImagedata, + SkGainmapInfo& outGainmapInfo) = 0; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkPathRef.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkPathRef.h new file mode 100644 index 0000000000..6b2d8eccee --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkPathRef.h @@ -0,0 +1,582 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkPathRef_DEFINED +#define SkPathRef_DEFINED + +#include "include/core/SkArc.h" +#include "include/core/SkPoint.h" +#include "include/core/SkRect.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" +#include "include/private/SkIDChangeListener.h" +#include "include/private/base/SkDebug.h" +#include "include/private/base/SkTArray.h" +#include "include/private/base/SkTo.h" + +#include +#include +#include +#include +#include + +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 + * SkPathRef::Editor object. Installing the editor resets the generation ID. It also performs + * copy-on-write if the SkPathRef is shared by multiple SkPaths. The caller passes the Editor's + * constructor a pointer to a sk_sp, which may be updated to point to a new SkPathRef + * after the editor's constructor returns. + * + * The points and verbs are stored in a single allocation. The points are at the begining of the + * allocation while the verbs are stored at end of the allocation, in reverse order. Thus the points + * and verbs both grow into the middle of the allocation until the meet. To access verb i in the + * verb array use ref.verbs()[~i] (because verbs() returns a pointer just beyond the first + * logical verb or the last verb in memory). + */ + +class SK_API SkPathRef final : public SkNVRefCnt { +public: + // See https://bugs.chromium.org/p/skia/issues/detail?id=13817 for how these sizes were + // determined. + using PointsArray = skia_private::STArray<4, SkPoint>; + using VerbsArray = skia_private::STArray<4, uint8_t>; + using ConicWeightsArray = skia_private::STArray<2, SkScalar>; + + enum class PathType : uint8_t { + kGeneral, + kOval, + kOpenOval, // An unclosed oval, as is generated by canvas2d ellipse or arc + kRRect, + kArc, + }; + + SkPathRef(PointsArray points, VerbsArray verbs, ConicWeightsArray weights, + unsigned segmentMask) + : fPoints(std::move(points)) + , fVerbs(std::move(verbs)) + , fConicWeights(std::move(weights)) + { + fBoundsIsDirty = true; // this also invalidates fIsFinite + fGenerationID = 0; // recompute + fSegmentMask = segmentMask; + fType = PathType::kGeneral; + // The next two values don't matter unless fType is kOval or kRRect + fRRectOrOvalIsCCW = false; + fRRectOrOvalStartIdx = 0xAC; + fArcOval.setEmpty(); + fArcStartAngle = fArcSweepAngle = 0.0f; + fArcType = SkArc::Type::kArc; + SkDEBUGCODE(fEditorsAttached.store(0);) + + this->computeBounds(); // do this now, before we worry about multiple owners/threads + SkDEBUGCODE(this->validate();) + } + + class Editor { + public: + Editor(sk_sp* pathRef, + int incReserveVerbs = 0, + int incReservePoints = 0, + int incReserveConics = 0); + + ~Editor() { SkDEBUGCODE(fPathRef->fEditorsAttached--;) } + + /** + * Returns the array of points. + */ + SkPoint* writablePoints() { return fPathRef->getWritablePoints(); } + const SkPoint* points() const { return fPathRef->points(); } + + /** + * Gets the ith point. Shortcut for this->points() + i + */ + SkPoint* atPoint(int i) { return fPathRef->getWritablePoints() + i; } + const SkPoint* atPoint(int i) const { return &fPathRef->fPoints[i]; } + + /** + * Adds the verb and allocates space for the number of points indicated by the verb. The + * return value is a pointer to where the points for the verb should be written. + * 'weight' is only used if 'verb' is kConic_Verb + */ + SkPoint* growForVerb(int /*SkPath::Verb*/ verb, SkScalar weight = 0) { + SkDEBUGCODE(fPathRef->validate();) + return fPathRef->growForVerb(verb, weight); + } + + /** + * Allocates space for multiple instances of a particular verb and the + * requisite points & weights. + * The return pointer points at the first new point (indexed normally []). + * If 'verb' is kConic_Verb, 'weights' will return a pointer to the + * space for the conic weights (indexed normally). + */ + SkPoint* growForRepeatedVerb(int /*SkPath::Verb*/ verb, + int numVbs, + SkScalar** weights = nullptr) { + return fPathRef->growForRepeatedVerb(verb, numVbs, weights); + } + + /** + * Concatenates all verbs from 'path' onto the pathRef's verbs array. Increases the point + * count by the number of points in 'path', and the conic weight count by the number of + * conics in 'path'. + * + * Returns pointers to the uninitialized points and conic weights data. + */ + std::tuple growForVerbsInPath(const SkPathRef& path) { + return fPathRef->growForVerbsInPath(path); + } + + /** + * Resets the path ref to a new verb and point count. The new verbs and points are + * uninitialized. + */ + void resetToSize(int newVerbCnt, int newPointCnt, int newConicCount) { + fPathRef->resetToSize(newVerbCnt, newPointCnt, newConicCount); + } + + /** + * Gets the path ref that is wrapped in the Editor. + */ + SkPathRef* pathRef() { return fPathRef; } + + void setIsOval(bool isCCW, unsigned start, bool isClosed) { + fPathRef->setIsOval(isCCW, start, isClosed); + } + + void setIsRRect(bool isCCW, unsigned start) { + fPathRef->setIsRRect(isCCW, start); + } + + void setIsArc(const SkArc& arc) { + fPathRef->setIsArc(arc); + } + + void setBounds(const SkRect& rect) { fPathRef->setBounds(rect); } + + private: + SkPathRef* fPathRef; + }; + + class SK_API Iter { + public: + Iter(); + Iter(const SkPathRef&); + + void setPathRef(const SkPathRef&); + + /** Return the next verb in this iteration of the path. When all + segments have been visited, return kDone_Verb. + + If any point in the path is non-finite, return kDone_Verb immediately. + + @param pts The points representing the current verb and/or segment + This must not be NULL. + @return The verb for the current segment + */ + uint8_t next(SkPoint pts[4]); + uint8_t peek() const; + + SkScalar conicWeight() const { return *fConicWeights; } + + private: + const SkPoint* fPts; + const uint8_t* fVerbs; + const uint8_t* fVerbStop; + const SkScalar* fConicWeights; + }; + +public: + /** + * Gets a path ref with no verbs or points. + */ + static SkPathRef* CreateEmpty(); + + /** + * Returns true if all of the points in this path are finite, meaning there + * are no infinities and no NaNs. + */ + bool isFinite() const { + if (fBoundsIsDirty) { + this->computeBounds(); + } + return SkToBool(fIsFinite); + } + + /** + * Returns a mask, where each bit corresponding to a SegmentMask is + * set if the path contains 1 or more segments of that type. + * Returns 0 for an empty path (no segments). + */ + uint32_t getSegmentMasks() const { return fSegmentMask; } + + /** Returns true if the path is an oval. + * + * @param rect returns the bounding rect of this oval. It's a circle + * if the height and width are the same. + * @param isCCW is the oval CCW (or CW if false). + * @param start indicates where the contour starts on the oval (see + * SkPath::addOval for intepretation of the index). + * + * @return true if this path is an oval. + * Tracking whether a path is an oval is considered an + * optimization for performance and so some paths that are in + * fact ovals can report false. + */ + bool isOval(SkRect* rect, bool* isCCW, unsigned* start) const { + if (fType == PathType::kOval) { + if (rect) { + *rect = this->getBounds(); + } + if (isCCW) { + *isCCW = SkToBool(fRRectOrOvalIsCCW); + } + if (start) { + *start = fRRectOrOvalStartIdx; + } + } + + return fType == PathType::kOval; + } + + bool isRRect(SkRRect* rrect, bool* isCCW, unsigned* start) const; + + bool isArc(SkArc* arc) const { + if (fType == PathType::kArc) { + if (arc) { + *arc = SkArc::Make(fArcOval, fArcStartAngle, fArcSweepAngle, fArcType); + } + } + + return fType == PathType::kArc; + } + + bool hasComputedBounds() const { + return !fBoundsIsDirty; + } + + /** Returns the bounds of the path's points. If the path contains 0 or 1 + points, the bounds is set to (0,0,0,0), and isEmpty() will return true. + Note: this bounds may be larger than the actual shape, since curves + do not extend as far as their control points. + */ + const SkRect& getBounds() const { + if (fBoundsIsDirty) { + this->computeBounds(); + } + return fBounds; + } + + SkRRect getRRect() const; + + /** + * Transforms a path ref by a matrix, allocating a new one only if necessary. + */ + static void CreateTransformedCopy(sk_sp* dst, + const SkPathRef& src, + const SkMatrix& matrix); + + // static SkPathRef* CreateFromBuffer(SkRBuffer* buffer); + + /** + * Rollsback a path ref to zero verbs and points with the assumption that the path ref will be + * repopulated with approximately the same number of verbs and points. A new path ref is created + * only if necessary. + */ + static void Rewind(sk_sp* pathRef); + + ~SkPathRef(); + int countPoints() const { return fPoints.size(); } + int countVerbs() const { return fVerbs.size(); } + int countWeights() const { return fConicWeights.size(); } + + size_t approximateBytesUsed() const; + + /** + * Returns a pointer one beyond the first logical verb (last verb in memory order). + */ + const uint8_t* verbsBegin() const { return fVerbs.begin(); } + + /** + * Returns a const pointer to the first verb in memory (which is the last logical verb). + */ + const uint8_t* verbsEnd() const { return fVerbs.end(); } + + /** + * Returns a const pointer to the first point. + */ + const SkPoint* points() const { return fPoints.begin(); } + + /** + * Shortcut for this->points() + this->countPoints() + */ + const SkPoint* pointsEnd() const { return this->points() + this->countPoints(); } + + const SkScalar* conicWeights() const { return fConicWeights.begin(); } + const SkScalar* conicWeightsEnd() const { return fConicWeights.end(); } + + /** + * Convenience methods for getting to a verb or point by index. + */ + uint8_t atVerb(int index) const { return fVerbs[index]; } + const SkPoint& atPoint(int index) const { return fPoints[index]; } + + bool operator== (const SkPathRef& ref) const; + + void interpolate(const SkPathRef& ending, SkScalar weight, SkPathRef* out) const; + + /** + * Gets an ID that uniquely identifies the contents of the path ref. If two path refs have the + * same ID then they have the same verbs and points. However, two path refs may have the same + * contents but different genIDs. + * skbug.com/1762 for background on why fillType is necessary (for now). + */ + uint32_t genID(uint8_t fillType) const; + + void addGenIDChangeListener(sk_sp); // Threadsafe. + int genIDChangeListenerCount(); // Threadsafe + + bool dataMatchesVerbs() const; + bool isValid() const; + SkDEBUGCODE(void validate() const { SkASSERT(this->isValid()); } ) + + /** + * Resets this SkPathRef to a clean state. + */ + void reset(); + + bool isInitialEmptyPathRef() const { + return fGenerationID == kEmptyGenID; + } + +private: + enum SerializationOffsets { + kLegacyRRectOrOvalStartIdx_SerializationShift = 28, // requires 3 bits, ignored. + kLegacyRRectOrOvalIsCCW_SerializationShift = 27, // requires 1 bit, ignored. + kLegacyIsRRect_SerializationShift = 26, // requires 1 bit, ignored. + kIsFinite_SerializationShift = 25, // requires 1 bit + kLegacyIsOval_SerializationShift = 24, // requires 1 bit, ignored. + kSegmentMask_SerializationShift = 0 // requires 4 bits (deprecated) + }; + + SkPathRef(int numVerbs = 0, int numPoints = 0, int numConics = 0) { + fBoundsIsDirty = true; // this also invalidates fIsFinite + fGenerationID = kEmptyGenID; + fSegmentMask = 0; + fType = PathType::kGeneral; + // The next two values don't matter unless fType is kOval or kRRect + fRRectOrOvalIsCCW = false; + fRRectOrOvalStartIdx = 0xAC; + fArcOval.setEmpty(); + fArcStartAngle = fArcSweepAngle = 0.0f; + fArcType = SkArc::Type::kArc; + if (numPoints > 0) { + fPoints.reserve_exact(numPoints); + } + if (numVerbs > 0) { + fVerbs.reserve_exact(numVerbs); + } + if (numConics > 0) { + fConicWeights.reserve_exact(numConics); + } + SkDEBUGCODE(fEditorsAttached.store(0);) + SkDEBUGCODE(this->validate();) + } + + void copy(const SkPathRef& ref, int additionalReserveVerbs, int additionalReservePoints, int additionalReserveConics); + + // Return true if the computed bounds are finite. + static bool ComputePtBounds(SkRect* bounds, const SkPathRef& ref) { + return bounds->setBoundsCheck(ref.points(), ref.countPoints()); + } + + // called, if dirty, by getBounds() + void computeBounds() const { + SkDEBUGCODE(this->validate();) + // TODO: remove fBoundsIsDirty and fIsFinite, + // using an inverted rect instead of fBoundsIsDirty and always recalculating fIsFinite. + SkASSERT(fBoundsIsDirty); + + fIsFinite = ComputePtBounds(&fBounds, *this); + fBoundsIsDirty = false; + } + + void setBounds(const SkRect& rect) { + SkASSERT(rect.fLeft <= rect.fRight && rect.fTop <= rect.fBottom); + fBounds = rect; + fBoundsIsDirty = false; + fIsFinite = fBounds.isFinite(); + } + + /** Makes additional room but does not change the counts or change the genID */ + void incReserve(int additionalVerbs, int additionalPoints, int additionalConics) { + SkDEBUGCODE(this->validate();) + // Use reserve() so that if there is not enough space, the array will grow with some + // additional space. This ensures repeated calls to grow won't always allocate. + if (additionalPoints > 0) { + fPoints.reserve(fPoints.size() + additionalPoints); + } + if (additionalVerbs > 0) { + fVerbs.reserve(fVerbs.size() + additionalVerbs); + } + if (additionalConics > 0) { + fConicWeights.reserve(fConicWeights.size() + additionalConics); + } + SkDEBUGCODE(this->validate();) + } + + /** + * Resets all state except that of the verbs, points, and conic-weights. + * Intended to be called from other functions that reset state. + */ + void commonReset() { + SkDEBUGCODE(this->validate();) + this->callGenIDChangeListeners(); + fBoundsIsDirty = true; // this also invalidates fIsFinite + fGenerationID = 0; + + fSegmentMask = 0; + fType = PathType::kGeneral; + } + + /** Resets the path ref with verbCount verbs and pointCount points, all uninitialized. Also + * allocates space for reserveVerb additional verbs and reservePoints additional points.*/ + void resetToSize(int verbCount, int pointCount, int conicCount, + int reserveVerbs = 0, int reservePoints = 0, + int reserveConics = 0) { + this->commonReset(); + // Use reserve_exact() so the arrays are sized to exactly fit the data. + fPoints.reserve_exact(pointCount + reservePoints); + fPoints.resize_back(pointCount); + + fVerbs.reserve_exact(verbCount + reserveVerbs); + fVerbs.resize_back(verbCount); + + fConicWeights.reserve_exact(conicCount + reserveConics); + fConicWeights.resize_back(conicCount); + SkDEBUGCODE(this->validate();) + } + + /** + * Increases the verb count by numVbs and point count by the required amount. + * The new points are uninitialized. All the new verbs are set to the specified + * verb. If 'verb' is kConic_Verb, 'weights' will return a pointer to the + * uninitialized conic weights. + */ + SkPoint* growForRepeatedVerb(int /*SkPath::Verb*/ verb, int numVbs, SkScalar** weights); + + /** + * Increases the verb count 1, records the new verb, and creates room for the requisite number + * of additional points. A pointer to the first point is returned. Any new points are + * uninitialized. + */ + SkPoint* growForVerb(int /*SkPath::Verb*/ verb, SkScalar weight); + + /** + * Concatenates all verbs from 'path' onto our own verbs array. Increases the point count by the + * number of points in 'path', and the conic weight count by the number of conics in 'path'. + * + * Returns pointers to the uninitialized points and conic weights data. + */ + std::tuple growForVerbsInPath(const SkPathRef& path); + + /** + * Private, non-const-ptr version of the public function verbsMemBegin(). + */ + uint8_t* verbsBeginWritable() { return fVerbs.begin(); } + + /** + * Called the first time someone calls CreateEmpty to actually create the singleton. + */ + friend SkPathRef* sk_create_empty_pathref(); + + void setIsOval(bool isCCW, unsigned start, bool isClosed) { + fType = isClosed ? PathType::kOval : PathType::kOpenOval; + fRRectOrOvalIsCCW = isCCW; + fRRectOrOvalStartIdx = SkToU8(start); + } + + void setIsRRect(bool isCCW, unsigned start) { + fType = PathType::kRRect; + fRRectOrOvalIsCCW = isCCW; + fRRectOrOvalStartIdx = SkToU8(start); + } + + void setIsArc(const SkArc& arc) { + fType = PathType::kArc; + fArcOval = arc.fOval; + fArcStartAngle = arc.fStartAngle; + fArcSweepAngle = arc.fSweepAngle; + fArcType = arc.fType; + } + + // called only by the editor. Note that this is not a const function. + SkPoint* getWritablePoints() { + SkDEBUGCODE(this->validate();) + fType = PathType::kGeneral; + return fPoints.begin(); + } + + const SkPoint* getPoints() const { + SkDEBUGCODE(this->validate();) + return fPoints.begin(); + } + + void callGenIDChangeListeners(); + + mutable SkRect fBounds; + + enum { + kEmptyGenID = 1, // GenID reserved for path ref with zero points and zero verbs. + }; + mutable uint32_t fGenerationID; + SkIDChangeListener::List fGenIDChangeListeners; + + PointsArray fPoints; + VerbsArray fVerbs; + ConicWeightsArray fConicWeights; + + SkDEBUGCODE(std::atomic fEditorsAttached;) // assert only one editor in use at any time. + + mutable uint8_t fBoundsIsDirty; + mutable bool fIsFinite; // only meaningful if bounds are valid + + 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; + 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; + + friend class PathRefTest_Private; + friend class ForceIsRRect_Private; // unit test isRRect + friend class SkPath; + friend class SkPathBuilder; + friend class SkPathPriv; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkSLSampleUsage.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkSLSampleUsage.h new file mode 100644 index 0000000000..39d9e25818 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkSLSampleUsage.h @@ -0,0 +1,85 @@ +/* + * Copyright 2020 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkSLSampleUsage_DEFINED +#define SkSLSampleUsage_DEFINED + +#include "include/core/SkTypes.h" + +namespace SkSL { + +/** + * Represents all of the ways that a fragment processor is sampled by its parent. + */ +class SampleUsage { +public: + enum class Kind { + // Child is never sampled + kNone, + // Child is only sampled at the same coordinates as the parent + kPassThrough, + // Child is sampled with a matrix whose value is uniform + kUniformMatrix, + // Child is sampled with sk_FragCoord.xy + kFragCoord, + // Child is sampled using explicit coordinates + kExplicit, + }; + + // Make a SampleUsage that corresponds to no sampling of the child at all + SampleUsage() = default; + + SampleUsage(Kind kind, bool hasPerspective) : fKind(kind), fHasPerspective(hasPerspective) { + if (kind != Kind::kUniformMatrix) { + SkASSERT(!fHasPerspective); + } + } + + // Child is sampled with a matrix whose value is uniform. The name is fixed. + static SampleUsage UniformMatrix(bool hasPerspective) { + return SampleUsage(Kind::kUniformMatrix, hasPerspective); + } + + static SampleUsage Explicit() { + return SampleUsage(Kind::kExplicit, false); + } + + static SampleUsage PassThrough() { + return SampleUsage(Kind::kPassThrough, false); + } + + static SampleUsage FragCoord() { return SampleUsage(Kind::kFragCoord, false); } + + bool operator==(const SampleUsage& that) const { + return fKind == that.fKind && fHasPerspective == that.fHasPerspective; + } + + bool operator!=(const SampleUsage& that) const { return !(*this == that); } + + // Arbitrary name used by all uniform sampling matrices + static const char* MatrixUniformName() { return "matrix"; } + + SampleUsage merge(const SampleUsage& other); + + Kind kind() const { return fKind; } + + bool hasPerspective() const { return fHasPerspective; } + + bool isSampled() const { return fKind != Kind::kNone; } + bool isPassThrough() const { return fKind == Kind::kPassThrough; } + bool isExplicit() const { return fKind == Kind::kExplicit; } + bool isUniformMatrix() const { return fKind == Kind::kUniformMatrix; } + bool isFragCoord() const { return fKind == Kind::kFragCoord; } + +private: + Kind fKind = Kind::kNone; + bool fHasPerspective = false; // Only valid if fKind is kUniformMatrix +}; + +} // namespace SkSL + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkWeakRefCnt.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkWeakRefCnt.h new file mode 100644 index 0000000000..4f949a4843 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkWeakRefCnt.h @@ -0,0 +1,173 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkWeakRefCnt_DEFINED +#define SkWeakRefCnt_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkTypes.h" + +#include +#include + +/** \class SkWeakRefCnt + + SkWeakRefCnt is the base class for objects that may be shared by multiple + objects. When an existing strong owner wants to share a reference, it calls + ref(). When a strong owner wants to release its reference, it calls + unref(). When the shared object's strong reference count goes to zero as + the result of an unref() call, its (virtual) weak_dispose method is called. + It is an error for the destructor to be called explicitly (or via the + object going out of scope on the stack or calling delete) if + getRefCnt() > 1. + + In addition to strong ownership, an owner may instead obtain a weak + reference by calling weak_ref(). A call to weak_ref() must be balanced by a + call to weak_unref(). To obtain a strong reference from a weak reference, + call try_ref(). If try_ref() returns true, the owner's pointer is now also + a strong reference on which unref() must be called. Note that this does not + affect the original weak reference, weak_unref() must still be called. When + the weak reference count goes to zero, the object is deleted. While the + weak reference count is positive and the strong reference count is zero the + object still exists, but will be in the disposed state. It is up to the + object to define what this means. + + Note that a strong reference implicitly implies a weak reference. As a + result, it is allowable for the owner of a strong ref to call try_ref(). + This will have the same effect as calling ref(), but may be more expensive. + + Example: + + SkWeakRefCnt myRef = strongRef.weak_ref(); + ... // strongRef.unref() may or may not be called + if (myRef.try_ref()) { + ... // use myRef + myRef.unref(); + } else { + // myRef is in the disposed state + } + myRef.weak_unref(); +*/ +class SK_API SkWeakRefCnt : public SkRefCnt { +public: + /** Default construct, initializing the reference counts to 1. + The strong references collectively hold one weak reference. When the + strong reference count goes to zero, the collectively held weak + reference is released. + */ + SkWeakRefCnt() : SkRefCnt(), fWeakCnt(1) {} + + /** Destruct, asserting that the weak reference count is 1. + */ + ~SkWeakRefCnt() override { +#ifdef SK_DEBUG + SkASSERT(getWeakCnt() == 1); + fWeakCnt.store(0, std::memory_order_relaxed); +#endif + } + +#ifdef SK_DEBUG + /** Return the weak reference count. */ + int32_t getWeakCnt() const { + return fWeakCnt.load(std::memory_order_relaxed); + } +#endif + +private: + /** If fRefCnt is 0, returns 0. + * Otherwise increments fRefCnt, acquires, and returns the old value. + */ + int32_t atomic_conditional_acquire_strong_ref() const { + int32_t prev = fRefCnt.load(std::memory_order_relaxed); + do { + if (0 == prev) { + break; + } + } while(!fRefCnt.compare_exchange_weak(prev, prev+1, std::memory_order_acquire, + std::memory_order_relaxed)); + return prev; + } + +public: + /** Creates a strong reference from a weak reference, if possible. The + caller must already be an owner. If try_ref() returns true the owner + is in posession of an additional strong reference. Both the original + reference and new reference must be properly unreferenced. If try_ref() + returns false, no strong reference could be created and the owner's + reference is in the same state as before the call. + */ + [[nodiscard]] bool try_ref() const { + if (atomic_conditional_acquire_strong_ref() != 0) { + // Acquire barrier (L/SL), if not provided above. + // Prevents subsequent code from happening before the increment. + return true; + } + return false; + } + + /** Increment the weak reference count. Must be balanced by a call to + weak_unref(). + */ + void weak_ref() const { + SkASSERT(getRefCnt() > 0); + SkASSERT(getWeakCnt() > 0); + // No barrier required. + (void)fWeakCnt.fetch_add(+1, std::memory_order_relaxed); + } + + /** Decrement the weak reference count. If the weak reference count is 1 + before the decrement, then call delete on the object. Note that if this + is the case, then the object needs to have been allocated via new, and + not on the stack. + */ + void weak_unref() const { + SkASSERT(getWeakCnt() > 0); + // A release here acts in place of all releases we "should" have been doing in ref(). + if (1 == fWeakCnt.fetch_add(-1, std::memory_order_acq_rel)) { + // Like try_ref(), the acquire is only needed on success, to make sure + // code in internal_dispose() doesn't happen before the decrement. +#ifdef SK_DEBUG + // so our destructor won't complain + fWeakCnt.store(1, std::memory_order_relaxed); +#endif + this->INHERITED::internal_dispose(); + } + } + + /** Returns true if there are no strong references to the object. When this + is the case all future calls to try_ref() will return false. + */ + bool weak_expired() const { + return fRefCnt.load(std::memory_order_relaxed) == 0; + } + +protected: + /** Called when the strong reference count goes to zero. This allows the + object to free any resources it may be holding. Weak references may + still exist and their level of allowed access to the object is defined + by the object's class. + */ + virtual void weak_dispose() const { + } + +private: + /** Called when the strong reference count goes to zero. Calls weak_dispose + on the object and releases the implicit weak reference held + collectively by the strong references. + */ + void internal_dispose() const override { + weak_dispose(); + weak_unref(); + } + + /* Invariant: fWeakCnt = #weak + (fRefCnt > 0 ? 1 : 0) */ + mutable std::atomic fWeakCnt; + + using INHERITED = SkRefCnt; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkXmp.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkXmp.h new file mode 100644 index 0000000000..58ae2338d6 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/SkXmp.h @@ -0,0 +1,62 @@ +/* + * Copyright 2023 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkXmp_DEFINED +#define SkXmp_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/private/base/SkAPI.h" + +class SkData; +struct SkGainmapInfo; + +#include +#include + +/* + * An interface to extract information from XMP metadata. + */ +class SK_API SkXmp { +public: + SkXmp() = default; + virtual ~SkXmp() = default; + // Make noncopyable + SkXmp(const SkXmp&) = delete; + SkXmp& operator= (const SkXmp&) = delete; + + // Create from XMP data. + static std::unique_ptr Make(sk_sp xmpData); + // Create from standard XMP + extended XMP data, see XMP Specification Part 3: Storage in files, + // Section 1.1.3.1: Extended XMP in JPEG + static std::unique_ptr Make(sk_sp xmpStandard, sk_sp xmpExtended); + + // Extract HDRGM gainmap parameters. + // TODO(b/338342146): Remove this once all callers are removed. + bool getGainmapInfoHDRGM(SkGainmapInfo* info) const { return getGainmapInfoAdobe(info); } + + // Extract gainmap parameters from http://ns.adobe.com/hdr-gain-map/1.0/. + virtual bool getGainmapInfoAdobe(SkGainmapInfo* info) const = 0; + + // If the image specifies http://ns.apple.com/pixeldatainfo/1.0/ AuxiliaryImageType of + // urn:com:apple:photo:2020:aux:hdrgainmap, and includes a http://ns.apple.com/HDRGainMap/1.0/ + // HDRGainMapVersion, then populate |info| with gainmap parameters that will approximate the + // math specified at [0] and return true. + // [0] https://developer.apple.com/documentation/appkit/images_and_pdf/ + // applying_apple_hdr_effect_to_your_photos + virtual bool getGainmapInfoApple(float exifHdrHeadroom, SkGainmapInfo* info) const = 0; + + // If this includes GContainer metadata and the GContainer contains an item with semantic + // GainMap and Mime of image/jpeg, then return true, and populate |offset| and |size| with + // that item's offset (from the end of the primary JPEG image's EndOfImage), and the size of + // the gainmap. + virtual bool getContainerGainmapLocation(size_t* offset, size_t* size) const = 0; + + // Return the GUID of an Extended XMP if present, or null otherwise. + virtual const char* getExtendedXmpGuid() const = 0; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/README.md b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/README.md new file mode 100644 index 0000000000..7f4f17b228 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/README.md @@ -0,0 +1,4 @@ +Files in "base" are used by many parts of Skia, but are not part of the public Skia API. +See also src/base for other files that are part of base, but not needed by the public API. + +Files here should not depend on anything other than system headers or other files in base. \ No newline at end of file diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SingleOwner.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SingleOwner.h new file mode 100644 index 0000000000..473981e1fb --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SingleOwner.h @@ -0,0 +1,75 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef skgpu_SingleOwner_DEFINED +#define skgpu_SingleOwner_DEFINED + +#include "include/private/base/SkDebug.h" // IWYU pragma: keep + +#if defined(SK_DEBUG) +#include "include/private/base/SkAssert.h" +#include "include/private/base/SkMutex.h" +#include "include/private/base/SkThreadAnnotations.h" +#include "include/private/base/SkThreadID.h" + +#endif + +namespace skgpu { + +#if defined(SK_DEBUG) + +#define SKGPU_ASSERT_SINGLE_OWNER(obj) \ + skgpu::SingleOwner::AutoEnforce debug_SingleOwner(obj, __FILE__, __LINE__); + +// This is a debug tool to verify an object is only being used from one thread at a time. +class SingleOwner { +public: + SingleOwner() : fOwner(kIllegalThreadID), fReentranceCount(0) {} + + struct AutoEnforce { + AutoEnforce(SingleOwner* so, const char* file, int line) + : fFile(file), fLine(line), fSO(so) { + fSO->enter(file, line); + } + ~AutoEnforce() { fSO->exit(fFile, fLine); } + + const char* fFile; + int fLine; + SingleOwner* fSO; + }; + +private: + void enter(const char* file, int line) { + SkAutoMutexExclusive lock(fMutex); + SkThreadID self = SkGetThreadID(); + SkASSERTF(fOwner == self || fOwner == kIllegalThreadID, "%s:%d Single owner failure.", + file, line); + fReentranceCount++; + fOwner = self; + } + + void exit(const char* file, int line) { + SkAutoMutexExclusive lock(fMutex); + SkASSERTF(fOwner == SkGetThreadID(), "%s:%d Single owner failure.", file, line); + fReentranceCount--; + if (fReentranceCount == 0) { + fOwner = kIllegalThreadID; + } + } + + SkMutex fMutex; + SkThreadID fOwner SK_GUARDED_BY(fMutex); + int fReentranceCount SK_GUARDED_BY(fMutex); +}; +#else +#define SKGPU_ASSERT_SINGLE_OWNER(obj) +class SingleOwner {}; // Provide a no-op implementation so we can pass pointers to constructors +#endif + +} // namespace skgpu + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkAPI.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkAPI.h new file mode 100644 index 0000000000..4028f95d87 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkAPI.h @@ -0,0 +1,52 @@ +/* + * 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 SkAPI_DEFINED +#define SkAPI_DEFINED + +#include "include/private/base/SkLoadUserConfig.h" // IWYU pragma: keep + +// If SKIA_IMPLEMENTATION is defined as 1, that signals we are building Skia and should +// export our symbols. If it is not set (or set to 0), then Skia is being used by a client +// and we should not export our symbols. +#if !defined(SKIA_IMPLEMENTATION) + #define SKIA_IMPLEMENTATION 0 +#endif + +// If we are compiling Skia is being as a DLL, we need to be sure to export all of our public +// APIs to that DLL. If a client is using Skia which was compiled as a DLL, we need to instruct +// the linker to use the symbols from that DLL. This is the goal of the SK_API define. +#if !defined(SK_API) + #if defined(SKIA_DLL) + #if defined(_MSC_VER) + #if SKIA_IMPLEMENTATION + #define SK_API __declspec(dllexport) + #else + #define SK_API __declspec(dllimport) + #endif + #else + #define SK_API __attribute__((visibility("default"))) + #endif + #else + #define SK_API + #endif +#endif + +// SK_SPI is functionally identical to SK_API, but used within src to clarify that it's less stable +#if !defined(SK_SPI) + #define SK_SPI SK_API +#endif + +// See https://clang.llvm.org/docs/AttributeReference.html#availability +// The API_AVAILABLE macro comes from on MacOS +#if defined(SK_ENABLE_API_AVAILABLE) +# define SK_API_AVAILABLE API_AVAILABLE +#else +# define SK_API_AVAILABLE(...) +#endif + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkASAN.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkASAN.h new file mode 100644 index 0000000000..095f71608d --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkASAN.h @@ -0,0 +1,56 @@ +/* + * Copyright 2020 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkASAN_DEFINED +#define SkASAN_DEFINED + +#include + +#ifdef __SANITIZE_ADDRESS__ + #define SK_SANITIZE_ADDRESS 1 +#endif +#if !defined(SK_SANITIZE_ADDRESS) && defined(__has_feature) + #if __has_feature(address_sanitizer) + #define SK_SANITIZE_ADDRESS 1 + #endif +#endif + +// Typically declared in LLVM's asan_interface.h. +#ifdef SK_SANITIZE_ADDRESS +extern "C" { + void __asan_poison_memory_region(void const volatile *addr, size_t size); + void __asan_unpoison_memory_region(void const volatile *addr, size_t size); + int __asan_address_is_poisoned(void const volatile *addr); +} +#endif + +// Code that implements bespoke allocation arenas can poison the entire arena on creation, then +// unpoison chunks of arena memory as they are parceled out. Consider leaving gaps between blocks +// to detect buffer overrun. +static inline void sk_asan_poison_memory_region([[maybe_unused]] void const volatile* addr, + [[maybe_unused]] size_t size) { +#ifdef SK_SANITIZE_ADDRESS + __asan_poison_memory_region(addr, size); +#endif +} + +static inline void sk_asan_unpoison_memory_region([[maybe_unused]] void const volatile* addr, + [[maybe_unused]] size_t size) { +#ifdef SK_SANITIZE_ADDRESS + __asan_unpoison_memory_region(addr, size); +#endif +} + +static inline int sk_asan_address_is_poisoned([[maybe_unused]] void const volatile* addr) { +#ifdef SK_SANITIZE_ADDRESS + return __asan_address_is_poisoned(addr); +#else + return 0; +#endif +} + +#endif // SkASAN_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkAlign.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkAlign.h new file mode 100644 index 0000000000..2b2138ddd4 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkAlign.h @@ -0,0 +1,39 @@ +/* + * 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 SkAlign_DEFINED +#define SkAlign_DEFINED + +#include "include/private/base/SkAssert.h" + +#include + +template static constexpr T SkAlign2(T x) { return (x + 1) >> 1 << 1; } +template static constexpr T SkAlign4(T x) { return (x + 3) >> 2 << 2; } +template static constexpr T SkAlign8(T x) { return (x + 7) >> 3 << 3; } + +template static constexpr bool SkIsAlign2(T x) { return 0 == (x & 1); } +template static constexpr bool SkIsAlign4(T x) { return 0 == (x & 3); } +template static constexpr bool SkIsAlign8(T x) { return 0 == (x & 7); } + +template static constexpr T SkAlignPtr(T x) { + return sizeof(void*) == 8 ? SkAlign8(x) : SkAlign4(x); +} +template static constexpr bool SkIsAlignPtr(T x) { + return sizeof(void*) == 8 ? SkIsAlign8(x) : SkIsAlign4(x); +} + +/** + * align up to a power of 2 + */ +static inline constexpr size_t SkAlignTo(size_t x, size_t alignment) { + // The same as alignment && SkIsPow2(value), w/o a dependency cycle. + SkASSERT(alignment && (alignment & (alignment - 1)) == 0); + return (x + alignment - 1) & ~(alignment - 1); +} + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkAlignedStorage.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkAlignedStorage.h new file mode 100644 index 0000000000..532ad03978 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkAlignedStorage.h @@ -0,0 +1,32 @@ +// 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 SkAlignedStorage_DEFINED +#define SkAlignedStorage_DEFINED + +#include +#include + +template class SkAlignedSTStorage { +public: + SkAlignedSTStorage() {} + SkAlignedSTStorage(SkAlignedSTStorage&&) = delete; + SkAlignedSTStorage(const SkAlignedSTStorage&) = delete; + SkAlignedSTStorage& operator=(SkAlignedSTStorage&&) = delete; + SkAlignedSTStorage& operator=(const SkAlignedSTStorage&) = delete; + + // Returns void* because this object does not initialize the + // memory. Use placement new for types that require a constructor. + void* get() { return fStorage; } + const void* get() const { return fStorage; } + + // Act as a container of bytes because the storage is uninitialized. + std::byte* data() { return fStorage; } + const std::byte* data() const { return fStorage; } + size_t size() const { return std::size(fStorage); } + +private: + alignas(T) std::byte fStorage[sizeof(T) * N]; +}; + +#endif // SkAlignedStorage_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkAnySubclass.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkAnySubclass.h new file mode 100644 index 0000000000..2b666cbdb1 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkAnySubclass.h @@ -0,0 +1,73 @@ +/* + * Copyright 2023 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkAnySubclass_DEFINED +#define SkAnySubclass_DEFINED + +#include "include/private/base/SkAssert.h" + +#include +#include +#include // IWYU pragma: keep +#include + +/** + * Stores any subclass `T` of `Base`, where sizeof(T) <= `Size`, without using the heap. + * Doesn't need advance knowledge of T, so it's particularly suited to platform or backend + * implementations of a generic interface, where the set of possible subclasses is finite and + * known, but can't be made available at compile-time. + */ +template +class SkAnySubclass { +public: + SkAnySubclass() = default; + ~SkAnySubclass() { + this->reset(); + } + + SkAnySubclass(const SkAnySubclass&) = delete; + SkAnySubclass& operator=(const SkAnySubclass&) = delete; + SkAnySubclass(SkAnySubclass&&) = delete; + SkAnySubclass& operator=(SkAnySubclass&&) = delete; + + template + void emplace(Args&&... args) { + static_assert(std::is_base_of_v); + static_assert(sizeof(T) <= Size); + // We're going to clean up our stored object by calling ~Base: + static_assert(std::has_virtual_destructor_v || std::is_trivially_destructible_v); + SkASSERT(!fValid); + new (fData) T(std::forward(args)...); + fValid = true; + } + + void reset() { + if (fValid) { + this->get()->~Base(); + } + fValid = false; + } + + const Base* get() const { + SkASSERT(fValid); + return std::launder(reinterpret_cast(fData)); + } + + Base* get() { + SkASSERT(fValid); + return std::launder(reinterpret_cast(fData)); + } + + Base* operator->() { return this->get(); } + const Base* operator->() const { return this->get(); } + +private: + alignas(8) std::byte fData[Size]; + bool fValid = false; +}; + +#endif // SkAnySubclass_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkAssert.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkAssert.h new file mode 100644 index 0000000000..67b31213dd --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkAssert.h @@ -0,0 +1,202 @@ +/* + * 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 SkAssert_DEFINED +#define SkAssert_DEFINED + +#include "include/private/base/SkAPI.h" +#include "include/private/base/SkAttributes.h" +#include "include/private/base/SkDebug.h" // IWYU pragma: keep + +#include +#include + +#if defined(__clang__) && defined(__has_attribute) + #if __has_attribute(likely) + #define SK_LIKELY [[likely]] + #define SK_UNLIKELY [[unlikely]] + #else + #define SK_LIKELY + #define SK_UNLIKELY + #endif +#else + #define SK_LIKELY + #define SK_UNLIKELY +#endif + +// c++23 will give us [[assume]] -- until then we're stuck with various other options: +#if defined(__clang__) + #define SK_ASSUME(cond) __builtin_assume(cond) +#elif defined(__GNUC__) + #if __GNUC__ >= 13 + #define SK_ASSUME(cond) __attribute__((assume(cond))) + #else + // NOTE: This implementation could actually evaluate `cond`, which is not desirable. + #define SK_ASSUME(cond) ((cond) ? (void)0 : __builtin_unreachable()) + #endif +#elif defined(_MSC_VER) + #define SK_ASSUME(cond) __assume(cond) +#else + #define SK_ASSUME(cond) ((void)0) +#endif + +/** Called internally if we hit an unrecoverable error. + The platform implementation must not return, but should either throw + an exception or otherwise exit. +*/ +[[noreturn]] SK_API extern void sk_abort_no_print(void); + +#if defined(SK_BUILD_FOR_GOOGLE3) + void SkDebugfForDumpStackTrace(const char* data, void* unused); + namespace base { + void DumpStackTrace(int skip_count, void w(const char*, void*), void* arg); + } +# define SK_DUMP_GOOGLE3_STACK() ::base::DumpStackTrace(0, SkDebugfForDumpStackTrace, nullptr) +#else +# define SK_DUMP_GOOGLE3_STACK() +#endif + +#if !defined(SK_ABORT) +# if defined(SK_BUILD_FOR_WIN) + // This style lets Visual Studio follow errors back to the source file. +# define SK_DUMP_LINE_FORMAT "%s(%d)" +# else +# define SK_DUMP_LINE_FORMAT "%s:%d" +# endif +# define SK_ABORT(message, ...) \ + do { \ + SkDebugf(SK_DUMP_LINE_FORMAT ": fatal error: \"" message "\"\n", \ + __FILE__, __LINE__, ##__VA_ARGS__); \ + SK_DUMP_GOOGLE3_STACK(); \ + sk_abort_no_print(); \ + } while (false) +#endif + +// SkASSERT, SkASSERTF and SkASSERT_RELEASE can be used as standalone assertion expressions, e.g. +// uint32_t foo(int x) { +// SkASSERT(x > 4); +// return x - 4; +// } +// and are also written to be compatible with constexpr functions: +// constexpr uint32_t foo(int x) { +// return SkASSERT(x > 4), +// x - 4; +// } +#if defined(__clang__) +#define SkASSERT_RELEASE(cond) \ + static_cast( __builtin_expect(static_cast(cond), 1) \ + ? static_cast(0) \ + : []{ SK_ABORT("check(%s)", #cond); }() ) + +#define SkASSERTF_RELEASE(cond, fmt, ...) \ + static_cast( __builtin_expect(static_cast(cond), 1) \ + ? static_cast(0) \ + : [&]{ SK_ABORT("assertf(%s): " fmt, #cond, ##__VA_ARGS__); }() ) +#else +#define SkASSERT_RELEASE(cond) \ + static_cast( (cond) ? static_cast(0) : []{ SK_ABORT("check(%s)", #cond); }() ) + +#define SkASSERTF_RELEASE(cond, fmt, ...) \ + static_cast( (cond) \ + ? static_cast(0) \ + : [&]{ SK_ABORT("assertf(%s): " fmt, #cond, ##__VA_ARGS__); }() ) +#endif + +#if defined(SK_DEBUG) + #define SkASSERT(cond) SkASSERT_RELEASE(cond) + #define SkASSERTF(cond, fmt, ...) SkASSERTF_RELEASE(cond, fmt, ##__VA_ARGS__) + #define SkDEBUGFAIL(message) SK_ABORT("%s", message) + #define SkDEBUGFAILF(fmt, ...) SK_ABORT(fmt, ##__VA_ARGS__) + #define SkAssertResult(cond) SkASSERT(cond) +#else + #define SkASSERT(cond) static_cast(0) + #define SkASSERTF(cond, fmt, ...) static_cast(0) + #define SkDEBUGFAIL(message) + #define SkDEBUGFAILF(fmt, ...) + + // unlike SkASSERT, this macro executes its condition in the non-debug build. + // The if is present so that this can be used with functions marked [[nodiscard]]. + #define SkAssertResult(cond) if (cond) {} do {} while(false) +#endif + +#if !defined(SkUNREACHABLE) +# if defined(_MSC_VER) && !defined(__clang__) +# include +# define FAST_FAIL_INVALID_ARG 5 +// See https://developercommunity.visualstudio.com/content/problem/1128631/code-flow-doesnt-see-noreturn-with-extern-c.html +// for why this is wrapped. Hopefully removable after msvc++ 19.27 is no longer supported. +[[noreturn]] static inline void sk_fast_fail() { __fastfail(FAST_FAIL_INVALID_ARG); } +# define SkUNREACHABLE sk_fast_fail() +# else +# define SkUNREACHABLE __builtin_trap() +# endif +#endif + +[[noreturn]] SK_API inline void sk_print_index_out_of_bounds(size_t i, size_t size) { + SK_ABORT("Index (%zu) out of bounds for size %zu.\n", i, size); +} + +template SK_API inline T sk_collection_check_bounds(T i, T size) { + if (0 <= i && i < size) SK_LIKELY { + return i; + } + + SK_UNLIKELY { + #if defined(SK_DEBUG) + sk_print_index_out_of_bounds(static_cast(i), static_cast(size)); + #else + SkUNREACHABLE; + #endif + } +} + +[[noreturn]] SK_API inline void sk_print_length_too_big(size_t i, size_t size) { + SK_ABORT("Length (%zu) is too big for size %zu.\n", i, size); +} + +template SK_API inline T sk_collection_check_length(T i, T size) { + if (0 <= i && i <= size) SK_LIKELY { + return i; + } + + SK_UNLIKELY { + #if defined(SK_DEBUG) + sk_print_length_too_big(static_cast(i), static_cast(size)); + #else + SkUNREACHABLE; + #endif + } +} + +SK_API inline void sk_collection_not_empty(bool empty) { + if (empty) SK_UNLIKELY { + #if defined(SK_DEBUG) + SK_ABORT("Collection is empty.\n"); + #else + SkUNREACHABLE; + #endif + } +} + +[[noreturn]] SK_API inline void sk_print_size_too_big(size_t size, size_t maxSize) { + SK_ABORT("Size (%zu) can't be represented in bytes. Max size is %zu.\n", size, maxSize); +} + +template +SK_ALWAYS_INLINE size_t check_size_bytes_too_big(size_t size) { + const size_t kMaxSize = std::numeric_limits::max() / sizeof(T); + if (size > kMaxSize) { + #if defined(SK_DEBUG) + sk_print_size_too_big(size, kMaxSize); + #else + SkUNREACHABLE; + #endif + } + return size; +} + +#endif // SkAssert_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkAttributes.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkAttributes.h new file mode 100644 index 0000000000..f8df5905cd --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkAttributes.h @@ -0,0 +1,90 @@ +/* + * 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 SkAttributes_DEFINED +#define SkAttributes_DEFINED + +#include "include/private/base/SkFeatures.h" // IWYU pragma: keep +#include "include/private/base/SkLoadUserConfig.h" // IWYU pragma: keep + +#if defined(__clang__) || defined(__GNUC__) +# define SK_ATTRIBUTE(attr) __attribute__((attr)) +#else +# define SK_ATTRIBUTE(attr) +#endif + +/** + * If your judgment is better than the compiler's (i.e. you've profiled it), + * you can use SK_ALWAYS_INLINE to force inlining. E.g. + * inline void someMethod() { ... } // may not be inlined + * SK_ALWAYS_INLINE void someMethod() { ... } // should always be inlined + */ +#if !defined(SK_ALWAYS_INLINE) +# if defined(SK_BUILD_FOR_WIN) +# define SK_ALWAYS_INLINE __forceinline +# else +# define SK_ALWAYS_INLINE SK_ATTRIBUTE(always_inline) inline +# endif +#endif + +/** + * If your judgment is better than the compiler's (i.e. you've profiled it), + * you can use SK_NEVER_INLINE to prevent inlining. + */ +#if !defined(SK_NEVER_INLINE) +# if defined(SK_BUILD_FOR_WIN) +# define SK_NEVER_INLINE __declspec(noinline) +# else +# define SK_NEVER_INLINE SK_ATTRIBUTE(noinline) +# endif +#endif + +/** + * Used to annotate a function as taking printf style arguments. + * `A` is the (1 based) index of the format string argument. + * `B` is the (1 based) index of the first argument used by the format string. + */ +#if !defined(SK_PRINTF_LIKE) +# define SK_PRINTF_LIKE(A, B) SK_ATTRIBUTE(format(printf, (A), (B))) +#endif + +/** + * 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 __has_attribute(no_sanitize) + #define SK_CLANG_NO_SANITIZE(A) SK_NO_SANITIZE(A) + #endif +#endif + +#if !defined(SK_CLANG_NO_SANITIZE) + #define SK_CLANG_NO_SANITIZE(A) +#endif + +/** + * Annotates a class' non-trivial special functions as trivial for the purposes of calls. + * Allows a class with a non-trivial destructor to be __is_trivially_relocatable. + * Use of this attribute on a public API breaks platform ABI. + * Annotated classes may not hold pointers derived from `this`. + * Annotated classes must implement move+delete as equivalent to memcpy+free. + * Use may require more complete types, as callee destroys. + * + * https://clang.llvm.org/docs/AttributeReference.html#trivial-abi + * https://libcxx.llvm.org/DesignDocs/UniquePtrTrivialAbi.html + */ +#if !defined(SK_TRIVIAL_ABI) +# define SK_TRIVIAL_ABI +#endif + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkCPUTypes.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkCPUTypes.h new file mode 100644 index 0000000000..a5f60fd3ef --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkCPUTypes.h @@ -0,0 +1,25 @@ +/* + * Copyright 2023 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SkCPUTypes_DEFINED +#define SkCPUTypes_DEFINED + +// TODO(bungeman,kjlubick) There are a lot of assumptions throughout the codebase that +// these types are 32 bits, when they could be more or less. Public APIs should stop +// using these. Internally, we could use uint_fast8_t and uint_fast16_t, but not in +// public APIs due to ABI incompatibilities. + +/** Fast type for unsigned 8 bits. Use for parameter passing and local + variables, not for storage +*/ +typedef unsigned U8CPU; + +/** Fast type for unsigned 16 bits. Use for parameter passing and local + variables, not for storage +*/ +typedef unsigned U16CPU; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkContainers.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkContainers.h new file mode 100644 index 0000000000..587e295efa --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkContainers.h @@ -0,0 +1,54 @@ +// 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 SkContainers_DEFINED +#define SkContainers_DEFINED + +#include "include/private/base/SkAPI.h" +#include "include/private/base/SkAlign.h" +#include "include/private/base/SkSpan_impl.h" + +#include +#include + +class SK_SPI SkContainerAllocator { +public: + SkContainerAllocator(size_t sizeOfT, int maxCapacity) + : fSizeOfT{sizeOfT} + , fMaxCapacity{maxCapacity} {} + + // allocate will abort on failure. Given a capacity of 0, it will return the empty span. + // The bytes allocated are freed using sk_free(). + SkSpan allocate(int capacity, double growthFactor = 1.0); + + // Rounds a requested capacity up towards `kCapacityMultiple` in a constexpr-friendly fashion. + template + static constexpr size_t RoundUp(size_t capacity) { + return SkAlignTo(capacity * sizeof(T), kCapacityMultiple) / sizeof(T); + } + +private: + friend struct SkContainerAllocatorTestingPeer; + + // All capacity counts will be rounded up to kCapacityMultiple. This matches ASAN's shadow + // granularity, as well as our typical struct alignment on a 64-bit machine. + static constexpr int64_t kCapacityMultiple = 8; + + // Rounds up capacity to next multiple of kCapacityMultiple and pin to fMaxCapacity. + size_t roundUpCapacity(int64_t capacity) const; + + // Grows the capacity by growthFactor being sure to stay with in kMinBytes and fMaxCapacity. + size_t growthFactorCapacity(int capacity, double growthFactor) const; + + const size_t fSizeOfT; + const int64_t fMaxCapacity; +}; + +// sk_allocate_canfail returns the empty span on failure. Parameter size must be > 0. +SkSpan sk_allocate_canfail(size_t size); + +// Returns the empty span if size is 0. sk_allocate_throw aborts on failure. +SkSpan sk_allocate_throw(size_t size); + +SK_SPI void sk_report_container_overflow_and_die(); +#endif // SkContainers_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkDebug.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkDebug.h new file mode 100644 index 0000000000..2e4810fc1c --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkDebug.h @@ -0,0 +1,27 @@ +/* + * 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 SkDebug_DEFINED +#define SkDebug_DEFINED + +#include "include/private/base/SkAPI.h" +#include "include/private/base/SkAttributes.h" +#include "include/private/base/SkLoadUserConfig.h" // IWYU pragma: keep + +#if !defined(SkDebugf) + void SK_SPI SkDebugf(const char format[], ...) SK_PRINTF_LIKE(1, 2); +#endif + +#if defined(SK_DEBUG) + #define SkDEBUGCODE(...) __VA_ARGS__ + #define SkDEBUGF(...) SkDebugf(__VA_ARGS__) +#else + #define SkDEBUGCODE(...) + #define SkDEBUGF(...) +#endif + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkDeque.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkDeque.h new file mode 100644 index 0000000000..c7a43c9fe4 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkDeque.h @@ -0,0 +1,138 @@ + +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#ifndef SkDeque_DEFINED +#define SkDeque_DEFINED + +#include "include/private/base/SkAPI.h" + +#include + +/* + * The deque class works by blindly creating memory space of a specified element + * size. It manages the memory as a doubly linked list of blocks each of which + * can contain multiple elements. Pushes and pops add/remove blocks from the + * beginning/end of the list as necessary while each block tracks the used + * portion of its memory. + * One behavior to be aware of is that the pops do not immediately remove an + * empty block from the beginning/end of the list (Presumably so push/pop pairs + * on the block boundaries don't cause thrashing). This can result in the first/ + * last element not residing in the first/last block. + */ +class SK_API SkDeque { +public: + /** + * elemSize specifies the size of each individual element in the deque + * allocCount specifies how many elements are to be allocated as a block + */ + explicit SkDeque(size_t elemSize, int allocCount = 1); + SkDeque(size_t elemSize, void* storage, size_t storageSize, int allocCount = 1); + ~SkDeque(); + + bool empty() const { return 0 == fCount; } + int count() const { return fCount; } + size_t elemSize() const { return fElemSize; } + + const void* front() const { return fFront; } + const void* back() const { return fBack; } + + void* front() { return fFront; } + void* back() { return fBack; } + + /** + * push_front and push_back return a pointer to the memory space + * for the new element + */ + void* push_front(); + void* push_back(); + + void pop_front(); + void pop_back(); + +private: + struct Block; + +public: + class Iter { + public: + enum IterStart { + kFront_IterStart, + kBack_IterStart, + }; + + /** + * Creates an uninitialized iterator. Must be reset() + */ + Iter(); + + Iter(const SkDeque& d, IterStart startLoc); + void* next(); + void* prev(); + + void reset(const SkDeque& d, IterStart startLoc); + + private: + SkDeque::Block* fCurBlock; + char* fPos; + size_t fElemSize; + }; + + // Inherit privately from Iter to prevent access to reverse iteration + class F2BIter : private Iter { + public: + F2BIter() {} + + /** + * Wrap Iter's 2 parameter ctor to force initialization to the + * beginning of the deque + */ + F2BIter(const SkDeque& d) : INHERITED(d, kFront_IterStart) {} + + using Iter::next; + + /** + * Wrap Iter::reset to force initialization to the beginning of the + * deque + */ + void reset(const SkDeque& d) { + this->INHERITED::reset(d, kFront_IterStart); + } + + private: + using INHERITED = Iter; + }; + +private: + // allow unit test to call numBlocksAllocated + friend class DequeUnitTestHelper; + + void* fFront; + void* fBack; + + Block* fFrontBlock; + Block* fBackBlock; + size_t fElemSize; + void* fInitialStorage; + int fCount; // number of elements in the deque + int fAllocCount; // number of elements to allocate per block + + Block* allocateBlock(int allocCount); + void freeBlock(Block* block); + + /** + * This returns the number of chunk blocks allocated by the deque. It + * can be used to gauge the effectiveness of the selected allocCount. + */ + int numBlocksAllocated() const; + + SkDeque(const SkDeque&) = delete; + SkDeque& operator=(const SkDeque&) = delete; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkFeatures.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkFeatures.h new file mode 100644 index 0000000000..353ce22897 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkFeatures.h @@ -0,0 +1,165 @@ +/* + * 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 SkFeatures_DEFINED +#define SkFeatures_DEFINED + +#if !defined(SK_BUILD_FOR_ANDROID) && !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_WIN) && \ + !defined(SK_BUILD_FOR_UNIX) && !defined(SK_BUILD_FOR_MAC) + + #ifdef __APPLE__ + #include + #endif + + #if defined(_WIN32) || defined(__SYMBIAN32__) + #define SK_BUILD_FOR_WIN + #elif defined(ANDROID) || defined(__ANDROID__) + #define SK_BUILD_FOR_ANDROID + #elif defined(linux) || defined(__linux) || defined(__FreeBSD__) || \ + defined(__OpenBSD__) || defined(__sun) || defined(__NetBSD__) || \ + defined(__DragonFly__) || defined(__Fuchsia__) || \ + defined(__GLIBC__) || defined(__GNU__) || defined(__unix__) + #define SK_BUILD_FOR_UNIX + #elif TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR + #define SK_BUILD_FOR_IOS + #else + #define SK_BUILD_FOR_MAC + #endif +#endif // end SK_BUILD_FOR_* + + +#if defined(SK_BUILD_FOR_WIN) && !defined(__clang__) + #if !defined(SK_RESTRICT) + #define SK_RESTRICT __restrict + #endif +#endif + +#if !defined(SK_RESTRICT) + #define SK_RESTRICT __restrict__ +#endif + +#if !defined(SK_CPU_BENDIAN) && !defined(SK_CPU_LENDIAN) + #if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) + #define SK_CPU_BENDIAN + #elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + #define SK_CPU_LENDIAN + #elif defined(__sparc) || defined(__sparc__) || \ + defined(_POWER) || defined(__powerpc__) || \ + defined(__ppc__) || defined(__hppa) || \ + defined(__PPC__) || defined(__PPC64__) || \ + defined(_MIPSEB) || defined(__ARMEB__) || \ + defined(__s390__) || \ + (defined(__sh__) && defined(__BIG_ENDIAN__)) || \ + (defined(__ia64) && defined(__BIG_ENDIAN__)) + #define SK_CPU_BENDIAN + #else + #define SK_CPU_LENDIAN + #endif +#endif + +#if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) + #define SK_CPU_X86 1 +#endif + +#if defined(__loongarch__) || defined (__loongarch64) + #define SK_CPU_LOONGARCH 1 +#endif + +/** + * SK_CPU_SSE_LEVEL + * + * If defined, SK_CPU_SSE_LEVEL should be set to the highest supported level. + * On non-intel CPU this should be undefined. + */ +#define SK_CPU_SSE_LEVEL_SSE1 10 +#define SK_CPU_SSE_LEVEL_SSE2 20 +#define SK_CPU_SSE_LEVEL_SSE3 30 +#define SK_CPU_SSE_LEVEL_SSSE3 31 +#define SK_CPU_SSE_LEVEL_SSE41 41 +#define SK_CPU_SSE_LEVEL_SSE42 42 +#define SK_CPU_SSE_LEVEL_AVX 51 +#define SK_CPU_SSE_LEVEL_AVX2 52 +#define SK_CPU_SSE_LEVEL_SKX 60 + +/** + * SK_CPU_LSX_LEVEL + * + * If defined, SK_CPU_LSX_LEVEL should be set to the highest supported level. + * On non-loongarch CPU this should be undefined. + */ +#define SK_CPU_LSX_LEVEL_LSX 70 +#define SK_CPU_LSX_LEVEL_LASX 80 + +// TODO(brianosman,kjlubick) clean up these checks + +// Are we in GCC/Clang? +#ifndef SK_CPU_SSE_LEVEL + // These checks must be done in descending order to ensure we set the highest + // available SSE level. + #if defined(__AVX512F__) && defined(__AVX512DQ__) && defined(__AVX512CD__) && \ + defined(__AVX512BW__) && defined(__AVX512VL__) + #define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SKX + #elif defined(__AVX2__) + #define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_AVX2 + #elif defined(__AVX__) + #define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_AVX + #elif defined(__SSE4_2__) + #define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSE42 + #elif defined(__SSE4_1__) + #define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSE41 + #elif defined(__SSSE3__) + #define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSSE3 + #elif defined(__SSE3__) + #define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSE3 + #elif defined(__SSE2__) + #define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSE2 + #endif +#endif + +#ifndef SK_CPU_LSX_LEVEL + #if defined(__loongarch_asx) + #define SK_CPU_LSX_LEVEL SK_CPU_LSX_LEVEL_LASX + #elif defined(__loongarch_sx) + #define SK_CPU_LSX_LEVEL SK_CPU_LSX_LEVEL_LSX + #endif +#endif + +// Are we in VisualStudio? +#ifndef SK_CPU_SSE_LEVEL + // These checks must be done in descending order to ensure we set the highest + // available SSE level. 64-bit intel guarantees at least SSE2 support. + #if defined(__AVX512F__) && defined(__AVX512DQ__) && defined(__AVX512CD__) && \ + defined(__AVX512BW__) && defined(__AVX512VL__) + #define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SKX + #elif defined(__AVX2__) + #define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_AVX2 + #elif defined(__AVX__) + #define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_AVX + #elif defined(_M_X64) || defined(_M_AMD64) + #define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSE2 + #elif defined(_M_IX86_FP) + #if _M_IX86_FP >= 2 + #define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSE2 + #elif _M_IX86_FP == 1 + #define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSE1 + #endif + #endif +#endif + +// ARM defines +#if defined(__arm__) && (!defined(__APPLE__) || !TARGET_IPHONE_SIMULATOR) + #define SK_CPU_ARM32 +#elif defined(__aarch64__) + #define SK_CPU_ARM64 +#endif + +// All 64-bit ARM chips have NEON. Many 32-bit ARM chips do too. +#if !defined(SK_ARM_HAS_NEON) && defined(__ARM_NEON) + #define SK_ARM_HAS_NEON +#endif + +#endif // SkFeatures_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkFixed.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkFixed.h new file mode 100644 index 0000000000..2c8f2fb56c --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkFixed.h @@ -0,0 +1,143 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkFixed_DEFINED +#define SkFixed_DEFINED + +#include "include/private/base/SkAssert.h" +#include "include/private/base/SkDebug.h" +#include "include/private/base/SkMath.h" // IWYU pragma: keep +#include "include/private/base/SkTPin.h" // IWYU pragma: keep + +#include + +/** \file SkFixed.h + + Types and macros for 16.16 fixed point +*/ + +/** 32 bit signed integer used to represent fractions values with 16 bits to the right of the decimal point +*/ +typedef int32_t SkFixed; +#define SK_Fixed1 (1 << 16) +#define SK_FixedHalf (1 << 15) +#define SK_FixedQuarter (1 << 14) +#define SK_FixedMax (0x7FFFFFFF) +#define SK_FixedMin (-SK_FixedMax) +#define SK_FixedPI (0x3243F) +#define SK_FixedSqrt2 (92682) +#define SK_FixedTanPIOver8 (0x6A0A) +#define SK_FixedRoot2Over2 (0xB505) + +// NOTE: SkFixedToFloat is exact. SkFloatToFixed seems to lack a rounding step. For all fixed-point +// values, this version is as accurate as possible for (fixed -> float -> fixed). Rounding reduces +// accuracy if the intermediate floats are in the range that only holds integers (adding 0.5f to an +// odd integer then snaps to nearest even). Using double for the rounding math gives maximum +// accuracy for (float -> fixed -> float), but that's usually overkill. +#define SkFixedToFloat(x) ((x) * 1.52587890625e-5f) +#define SkFloatToFixed(x) sk_float_saturate2int((x) * SK_Fixed1) + +#ifdef SK_DEBUG + static inline SkFixed SkFloatToFixed_Check(float x) { + int64_t n64 = (int64_t)(x * SK_Fixed1); + SkFixed n32 = (SkFixed)n64; + SkASSERT(n64 == n32); + return n32; + } +#else + #define SkFloatToFixed_Check(x) SkFloatToFixed(x) +#endif + +#define SkFixedToDouble(x) ((x) * 1.52587890625e-5) +#define SkDoubleToFixed(x) ((SkFixed)((x) * SK_Fixed1)) + +/** Converts an integer to a SkFixed, asserting that the result does not overflow + a 32 bit signed integer +*/ +#ifdef SK_DEBUG + inline SkFixed SkIntToFixed(int n) + { + SkASSERT(n >= -32768 && n <= 32767); + // Left shifting a negative value has undefined behavior in C, so we cast to unsigned before + // shifting. + return (SkFixed)( (unsigned)n << 16 ); + } +#else + // Left shifting a negative value has undefined behavior in C, so we cast to unsigned before + // shifting. Then we force the cast to SkFixed to ensure that the answer is signed (like the + // debug version). + #define SkIntToFixed(n) (SkFixed)((unsigned)(n) << 16) +#endif + +#define SkFixedRoundToInt(x) (((x) + SK_FixedHalf) >> 16) +#define SkFixedCeilToInt(x) (((x) + SK_Fixed1 - 1) >> 16) +#define SkFixedFloorToInt(x) ((x) >> 16) + +static inline SkFixed SkFixedRoundToFixed(SkFixed x) { + return (SkFixed)( (uint32_t)(x + SK_FixedHalf) & 0xFFFF0000 ); +} +static inline SkFixed SkFixedCeilToFixed(SkFixed x) { + return (SkFixed)( (uint32_t)(x + SK_Fixed1 - 1) & 0xFFFF0000 ); +} +static inline SkFixed SkFixedFloorToFixed(SkFixed x) { + return (SkFixed)( (uint32_t)x & 0xFFFF0000 ); +} + +#define SkFixedAve(a, b) (((a) + (b)) >> 1) + +// The divide may exceed 32 bits. Clamp to a signed 32 bit result. +#define SkFixedDiv(numer, denom) \ + SkToS32(SkTPin((SkLeftShift((int64_t)(numer), 16) / (denom)), SK_MinS32, SK_MaxS32)) + +static inline SkFixed SkFixedMul(SkFixed a, SkFixed b) { + return (SkFixed)((int64_t)a * b >> 16); +} + +/////////////////////////////////////////////////////////////////////////////// +// Platform-specific alternatives to our portable versions. + +// The VCVT float-to-fixed instruction is part of the VFPv3 instruction set. +#if defined(__ARM_VFPV3__) + #include + + /* This does not handle NaN or other obscurities, but is faster than + than (int)(x*65536). When built on Android with -Os, needs forcing + to inline or we lose the speed benefit. + */ + SK_ALWAYS_INLINE SkFixed SkFloatToFixed_arm(float x) + { + int32_t y; + asm("vcvt.s32.f32 %0, %0, #16": "+w"(x)); + std::memcpy(&y, &x, sizeof(y)); + return y; + } + #undef SkFloatToFixed + #define SkFloatToFixed(x) SkFloatToFixed_arm(x) +#endif + +/////////////////////////////////////////////////////////////////////////////// + +#define SkFixedToScalar(x) SkFixedToFloat(x) +#define SkScalarToFixed(x) SkFloatToFixed(x) + +/////////////////////////////////////////////////////////////////////////////// + +typedef int64_t SkFixed3232; // 32.32 + +#define SkFixed3232Max SK_MaxS64 +#define SkFixed3232Min (-SkFixed3232Max) + +#define SkIntToFixed3232(x) (SkLeftShift((SkFixed3232)(x), 32)) +#define SkFixed3232ToInt(x) ((int)((x) >> 32)) +#define SkFixedToFixed3232(x) (SkLeftShift((SkFixed3232)(x), 16)) +#define SkFixed3232ToFixed(x) ((SkFixed)((x) >> 16)) +#define SkFloatToFixed3232(x) sk_float_saturate2int64((x) * (65536.0f * 65536.0f)) +#define SkFixed3232ToFloat(x) (x * (1 / (65536.0f * 65536.0f))) + +#define SkScalarToFixed3232(x) SkFloatToFixed3232(x) + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkFloatingPoint.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkFloatingPoint.h new file mode 100644 index 0000000000..5a1e4e30b7 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkFloatingPoint.h @@ -0,0 +1,184 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkFloatingPoint_DEFINED +#define SkFloatingPoint_DEFINED + +#include "include/private/base/SkAttributes.h" +#include "include/private/base/SkMath.h" + +#include +#include +#include +#include + +inline constexpr float SK_FloatSqrt2 = 1.41421356f; +inline constexpr float SK_FloatPI = 3.14159265f; +inline constexpr double SK_DoublePI = 3.14159265358979323846264338327950288; + +static constexpr int sk_float_sgn(float x) { + return (0.0f < x) - (x < 0.0f); +} + +static constexpr float sk_float_degrees_to_radians(float degrees) { + return degrees * (SK_FloatPI / 180); +} + +static constexpr float sk_float_radians_to_degrees(float radians) { + return radians * (180 / SK_FloatPI); +} + +// floor(double+0.5) vs. floorf(float+0.5f) give comparable performance, but upcasting to double +// means tricky values like 0.49999997 and 2^24 get rounded correctly. If these were rounded +// as floatf(x + .5f), they would be 1 higher than expected. +#define sk_float_round(x) (float)sk_double_round((double)(x)) + +template , bool> = true> +static inline constexpr bool SkIsNaN(T x) { + return x != x; +} + +// Subtracting a value from itself will result in zero, except for NAN or ±Inf, which make NAN. +// Multiplying a group of values against zero will result in zero for each product, except for +// NAN or ±Inf, which will result in NAN and continue resulting in NAN for the rest of the elements. +// This generates better code than `std::isfinite` when building with clang-cl (April 2024). +template , bool> = true> +static inline bool SkIsFinite(T x, Pack... values) { + T prod = x - x; + prod = (prod * ... * values); + // At this point, `prod` will either be NaN or 0. + return prod == prod; +} + +template , bool> = true> +static inline bool SkIsFinite(const T array[], int count) { + T x = array[0]; + T prod = x - x; + for (int i = 1; i < count; ++i) { + prod *= array[i]; + } + // At this point, `prod` will either be NaN or 0. + return prod == prod; +} + +inline constexpr int SK_MaxS32FitsInFloat = 2147483520; +inline constexpr int SK_MinS32FitsInFloat = -SK_MaxS32FitsInFloat; + +// 0x7fffff8000000000 +inline constexpr int64_t SK_MaxS64FitsInFloat = SK_MaxS64 >> (63-24) << (63-24); +inline constexpr int64_t SK_MinS64FitsInFloat = -SK_MaxS64FitsInFloat; + +// sk_[float|double]_saturate2int are written to return their maximum values when passed NaN. +// MSVC 19.38+ has a bug with this implementation, leading to incorrect results: +// https://developercommunity.visualstudio.com/t/Optimizer-incorrectly-handles-NaN-floati/10654403 +// +// We inject an explicit NaN test on MSVC to work around the problem. +#if defined(_MSC_VER) && !defined(__clang__) + #define SK_CHECK_NAN(resultVal) if (SkIsNaN(x)) { return resultVal; } +#else + #define SK_CHECK_NAN(resultVal) +#endif + +/** + * Return the closest int for the given float. Returns SK_MaxS32FitsInFloat for NaN. + */ +static constexpr int sk_float_saturate2int(float x) { + SK_CHECK_NAN(SK_MaxS32FitsInFloat) + x = x < SK_MaxS32FitsInFloat ? x : SK_MaxS32FitsInFloat; + x = x > SK_MinS32FitsInFloat ? x : SK_MinS32FitsInFloat; + return (int)x; +} + +/** + * Return the closest int for the given double. Returns SK_MaxS32 for NaN. + */ +static constexpr int sk_double_saturate2int(double x) { + SK_CHECK_NAN(SK_MaxS32) + x = x < SK_MaxS32 ? x : SK_MaxS32; + x = x > SK_MinS32 ? x : SK_MinS32; + return (int)x; +} + +/** + * Return the closest int64_t for the given float. Returns SK_MaxS64FitsInFloat for NaN. + */ +static constexpr int64_t sk_float_saturate2int64(float x) { + SK_CHECK_NAN(SK_MaxS64FitsInFloat) + x = x < SK_MaxS64FitsInFloat ? x : SK_MaxS64FitsInFloat; + x = x > SK_MinS64FitsInFloat ? x : SK_MinS64FitsInFloat; + return (int64_t)x; +} + +#undef SK_CHECK_NAN + +#define sk_float_floor2int(x) sk_float_saturate2int(std::floor(x)) +#define sk_float_round2int(x) sk_float_saturate2int(sk_float_round(x)) +#define sk_float_ceil2int(x) sk_float_saturate2int(std::ceil(x)) + +#define sk_float_floor2int_no_saturate(x) ((int)std::floor(x)) +#define sk_float_round2int_no_saturate(x) ((int)sk_float_round(x)) +#define sk_float_ceil2int_no_saturate(x) ((int)std::ceil(x)) + +#define sk_double_round(x) (std::floor((x) + 0.5)) +#define sk_double_floor2int(x) ((int)std::floor(x)) +#define sk_double_round2int(x) ((int)std::round(x)) +#define sk_double_ceil2int(x) ((int)std::ceil(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! +SK_NO_SANITIZE("float-cast-overflow") +static constexpr float sk_double_to_float(double x) { + return static_cast(x); +} + +inline constexpr float SK_FloatNaN = std::numeric_limits::quiet_NaN(); +inline constexpr float SK_FloatInfinity = std::numeric_limits::infinity(); +inline constexpr float SK_FloatNegativeInfinity = -SK_FloatInfinity; + +inline constexpr double SK_DoubleNaN = std::numeric_limits::quiet_NaN(); + +// Calculate the midpoint between a and b. Similar to std::midpoint in c++20. +static constexpr float sk_float_midpoint(float a, float b) { + // Use double math to avoid underflow and overflow. + return static_cast(0.5 * (static_cast(a) + b)); +} + +static inline float sk_float_rsqrt_portable(float x) { return 1.0f / std::sqrt(x); } +static inline float sk_float_rsqrt (float x) { return 1.0f / std::sqrt(x); } + +// IEEE defines how float divide behaves for non-finite values and zero-denoms, but C does not, +// so we have a helper that suppresses the possible undefined-behavior warnings. +#ifdef SK_BUILD_FOR_WIN +#pragma warning(push) +#pragma warning(disable : 4723) +#endif +SK_NO_SANITIZE("float-divide-by-zero") +static constexpr float sk_ieee_float_divide(float numer, float denom) { + return numer / denom; +} + +SK_NO_SANITIZE("float-divide-by-zero") +static constexpr double sk_ieee_double_divide(double numer, double denom) { + return numer / denom; +} +#ifdef SK_BUILD_FOR_WIN +#pragma warning( pop ) +#endif + +// Returns true iff the provided number is within a small epsilon of 0. +bool sk_double_nearly_zero(double a); + +// Compare two doubles and return true if they are within maxUlpsDiff of each other. +// * nan as a or b - returns false. +// * infinity, infinity or -infinity, -infinity - returns true. +// * infinity and any other number - returns false. +// +// ulp is an initialism for Units in the Last Place. +bool sk_doubles_nearly_equal_ulps(double a, double b, uint8_t maxUlpsDiff = 16); + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkLoadUserConfig.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkLoadUserConfig.h new file mode 100644 index 0000000000..9f949782c0 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkLoadUserConfig.h @@ -0,0 +1,63 @@ +/* + * 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 SK_USER_CONFIG_WAS_LOADED + +// Include this to set reasonable defaults (e.g. for SK_CPU_LENDIAN) +#include "include/private/base/SkFeatures.h" + +// Allows embedders that want to disable macros that take arguments to just +// define that symbol to be one of these +#define SK_NOTHING_ARG1(arg1) +#define SK_NOTHING_ARG2(arg1, arg2) +#define SK_NOTHING_ARG3(arg1, arg2, arg3) + +// IWYU pragma: begin_exports + +// Note: SK_USER_CONFIG_HEADER will not work with Bazel builds and some C++ compilers. +#if defined(SK_USER_CONFIG_HEADER) + #include SK_USER_CONFIG_HEADER +#elif defined(SK_USE_BAZEL_CONFIG_HEADER) + // The Bazel config file is presumed to be in the root directory of its Bazel Workspace. + // This is achieved in Skia by having a nested WORKSPACE in include/config and a cc_library + // defined in that folder. As a result, we do not try to include SkUserConfig.h from the + // top of Skia because Bazel sandboxing will move it to a different location. + #include "SkUserConfig.h" // NO_G3_REWRITE +#else + #include "include/config/SkUserConfig.h" +#endif +// IWYU pragma: end_exports + +// Checks to make sure the SkUserConfig options do not conflict. +#if !defined(SK_DEBUG) && !defined(SK_RELEASE) + #ifdef NDEBUG + #define SK_RELEASE + #else + #define SK_DEBUG + #endif +#endif + +#if defined(SK_DEBUG) && defined(SK_RELEASE) +# error "cannot define both SK_DEBUG and SK_RELEASE" +#elif !defined(SK_DEBUG) && !defined(SK_RELEASE) +# error "must define either SK_DEBUG or SK_RELEASE" +#endif + +#if defined(SK_CPU_LENDIAN) && defined(SK_CPU_BENDIAN) +# error "cannot define both SK_CPU_LENDIAN and SK_CPU_BENDIAN" +#elif !defined(SK_CPU_LENDIAN) && !defined(SK_CPU_BENDIAN) +# error "must define either SK_CPU_LENDIAN or SK_CPU_BENDIAN" +#endif + +#if defined(SK_CPU_BENDIAN) && !defined(I_ACKNOWLEDGE_SKIA_DOES_NOT_SUPPORT_BIG_ENDIAN) + #error "The Skia team is not endian-savvy enough to support big-endian CPUs." + #error "If you still want to use Skia," + #error "please define I_ACKNOWLEDGE_SKIA_DOES_NOT_SUPPORT_BIG_ENDIAN." +#endif + +#define SK_USER_CONFIG_WAS_LOADED +#endif // SK_USER_CONFIG_WAS_LOADED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkMacros.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkMacros.h new file mode 100644 index 0000000000..5d1835d013 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkMacros.h @@ -0,0 +1,94 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SkMacros_DEFINED +#define SkMacros_DEFINED + +/* + * Usage: SK_MACRO_CONCAT(a, b) to construct the symbol ab + * + * SK_MACRO_CONCAT_IMPL_PRIV just exists to make this work. Do not use directly + * + */ +#define SK_MACRO_CONCAT(X, Y) SK_MACRO_CONCAT_IMPL_PRIV(X, Y) +#define SK_MACRO_CONCAT_IMPL_PRIV(X, Y) X ## Y + +/* + * Usage: SK_MACRO_APPEND_LINE(foo) to make foo123, where 123 is the current + * line number. Easy way to construct + * unique names for local functions or + * variables. + */ +#define SK_MACRO_APPEND_LINE(name) SK_MACRO_CONCAT(name, __LINE__) + +#define SK_MACRO_APPEND_COUNTER(name) SK_MACRO_CONCAT(name, __COUNTER__) + +//////////////////////////////////////////////////////////////////////////////// + +// Can be used to bracket data types that must be dense/packed, e.g. hash keys. +#if defined(__clang__) // This should work on GCC too, but GCC diagnostic pop didn't seem to work! + #define SK_BEGIN_REQUIRE_DENSE _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic error \"-Wpadded\"") + #define SK_END_REQUIRE_DENSE _Pragma("GCC diagnostic pop") +#else + #define SK_BEGIN_REQUIRE_DENSE + #define SK_END_REQUIRE_DENSE +#endif + +#if defined(__clang__) && defined(__has_feature) + // Some compilers have a preprocessor that does not appear to do short-circuit + // evaluation as expected + #if __has_feature(leak_sanitizer) || __has_feature(address_sanitizer) + // Chrome had issues if we tried to include lsan_interface.h ourselves. + // https://github.com/llvm/llvm-project/blob/10a35632d55bb05004fe3d0c2d4432bb74897ee7/compiler-rt/include/sanitizer/lsan_interface.h#L26 +extern "C" { + void __lsan_ignore_object(const void *p); +} + #define SK_INTENTIONALLY_LEAKED(X) __lsan_ignore_object(X) + #else + #define SK_INTENTIONALLY_LEAKED(X) ((void)0) + #endif +#else + #define SK_INTENTIONALLY_LEAKED(X) ((void)0) +#endif + +#define SK_INIT_TO_AVOID_WARNING = 0 + +//////////////////////////////////////////////////////////////////////////////// + +/** + * Defines overloaded bitwise operators to make it easier to use an enum as a + * bitfield. + */ +#define SK_MAKE_BITFIELD_OPS(X) \ + inline X operator ~(X a) { \ + using U = std::underlying_type_t; \ + return (X) (~static_cast(a)); \ + } \ + inline X operator |(X a, X b) { \ + using U = std::underlying_type_t; \ + return (X) (static_cast(a) | static_cast(b)); \ + } \ + inline X& operator |=(X& a, X b) { \ + return (a = a | b); \ + } \ + inline X operator &(X a, X b) { \ + using U = std::underlying_type_t; \ + return (X) (static_cast(a) & static_cast(b)); \ + } \ + inline X& operator &=(X& a, X b) { \ + return (a = a & b); \ + } + +#define SK_DECL_BITFIELD_OPS_FRIENDS(X) \ + friend X operator ~(X a); \ + friend X operator |(X a, X b); \ + friend X& operator |=(X& a, X b); \ + \ + friend X operator &(X a, X b); \ + friend X& operator &=(X& a, X b); + +#endif // SkMacros_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkMalloc.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkMalloc.h new file mode 100644 index 0000000000..60a77ee7a9 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkMalloc.h @@ -0,0 +1,152 @@ +/* + * 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 SkMalloc_DEFINED +#define SkMalloc_DEFINED + +#include + +#include "include/private/base/SkAPI.h" + +/* + memory wrappers to be implemented by the porting layer (platform) +*/ + + +/** Free memory returned by sk_malloc(). It is safe to pass null. */ +SK_API extern void sk_free(void*); + +/** + * Called internally if we run out of memory. The platform implementation must + * not return, but should either throw an exception or otherwise exit. + */ +SK_API extern void sk_out_of_memory(void); + +enum { + /** + * If this bit is set, the returned buffer must be zero-initialized. If this bit is not set + * the buffer can be uninitialized. + */ + SK_MALLOC_ZERO_INITIALIZE = 1 << 0, + + /** + * If this bit is set, the implementation must throw/crash/quit if the request cannot + * be fulfilled. If this bit is not set, then it should return nullptr on failure. + */ + SK_MALLOC_THROW = 1 << 1, +}; +/** + * Return a block of memory (at least 4-byte aligned) of at least the specified size. + * If the requested memory cannot be returned, either return nullptr or throw/exit, depending + * on the SK_MALLOC_THROW bit. If the allocation succeeds, the memory will be zero-initialized + * if the SK_MALLOC_ZERO_INITIALIZE bit was set. + * + * To free the memory, call sk_free() + */ +SK_API extern void* sk_malloc_flags(size_t size, unsigned flags); + +/** Same as standard realloc(), but this one never returns null on failure. It will throw + * if it fails. + * If size is 0, it will call sk_free on buffer and return null. (This behavior is implementation- + * defined for normal realloc. We follow what glibc does.) + */ +SK_API extern void* sk_realloc_throw(void* buffer, size_t size); + +/** + * Return the size of the block of memory allocated in reality for a given pointer. The pointer + * passed must have been allocated using the sk_malloc_* or sk_realloc_* functions. The "size" + * parameter indicates the size originally requested when the memory block was allocated, and + * the value returned by this function must be bigger or equal to it. + */ +SK_API extern size_t sk_malloc_size(void* addr, size_t size); + +static inline void* sk_malloc_throw(size_t size) { + return sk_malloc_flags(size, SK_MALLOC_THROW); +} + +static inline void* sk_calloc_throw(size_t size) { + return sk_malloc_flags(size, SK_MALLOC_THROW | SK_MALLOC_ZERO_INITIALIZE); +} + +static inline void* sk_calloc_canfail(size_t size) { +#if defined(SK_BUILD_FOR_FUZZER) + // To reduce the chance of OOM, pretend we can't allocate more than 200kb. + if (size > 200000) { + return nullptr; + } +#endif + return sk_malloc_flags(size, SK_MALLOC_ZERO_INITIALIZE); +} + +// Performs a safe multiply count * elemSize, checking for overflow +SK_API extern void* sk_calloc_throw(size_t count, size_t elemSize); +SK_API extern void* sk_malloc_throw(size_t count, size_t elemSize); +SK_API extern void* sk_realloc_throw(void* buffer, size_t count, size_t elemSize); + +/** + * These variants return nullptr on failure + */ +static inline void* sk_malloc_canfail(size_t size) { +#if defined(SK_BUILD_FOR_FUZZER) + // To reduce the chance of OOM, pretend we can't allocate more than 200kb. + if (size > 200000) { + return nullptr; + } +#endif + return sk_malloc_flags(size, 0); +} +SK_API extern void* sk_malloc_canfail(size_t count, size_t elemSize); + +// bzero is safer than memset, but we can't rely on it, so... sk_bzero() +static inline void sk_bzero(void* buffer, size_t size) { + // Please c.f. sk_careful_memcpy. It's undefined behavior to call memset(null, 0, 0). + if (size) { + memset(buffer, 0, size); + } +} + +/** + * sk_careful_memcpy() is just like memcpy(), but guards against undefined behavior. + * + * It is undefined behavior to call memcpy() with null dst or src, even if len is 0. + * If an optimizer is "smart" enough, it can exploit this to do unexpected things. + * memcpy(dst, src, 0); + * if (src) { + * printf("%x\n", *src); + * } + * In this code the compiler can assume src is not null and omit the if (src) {...} check, + * unconditionally running the printf, crashing the program if src really is null. + * Of the compilers we pay attention to only GCC performs this optimization in practice. + */ +static inline void* sk_careful_memcpy(void* dst, const void* src, size_t len) { + // When we pass >0 len we had better already be passing valid pointers. + // So we just need to skip calling memcpy when len == 0. + if (len) { + memcpy(dst,src,len); + } + return dst; +} + +static inline void* sk_careful_memmove(void* dst, const void* src, size_t len) { + // When we pass >0 len we had better already be passing valid pointers. + // So we just need to skip calling memcpy when len == 0. + if (len) { + memmove(dst,src,len); + } + return dst; +} + +static inline int sk_careful_memcmp(const void* a, const void* b, size_t len) { + // When we pass >0 len we had better already be passing valid pointers. + // So we just need to skip calling memcmp when len == 0. + if (len == 0) { + return 0; // we treat zero-length buffers as "equal" + } + return memcmp(a, b, len); +} + +#endif // SkMalloc_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkMath.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkMath.h new file mode 100644 index 0000000000..34bfa739f7 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkMath.h @@ -0,0 +1,77 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkMath_DEFINED +#define SkMath_DEFINED + +#include "include/private/base/SkAssert.h" +#include "include/private/base/SkCPUTypes.h" + +#include +#include + +// Max Signed 16 bit value +static constexpr int16_t SK_MaxS16 = INT16_MAX; +static constexpr int16_t SK_MinS16 = -SK_MaxS16; + +static constexpr int32_t SK_MaxS32 = INT32_MAX; +static constexpr int32_t SK_MinS32 = -SK_MaxS32; +static constexpr int32_t SK_NaN32 = INT32_MIN; + +static constexpr int64_t SK_MaxS64 = INT64_MAX; +static constexpr int64_t SK_MinS64 = -SK_MaxS64; + +// 64bit -> 32bit utilities + +// Handy util that can be passed two ints, and will automatically promote to +// 64bits before the multiply, so the caller doesn't have to remember to cast +// e.g. (int64_t)a * b; +static inline int64_t sk_64_mul(int64_t a, int64_t b) { + return a * b; +} + +static inline constexpr int32_t SkLeftShift(int32_t value, int32_t shift) { + return (int32_t) ((uint32_t) value << shift); +} + +static inline constexpr int64_t SkLeftShift(int64_t value, int32_t shift) { + return (int64_t) ((uint64_t) value << shift); +} + +/////////////////////////////////////////////////////////////////////////////// + +/** + * Returns true if value is a power of 2. Does not explicitly check for + * value <= 0. + */ +template constexpr inline bool SkIsPow2(T value) { + return (value & (value - 1)) == 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +/** + * Return a*b/((1 << shift) - 1), rounding any fractional bits. + * Only valid if a and b are unsigned and <= 32767 and shift is > 0 and <= 8 + */ +static inline unsigned SkMul16ShiftRound(U16CPU a, U16CPU b, int shift) { + SkASSERT(a <= 32767); + SkASSERT(b <= 32767); + SkASSERT(shift > 0 && shift <= 8); + unsigned prod = a*b + (1 << (shift - 1)); + return (prod + (prod >> shift)) >> shift; +} + +/** + * Return a*b/255, rounding any fractional bits. + * Only valid if a and b are unsigned and <= 32767. + */ +static inline U8CPU SkMulDiv255Round(U16CPU a, U16CPU b) { + return SkMul16ShiftRound(a, b, 8); +} + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkMutex.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkMutex.h new file mode 100644 index 0000000000..4452beb912 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkMutex.h @@ -0,0 +1,64 @@ +/* + * 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 SkMutex_DEFINED +#define SkMutex_DEFINED + +#include "include/private/base/SkAssert.h" +#include "include/private/base/SkDebug.h" +#include "include/private/base/SkSemaphore.h" +#include "include/private/base/SkThreadAnnotations.h" +#include "include/private/base/SkThreadID.h" + +class SK_CAPABILITY("mutex") SkMutex { +public: + constexpr SkMutex() = default; + + ~SkMutex() { + this->assertNotHeld(); + } + + void acquire() SK_ACQUIRE() { + fSemaphore.wait(); + SkDEBUGCODE(fOwner = SkGetThreadID();) + } + + void release() SK_RELEASE_CAPABILITY() { + this->assertHeld(); + SkDEBUGCODE(fOwner = kIllegalThreadID;) + fSemaphore.signal(); + } + + void assertHeld() SK_ASSERT_CAPABILITY(this) { + SkASSERT(fOwner == SkGetThreadID()); + } + + void assertNotHeld() { + SkASSERT(fOwner == kIllegalThreadID); + } + +private: + SkSemaphore fSemaphore{1}; + SkDEBUGCODE(SkThreadID fOwner{kIllegalThreadID};) +}; + +class SK_SCOPED_CAPABILITY SkAutoMutexExclusive { +public: + SkAutoMutexExclusive(SkMutex& mutex) SK_ACQUIRE(mutex) : fMutex(mutex) { fMutex.acquire(); } + ~SkAutoMutexExclusive() SK_RELEASE_CAPABILITY() { fMutex.release(); } + + SkAutoMutexExclusive(const SkAutoMutexExclusive&) = delete; + SkAutoMutexExclusive(SkAutoMutexExclusive&&) = delete; + + SkAutoMutexExclusive& operator=(const SkAutoMutexExclusive&) = delete; + SkAutoMutexExclusive& operator=(SkAutoMutexExclusive&&) = delete; + +private: + SkMutex& fMutex; +}; + +#endif // SkMutex_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkNoncopyable.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkNoncopyable.h new file mode 100644 index 0000000000..ec4a4e5161 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkNoncopyable.h @@ -0,0 +1,30 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkNoncopyable_DEFINED +#define SkNoncopyable_DEFINED + +#include "include/private/base/SkAPI.h" + +/** \class SkNoncopyable (DEPRECATED) + + SkNoncopyable is the base class for objects that do not want to + be copied. It hides its copy-constructor and its assignment-operator. +*/ +class SK_API SkNoncopyable { +public: + SkNoncopyable() = default; + + SkNoncopyable(SkNoncopyable&&) = default; + SkNoncopyable& operator =(SkNoncopyable&&) = default; + +private: + SkNoncopyable(const SkNoncopyable&) = delete; + SkNoncopyable& operator=(const SkNoncopyable&) = delete; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkOnce.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkOnce.h new file mode 100644 index 0000000000..97ce6b6311 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkOnce.h @@ -0,0 +1,55 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkOnce_DEFINED +#define SkOnce_DEFINED + +#include "include/private/base/SkThreadAnnotations.h" + +#include +#include +#include + +// SkOnce provides call-once guarantees for Skia, much like std::once_flag/std::call_once(). +// +// There should be no particularly error-prone gotcha use cases when using SkOnce. +// It works correctly as a class member, a local, a global, a function-scoped static, whatever. + +class SkOnce { +public: + constexpr SkOnce() = default; + + template + void operator()(Fn&& fn, Args&&... args) { + auto state = fState.load(std::memory_order_acquire); + + if (state == Done) { + return; + } + + // If it looks like no one has started calling fn(), try to claim that job. + if (state == NotStarted && fState.compare_exchange_strong(state, Claimed, + std::memory_order_relaxed, + std::memory_order_relaxed)) { + // Great! We'll run fn() then notify the other threads by releasing Done into fState. + fn(std::forward(args)...); + return fState.store(Done, std::memory_order_release); + } + + // Some other thread is calling fn(). + // We'll just spin here acquiring until it releases Done into fState. + SK_POTENTIALLY_BLOCKING_REGION_BEGIN; + while (fState.load(std::memory_order_acquire) != Done) { /*spin*/ } + SK_POTENTIALLY_BLOCKING_REGION_END; + } + +private: + enum State : uint8_t { NotStarted, Claimed, Done}; + std::atomic fState{NotStarted}; +}; + +#endif // SkOnce_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkPoint_impl.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkPoint_impl.h new file mode 100644 index 0000000000..e3843b240b --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkPoint_impl.h @@ -0,0 +1,560 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkPoint_DEFINED +#define SkPoint_DEFINED + +#include "include/private/base/SkAPI.h" +#include "include/private/base/SkFloatingPoint.h" +#include "include/private/base/SkSafe32.h" + +#include +#include + +struct SkIPoint; + +/** SkIVector provides an alternative name for SkIPoint. SkIVector and SkIPoint + can be used interchangeably for all purposes. +*/ +typedef SkIPoint SkIVector; + +/** \struct SkIPoint + SkIPoint holds two 32-bit integer coordinates. +*/ +struct SkIPoint { + int32_t fX; //!< x-axis value + int32_t fY; //!< y-axis value + + /** Sets fX to x, fY to y. + + @param x integer x-axis value of constructed SkIPoint + @param y integer y-axis value of constructed SkIPoint + @return SkIPoint (x, y) + */ + static constexpr SkIPoint Make(int32_t x, int32_t y) { + return {x, y}; + } + + /** Returns x-axis value of SkIPoint. + + @return fX + */ + constexpr int32_t x() const { return fX; } + + /** Returns y-axis value of SkIPoint. + + @return fY + */ + constexpr int32_t y() const { return fY; } + + /** Returns true if fX and fY are both zero. + + @return true if fX is zero and fY is zero + */ + bool isZero() const { return (fX | fY) == 0; } + + /** Sets fX to x and fY to y. + + @param x new value for fX + @param y new value for fY + */ + void set(int32_t x, int32_t y) { + fX = x; + fY = y; + } + + /** Returns SkIPoint changing the signs of fX and fY. + + @return SkIPoint as (-fX, -fY) + */ + SkIPoint operator-() const { + return {-fX, -fY}; + } + + /** Offsets SkIPoint by ivector v. Sets SkIPoint to (fX + v.fX, fY + v.fY). + + @param v ivector to add + */ + void operator+=(const SkIVector& v) { + fX = Sk32_sat_add(fX, v.fX); + fY = Sk32_sat_add(fY, v.fY); + } + + /** Subtracts ivector v from SkIPoint. Sets SkIPoint to: (fX - v.fX, fY - v.fY). + + @param v ivector to subtract + */ + void operator-=(const SkIVector& v) { + fX = Sk32_sat_sub(fX, v.fX); + fY = Sk32_sat_sub(fY, v.fY); + } + + /** Returns true if SkIPoint is equivalent to SkIPoint constructed from (x, y). + + @param x value compared with fX + @param y value compared with fY + @return true if SkIPoint equals (x, y) + */ + bool equals(int32_t x, int32_t y) const { + return fX == x && fY == y; + } + + /** Returns true if a is equivalent to b. + + @param a SkIPoint to compare + @param b SkIPoint to compare + @return true if a.fX == b.fX and a.fY == b.fY + */ + friend bool operator==(const SkIPoint& a, const SkIPoint& b) { + return a.fX == b.fX && a.fY == b.fY; + } + + /** Returns true if a is not equivalent to b. + + @param a SkIPoint to compare + @param b SkIPoint to compare + @return true if a.fX != b.fX or a.fY != b.fY + */ + friend bool operator!=(const SkIPoint& a, const SkIPoint& b) { + return a.fX != b.fX || a.fY != b.fY; + } + + /** Returns ivector from b to a; computed as (a.fX - b.fX, a.fY - b.fY). + + Can also be used to subtract ivector from ivector, returning ivector. + + @param a SkIPoint or ivector to subtract from + @param b ivector to subtract + @return ivector from b to a + */ + friend SkIVector operator-(const SkIPoint& a, const SkIPoint& b) { + return { Sk32_sat_sub(a.fX, b.fX), Sk32_sat_sub(a.fY, b.fY) }; + } + + /** Returns SkIPoint resulting from SkIPoint a offset by ivector b, computed as: + (a.fX + b.fX, a.fY + b.fY). + + Can also be used to offset SkIPoint b by ivector a, returning SkIPoint. + Can also be used to add ivector to ivector, returning ivector. + + @param a SkIPoint or ivector to add to + @param b SkIPoint or ivector to add + @return SkIPoint equal to a offset by b + */ + friend SkIPoint operator+(const SkIPoint& a, const SkIVector& b) { + return { Sk32_sat_add(a.fX, b.fX), Sk32_sat_add(a.fY, b.fY) }; + } +}; + +struct SkPoint; + +/** SkVector provides an alternative name for SkPoint. SkVector and SkPoint can + be used interchangeably for all purposes. +*/ +typedef SkPoint SkVector; + +/** \struct SkPoint + SkPoint holds two 32-bit floating point coordinates. +*/ +struct SK_API SkPoint { + float fX; //!< x-axis value + float fY; //!< y-axis value + + /** Sets fX to x, fY to y. Used both to set SkPoint and vector. + + @param x float x-axis value of constructed SkPoint or vector + @param y float y-axis value of constructed SkPoint or vector + @return SkPoint (x, y) + */ + static constexpr SkPoint Make(float x, float y) { + return {x, y}; + } + + /** Returns x-axis value of SkPoint or vector. + + @return fX + */ + constexpr float x() const { return fX; } + + /** Returns y-axis value of SkPoint or vector. + + @return fY + */ + constexpr float y() const { return fY; } + + /** Returns true if fX and fY are both zero. + + @return true if fX is zero and fY is zero + */ + bool isZero() const { return (0 == fX) & (0 == fY); } + + /** Sets fX to x and fY to y. + + @param x new value for fX + @param y new value for fY + */ + void set(float x, float y) { + fX = x; + fY = y; + } + + /** Sets fX to x and fY to y, promoting integers to float values. + + Assigning a large integer value directly to fX or fY may cause a compiler + error, triggered by narrowing conversion of int to float. This safely + casts x and y to avoid the error. + + @param x new value for fX + @param y new value for fY + */ + void iset(int32_t x, int32_t y) { + fX = static_cast(x); + fY = static_cast(y); + } + + /** Sets fX to p.fX and fY to p.fY, promoting integers to float values. + + Assigning an SkIPoint containing a large integer value directly to fX or fY may + cause a compiler error, triggered by narrowing conversion of int to float. + This safely casts p.fX and p.fY to avoid the error. + + @param p SkIPoint members promoted to float + */ + void iset(const SkIPoint& p) { + fX = static_cast(p.fX); + fY = static_cast(p.fY); + } + + /** Sets fX to absolute value of pt.fX; and fY to absolute value of pt.fY. + + @param pt members providing magnitude for fX and fY + */ + void setAbs(const SkPoint& pt) { + fX = std::abs(pt.fX); + fY = std::abs(pt.fY); + } + + /** Adds offset to each SkPoint in points array with count entries. + + @param points SkPoint array + @param count entries in array + @param offset vector added to points + */ + static void Offset(SkPoint points[], int count, const SkVector& offset) { + Offset(points, count, offset.fX, offset.fY); + } + + /** Adds offset (dx, dy) to each SkPoint in points array of length count. + + @param points SkPoint array + @param count entries in array + @param dx added to fX in points + @param dy added to fY in points + */ + static void Offset(SkPoint points[], int count, float dx, float dy) { + for (int i = 0; i < count; ++i) { + points[i].offset(dx, dy); + } + } + + /** Adds offset (dx, dy) to SkPoint. + + @param dx added to fX + @param dy added to fY + */ + void offset(float dx, float dy) { + fX += dx; + fY += dy; + } + + /** Returns the Euclidean distance from origin, computed as: + + sqrt(fX * fX + fY * fY) + + . + + @return straight-line distance to origin + */ + float length() const { return SkPoint::Length(fX, fY); } + + /** Returns the Euclidean distance from origin, computed as: + + sqrt(fX * fX + fY * fY) + + . + + @return straight-line distance to origin + */ + float distanceToOrigin() const { return this->length(); } + + /** Scales (fX, fY) so that length() returns one, while preserving ratio of fX to fY, + if possible. If prior length is nearly zero, sets vector to (0, 0) and returns + false; otherwise returns true. + + @return true if former length is not zero or nearly zero + + example: https://fiddle.skia.org/c/@Point_normalize_2 + */ + bool normalize(); + + /** Sets vector to (x, y) scaled so length() returns one, and so that + (fX, fY) is proportional to (x, y). If (x, y) length is nearly zero, + sets vector to (0, 0) and returns false; otherwise returns true. + + @param x proportional value for fX + @param y proportional value for fY + @return true if (x, y) length is not zero or nearly zero + + example: https://fiddle.skia.org/c/@Point_setNormalize + */ + bool setNormalize(float x, float y); + + /** Scales vector so that distanceToOrigin() returns length, if possible. If former + length is nearly zero, sets vector to (0, 0) and return false; otherwise returns + true. + + @param length straight-line distance to origin + @return true if former length is not zero or nearly zero + + example: https://fiddle.skia.org/c/@Point_setLength + */ + bool setLength(float length); + + /** Sets vector to (x, y) scaled to length, if possible. If former + length is nearly zero, sets vector to (0, 0) and return false; otherwise returns + true. + + @param x proportional value for fX + @param y proportional value for fY + @param length straight-line distance to origin + @return true if (x, y) length is not zero or nearly zero + + example: https://fiddle.skia.org/c/@Point_setLength_2 + */ + bool setLength(float x, float y, float length); + + /** Sets dst to SkPoint times scale. dst may be SkPoint to modify SkPoint in place. + + @param scale factor to multiply SkPoint by + @param dst storage for scaled SkPoint + + example: https://fiddle.skia.org/c/@Point_scale + */ + void scale(float scale, SkPoint* dst) const; + + /** Scales SkPoint in place by scale. + + @param value factor to multiply SkPoint by + */ + void scale(float value) { this->scale(value, this); } + + /** Changes the sign of fX and fY. + */ + void negate() { + fX = -fX; + fY = -fY; + } + + /** Returns SkPoint changing the signs of fX and fY. + + @return SkPoint as (-fX, -fY) + */ + SkPoint operator-() const { + return {-fX, -fY}; + } + + /** Adds vector v to SkPoint. Sets SkPoint to: (fX + v.fX, fY + v.fY). + + @param v vector to add + */ + void operator+=(const SkVector& v) { + fX += v.fX; + fY += v.fY; + } + + /** Subtracts vector v from SkPoint. Sets SkPoint to: (fX - v.fX, fY - v.fY). + + @param v vector to subtract + */ + void operator-=(const SkVector& v) { + fX -= v.fX; + fY -= v.fY; + } + + /** Returns SkPoint multiplied by scale. + + @param scale float to multiply by + @return SkPoint as (fX * scale, fY * scale) + */ + SkPoint operator*(float scale) const { + return {fX * scale, fY * scale}; + } + + /** Multiplies SkPoint by scale. Sets SkPoint to: (fX * scale, fY * scale). + + @param scale float to multiply by + @return reference to SkPoint + */ + SkPoint& operator*=(float scale) { + fX *= scale; + fY *= scale; + return *this; + } + + /** Returns true if both fX and fY are measurable values. + + @return true for values other than infinities and NaN + */ + bool isFinite() const { + return SkIsFinite(fX, fY); + } + + /** Returns true if SkPoint is equivalent to SkPoint constructed from (x, y). + + @param x value compared with fX + @param y value compared with fY + @return true if SkPoint equals (x, y) + */ + bool equals(float x, float y) const { + return fX == x && fY == y; + } + + /** Returns true if a is equivalent to b. + + @param a SkPoint to compare + @param b SkPoint to compare + @return true if a.fX == b.fX and a.fY == b.fY + */ + friend bool operator==(const SkPoint& a, const SkPoint& b) { + return a.fX == b.fX && a.fY == b.fY; + } + + /** Returns true if a is not equivalent to b. + + @param a SkPoint to compare + @param b SkPoint to compare + @return true if a.fX != b.fX or a.fY != b.fY + */ + friend bool operator!=(const SkPoint& a, const SkPoint& b) { + return a.fX != b.fX || a.fY != b.fY; + } + + /** Returns vector from b to a, computed as (a.fX - b.fX, a.fY - b.fY). + + Can also be used to subtract vector from SkPoint, returning SkPoint. + Can also be used to subtract vector from vector, returning vector. + + @param a SkPoint to subtract from + @param b SkPoint to subtract + @return vector from b to a + */ + friend SkVector operator-(const SkPoint& a, const SkPoint& b) { + return {a.fX - b.fX, a.fY - b.fY}; + } + + /** Returns SkPoint resulting from SkPoint a offset by vector b, computed as: + (a.fX + b.fX, a.fY + b.fY). + + Can also be used to offset SkPoint b by vector a, returning SkPoint. + Can also be used to add vector to vector, returning vector. + + @param a SkPoint or vector to add to + @param b SkPoint or vector to add + @return SkPoint equal to a offset by b + */ + friend SkPoint operator+(const SkPoint& a, const SkVector& b) { + return {a.fX + b.fX, a.fY + b.fY}; + } + + /** Returns the Euclidean distance from origin, computed as: + + sqrt(x * x + y * y) + + . + + @param x component of length + @param y component of length + @return straight-line distance to origin + + example: https://fiddle.skia.org/c/@Point_Length + */ + static float Length(float x, float y); + + /** Scales (vec->fX, vec->fY) so that length() returns one, while preserving ratio of vec->fX + to vec->fY, if possible. If original length is nearly zero, sets vec to (0, 0) and returns + zero; otherwise, returns length of vec before vec is scaled. + + Returned prior length may be INFINITY if it can not be represented by float. + + Note that normalize() is faster if prior length is not required. + + @param vec normalized to unit length + @return original vec length + + example: https://fiddle.skia.org/c/@Point_Normalize + */ + static float Normalize(SkVector* vec); + + /** Returns the Euclidean distance between a and b. + + @param a line end point + @param b line end point + @return straight-line distance from a to b + */ + static float Distance(const SkPoint& a, const SkPoint& b) { + return Length(a.fX - b.fX, a.fY - b.fY); + } + + /** Returns the dot product of vector a and vector b. + + @param a left side of dot product + @param b right side of dot product + @return product of input magnitudes and cosine of the angle between them + */ + static float DotProduct(const SkVector& a, const SkVector& b) { + return a.fX * b.fX + a.fY * b.fY; + } + + /** Returns the cross product of vector a and vector b. + + a and b form three-dimensional vectors with z-axis value equal to zero. The + cross product is a three-dimensional vector with x-axis and y-axis values equal + to zero. The cross product z-axis component is returned. + + @param a left side of cross product + @param b right side of cross product + @return area spanned by vectors signed by angle direction + */ + static float CrossProduct(const SkVector& a, const SkVector& b) { + return a.fX * b.fY - a.fY * b.fX; + } + + /** Returns the cross product of vector and vec. + + Vector and vec form three-dimensional vectors with z-axis value equal to zero. + The cross product is a three-dimensional vector with x-axis and y-axis values + equal to zero. The cross product z-axis component is returned. + + @param vec right side of cross product + @return area spanned by vectors signed by angle direction + */ + float cross(const SkVector& vec) const { + return CrossProduct(*this, vec); + } + + /** Returns the dot product of vector and vector vec. + + @param vec right side of dot product + @return product of input magnitudes and cosine of the angle between them + */ + float dot(const SkVector& vec) const { + return DotProduct(*this, vec); + } + +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkSafe32.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkSafe32.h new file mode 100644 index 0000000000..5ba4c2f9a4 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkSafe32.h @@ -0,0 +1,49 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkSafe32_DEFINED +#define SkSafe32_DEFINED + +#include "include/private/base/SkAssert.h" +#include "include/private/base/SkMath.h" + +#include + +static constexpr int32_t Sk64_pin_to_s32(int64_t x) { + return x < SK_MinS32 ? SK_MinS32 : (x > SK_MaxS32 ? SK_MaxS32 : (int32_t)x); +} + +static constexpr int32_t Sk32_sat_add(int32_t a, int32_t b) { + return Sk64_pin_to_s32((int64_t)a + (int64_t)b); +} + +static constexpr int32_t Sk32_sat_sub(int32_t a, int32_t b) { + return Sk64_pin_to_s32((int64_t)a - (int64_t)b); +} + +// To avoid UBSAN complaints about 2's compliment overflows +// +static constexpr int32_t Sk32_can_overflow_add(int32_t a, int32_t b) { + return (int32_t)((uint32_t)a + (uint32_t)b); +} +static constexpr int32_t Sk32_can_overflow_sub(int32_t a, int32_t b) { + return (int32_t)((uint32_t)a - (uint32_t)b); +} + +/** + * This is a 'safe' abs for 32-bit integers that asserts when undefined behavior would occur. + * SkTAbs (in SkTemplates.h) is a general purpose absolute-value function. + */ +static inline int32_t SkAbs32(int32_t value) { + SkASSERT(value != SK_NaN32); // The most negative int32_t can't be negated. + if (value < 0) { + value = -value; + } + return value; +} + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkSemaphore.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkSemaphore.h new file mode 100644 index 0000000000..f78ee86625 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkSemaphore.h @@ -0,0 +1,84 @@ +/* + * 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 SkSemaphore_DEFINED +#define SkSemaphore_DEFINED + +#include "include/private/base/SkAPI.h" +#include "include/private/base/SkOnce.h" +#include "include/private/base/SkThreadAnnotations.h" + +#include +#include + +class SkSemaphore { +public: + constexpr SkSemaphore(int count = 0) : fCount(count), fOSSemaphore(nullptr) {} + + // Cleanup the underlying OS semaphore. + SK_SPI ~SkSemaphore(); + + // Increment the counter n times. + // Generally it's better to call signal(n) instead of signal() n times. + void signal(int n = 1); + + // Decrement the counter by 1, + // then if the counter is < 0, sleep this thread until the counter is >= 0. + void wait(); + + // If the counter is positive, decrement it by 1 and return true, otherwise return false. + SK_SPI bool try_wait(); + +private: + // This implementation follows the general strategy of + // 'A Lightweight Semaphore with Partial Spinning' + // found here + // http://preshing.com/20150316/semaphores-are-surprisingly-versatile/ + // That article (and entire blog) are very much worth reading. + // + // We wrap an OS-provided semaphore with a user-space atomic counter that + // lets us avoid interacting with the OS semaphore unless strictly required: + // moving the count from >=0 to <0 or vice-versa, i.e. sleeping or waking threads. + struct OSSemaphore; + + SK_SPI void osSignal(int n); + SK_SPI void osWait(); + + std::atomic fCount; + SkOnce fOSSemaphoreOnce; + OSSemaphore* fOSSemaphore; +}; + +inline void SkSemaphore::signal(int n) { + int prev = fCount.fetch_add(n, std::memory_order_release); + + // We only want to call the OS semaphore when our logical count crosses + // from <0 to >=0 (when we need to wake sleeping threads). + // + // This is easiest to think about with specific examples of prev and n. + // If n == 5 and prev == -3, there are 3 threads sleeping and we signal + // std::min(-(-3), 5) == 3 times on the OS semaphore, leaving the count at 2. + // + // If prev >= 0, no threads are waiting, std::min(-prev, n) is always <= 0, + // so we don't call the OS semaphore, leaving the count at (prev + n). + int toSignal = std::min(-prev, n); + if (toSignal > 0) { + this->osSignal(toSignal); + } +} + +inline void SkSemaphore::wait() { + // Since this fetches the value before the subtract, zero and below means that there are no + // resources left, so the thread needs to wait. + if (fCount.fetch_sub(1, std::memory_order_acquire) <= 0) { + SK_POTENTIALLY_BLOCKING_REGION_BEGIN; + this->osWait(); + SK_POTENTIALLY_BLOCKING_REGION_END; + } +} + +#endif//SkSemaphore_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkSpan_impl.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkSpan_impl.h new file mode 100644 index 0000000000..09b2a754e9 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkSpan_impl.h @@ -0,0 +1,131 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkSpan_DEFINED +#define SkSpan_DEFINED + +#include "include/private/base/SkAssert.h" +#include "include/private/base/SkDebug.h" +#include "include/private/base/SkTo.h" + +#include +#include +#include +#include +#include + +// Having this be an export works around IWYU churn related to +// https://github.com/include-what-you-use/include-what-you-use/issues/1121 +#include // IWYU pragma: export + +// Add macro to check the lifetime of initializer_list arguments. initializer_list has a very +// short life span, and can only be used as a parameter, and not as a variable. +#if defined(__clang__) && defined(__has_cpp_attribute) && __has_cpp_attribute(clang::lifetimebound) +#define SK_CHECK_IL_LIFETIME [[clang::lifetimebound]] +#else +#define SK_CHECK_IL_LIFETIME +#endif + +/** + * SkSpan holds a reference to contiguous data of type T along with a count. SkSpan does not own + * the data itself but is merely a reference, therefore you must take care with the lifetime of + * the underlying data. + * + * SkSpan is a count and a pointer into existing array or data type that stores its data in + * contiguous memory like std::vector. Any container that works with std::size() and std::data() + * can be used. + * + * SkSpan makes a convenient parameter for a routine to accept array like things. This allows you to + * write the routine without overloads for all different container types. + * + * Example: + * void routine(SkSpan a) { ... } + * + * std::vector v = {1, 2, 3, 4, 5}; + * + * routine(a); + * + * A word of caution when working with initializer_list, initializer_lists have a lifetime that is + * limited to the current statement. The following is correct and safe: + * + * Example: + * routine({1,2,3,4,5}); + * + * The following is undefined, and will result in erratic execution: + * + * Bad Example: + * initializer_list l = {1, 2, 3, 4, 5}; // The data behind l dies at the ;. + * routine(l); + */ +template +class SkSpan { +public: + constexpr SkSpan() : fPtr{nullptr}, fSize{0} {} + + template , bool> = true> + constexpr SkSpan(T* ptr, Integer size) : fPtr{ptr}, fSize{SkToSizeT(size)} { + SkASSERT(ptr || fSize == 0); // disallow nullptr + a nonzero size + SkASSERT(fSize < kMaxSize); + } + template >> + constexpr SkSpan(const SkSpan& that) : fPtr(std::data(that)), fSize(std::size(that)) {} + constexpr SkSpan(const SkSpan& o) = default; + template constexpr SkSpan(T(&a)[N]) : SkSpan(a, N) { } + template + constexpr SkSpan(Container&& c) : SkSpan(std::data(c), std::size(c)) { } + SkSpan(std::initializer_list il SK_CHECK_IL_LIFETIME) + : SkSpan(std::data(il), std::size(il)) {} + + constexpr SkSpan& operator=(const SkSpan& that) = default; + + constexpr T& operator [] (size_t i) const { + return fPtr[sk_collection_check_bounds(i, this->size())]; + } + constexpr T& front() const { sk_collection_not_empty(this->empty()); return fPtr[0]; } + constexpr T& back() const { sk_collection_not_empty(this->empty()); return fPtr[fSize - 1]; } + constexpr T* begin() const { return fPtr; } + constexpr T* end() const { return fPtr + fSize; } + constexpr auto rbegin() const { return std::make_reverse_iterator(this->end()); } + constexpr auto rend() const { return std::make_reverse_iterator(this->begin()); } + constexpr T* data() const { return this->begin(); } + constexpr size_t size() const { return fSize; } + constexpr bool empty() const { return fSize == 0; } + constexpr size_t size_bytes() const { return fSize * sizeof(T); } + constexpr SkSpan first(size_t prefixLen) const { + return SkSpan{fPtr, sk_collection_check_length(prefixLen, fSize)}; + } + constexpr SkSpan last(size_t postfixLen) const { + return SkSpan{fPtr + (this->size() - postfixLen), + sk_collection_check_length(postfixLen, fSize)}; + } + constexpr SkSpan subspan(size_t offset) const { + return this->subspan(offset, this->size() - offset); + } + constexpr SkSpan subspan(size_t offset, size_t count) const { + const size_t safeOffset = sk_collection_check_length(offset, fSize); + + // Should read offset + count > size(), but that could overflow. We know that safeOffset + // is <= size, therefore the subtraction will not overflow. + if (count > this->size() - safeOffset) SK_UNLIKELY { + // The count is too large. + SkUNREACHABLE; + } + return SkSpan{fPtr + safeOffset, count}; + } + +private: + static constexpr size_t kMaxSize = std::numeric_limits::max() / sizeof(T); + + T* fPtr; + size_t fSize; +}; + +template +SkSpan(Container&&) -> + SkSpan()))>>; + +#endif // SkSpan_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTArray.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTArray.h new file mode 100644 index 0000000000..bb17c854c3 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTArray.h @@ -0,0 +1,806 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkTArray_DEFINED +#define SkTArray_DEFINED + +#include "include/private/base/SkASAN.h" // IWYU pragma: keep +#include "include/private/base/SkAlignedStorage.h" +#include "include/private/base/SkAssert.h" +#include "include/private/base/SkAttributes.h" +#include "include/private/base/SkContainers.h" +#include "include/private/base/SkDebug.h" +#include "include/private/base/SkMalloc.h" +#include "include/private/base/SkMath.h" +#include "include/private/base/SkSpan_impl.h" +#include "include/private/base/SkTo.h" +#include "include/private/base/SkTypeTraits.h" // IWYU pragma: keep + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace skia_private { +/** TArray implements a typical, mostly std::vector-like array. + Each T will be default-initialized on allocation, and ~T will be called on destruction. + + MEM_MOVE controls the behavior when a T needs to be moved (e.g. when the array is resized) + - true: T will be bit-copied via memcpy. + - false: T will be moved via move-constructors. +*/ +template > class TArray { +public: + using value_type = T; + + /** + * Creates an empty array with no initial storage + */ + TArray() : fOwnMemory(true), fCapacity{0} {} + + /** + * Creates an empty array that will preallocate space for reserveCount elements. + */ + explicit TArray(int reserveCount) : TArray() { this->reserve_exact(reserveCount); } + + /** + * Copies one array to another. The new array will be heap allocated. + */ + TArray(const TArray& that) : TArray(that.fData, that.fSize) {} + + TArray(TArray&& that) { + if (that.fOwnMemory) { + this->setData(that); + that.setData({}); + } else { + this->initData(that.fSize); + that.move(fData); + } + this->changeSize(that.fSize); + that.changeSize(0); + } + + /** + * Creates a TArray by copying contents of a standard C array. The new + * array will be heap allocated. Be careful not to use this constructor + * when you really want the (void*, int) version. + */ + TArray(const T* array, int count) { + this->initData(count); + this->copy(array); + } + + /** + * Creates a TArray by copying contents of an initializer list. + */ + TArray(std::initializer_list data) : TArray(data.begin(), data.size()) {} + + TArray& operator=(const TArray& that) { + if (this == &that) { + return *this; + } + this->clear(); + this->checkRealloc(that.size(), kExactFit); + this->changeSize(that.fSize); + this->copy(that.fData); + return *this; + } + + TArray& operator=(TArray&& that) { + if (this != &that) { + this->clear(); + this->unpoison(); + that.unpoison(); + if (that.fOwnMemory) { + // The storage is on the heap, so move the data pointer. + if (fOwnMemory) { + sk_free(fData); + } + + fData = std::exchange(that.fData, nullptr); + + // Can't use exchange with bitfields. + fCapacity = that.fCapacity; + that.fCapacity = 0; + + fOwnMemory = true; + + this->changeSize(that.fSize); + } else { + // The data is stored inline in that, so move it element-by-element. + this->checkRealloc(that.size(), kExactFit); + this->changeSize(that.fSize); + that.move(fData); + } + that.changeSize(0); + } + return *this; + } + + ~TArray() { + this->destroyAll(); + this->unpoison(); + if (fOwnMemory) { + sk_free(fData); + } + } + + /** + * Resets to size() = n newly constructed T objects and resets any reserve count. + */ + void reset(int n) { + SkASSERT(n >= 0); + this->clear(); + this->checkRealloc(n, kExactFit); + this->changeSize(n); + for (int i = 0; i < this->size(); ++i) { + new (fData + i) T; + } + } + + /** + * Resets to a copy of a C array and resets any reserve count. + */ + void reset(const T* array, int count) { + SkASSERT(count >= 0); + this->clear(); + this->checkRealloc(count, kExactFit); + this->changeSize(count); + this->copy(array); + } + + /** + * Ensures there is enough reserved space for at least n elements. This is guaranteed at least + * until the array size grows above n and subsequently shrinks below n, any version of reset() + * is called, or reserve() is called again. + */ + void reserve(int n) { + SkASSERT(n >= 0); + if (n > this->size()) { + this->checkRealloc(n - this->size(), kGrowing); + } + } + + /** + * Ensures there is enough reserved space for exactly n elements. The same capacity guarantees + * as above apply. + */ + void reserve_exact(int n) { + SkASSERT(n >= 0); + if (n > this->size()) { + this->checkRealloc(n - this->size(), kExactFit); + } + } + + void removeShuffle(int n) { + SkASSERT(n < this->size()); + int newCount = fSize - 1; + fData[n].~T(); + if (n != newCount) { + this->move(n, newCount); + } + this->changeSize(newCount); + } + + // Is the array empty. + bool empty() const { return fSize == 0; } + + /** + * Adds one new default-initialized T value and returns it by reference. Note that the reference + * only remains valid until the next call that adds or removes elements. + */ + T& push_back() { + void* newT = this->push_back_raw(1); + return *new (newT) T; + } + + /** + * Adds one new T value which is copy-constructed, returning it by reference. As always, + * the reference only remains valid until the next call that adds or removes elements. + */ + T& push_back(const T& t) { + this->unpoison(); + T* newT; + if (this->capacity() > fSize) SK_LIKELY { + // Copy over the element directly. + newT = new (fData + fSize) T(t); + } else { + newT = this->growAndConstructAtEnd(t); + } + + this->changeSize(fSize + 1); + return *newT; + } + + /** + * Adds one new T value which is copy-constructed, returning it by reference. + */ + T& push_back(T&& t) { + this->unpoison(); + T* newT; + if (this->capacity() > fSize) SK_LIKELY { + // Move over the element directly. + newT = new (fData + fSize) T(std::move(t)); + } else { + newT = this->growAndConstructAtEnd(std::move(t)); + } + + this->changeSize(fSize + 1); + return *newT; + } + + /** + * Constructs a new T at the back of this array, returning it by reference. + */ + template T& emplace_back(Args&&... args) { + this->unpoison(); + T* newT; + if (this->capacity() > fSize) SK_LIKELY { + // Emplace the new element in directly. + newT = new (fData + fSize) T(std::forward(args)...); + } else { + newT = this->growAndConstructAtEnd(std::forward(args)...); + } + + this->changeSize(fSize + 1); + return *newT; + } + + /** + * Allocates n more default-initialized T values, and returns the address of + * the start of that new range. Note: this address is only valid until the + * next API call made on the array that might add or remove elements. + */ + T* push_back_n(int n) { + SkASSERT(n >= 0); + T* newTs = TCast(this->push_back_raw(n)); + for (int i = 0; i < n; ++i) { + new (&newTs[i]) T; + } + return newTs; + } + + /** + * Version of above that uses a copy constructor to initialize all n items + * to the same T. + */ + T* push_back_n(int n, const T& t) { + SkASSERT(n >= 0); + T* newTs = TCast(this->push_back_raw(n)); + for (int i = 0; i < n; ++i) { + new (&newTs[i]) T(t); + } + return static_cast(newTs); + } + + /** + * Version of above that uses a copy constructor to initialize the n items + * to separate T values. + */ + T* push_back_n(int n, const T t[]) { + SkASSERT(n >= 0); + this->checkRealloc(n, kGrowing); + T* end = this->end(); + this->changeSize(fSize + n); + for (int i = 0; i < n; ++i) { + new (end + i) T(t[i]); + } + return end; + } + + /** + * Version of above that uses the move constructor to set n items. + */ + T* move_back_n(int n, T* t) { + SkASSERT(n >= 0); + this->checkRealloc(n, kGrowing); + T* end = this->end(); + this->changeSize(fSize + n); + for (int i = 0; i < n; ++i) { + new (end + i) T(std::move(t[i])); + } + return end; + } + + /** + * Removes the last element. Not safe to call when size() == 0. + */ + void pop_back() { + sk_collection_not_empty(this->empty()); + fData[fSize - 1].~T(); + this->changeSize(fSize - 1); + } + + /** + * Removes the last n elements. Not safe to call when size() < n. + */ + void pop_back_n(int n) { + SkASSERT(n >= 0); + SkASSERT(this->size() >= n); + int i = fSize; + while (i-- > fSize - n) { + (*this)[i].~T(); + } + this->changeSize(fSize - n); + } + + /** + * Pushes or pops from the back to resize. Pushes will be default initialized. + */ + void resize_back(int newCount) { + SkASSERT(newCount >= 0); + if (newCount > this->size()) { + if (this->empty()) { + // When the container is completely empty, grow to exactly the requested size. + this->checkRealloc(newCount, kExactFit); + } + this->push_back_n(newCount - fSize); + } else if (newCount < this->size()) { + this->pop_back_n(fSize - newCount); + } + } + + /** Swaps the contents of this array with that array. Does a pointer swap if possible, + otherwise copies the T values. */ + void swap(TArray& that) { + using std::swap; + if (this == &that) { + return; + } + if (fOwnMemory && that.fOwnMemory) { + swap(fData, that.fData); + swap(fSize, that.fSize); + + // Can't use swap because fCapacity is a bit field. + auto allocCount = fCapacity; + fCapacity = that.fCapacity; + that.fCapacity = allocCount; + } else { + // This could be more optimal... + TArray copy(std::move(that)); + that = std::move(*this); + *this = std::move(copy); + } + } + + /** + * Moves all elements of `that` to the end of this array, leaving `that` empty. + * This is a no-op if `that` is empty or equal to this array. + */ + void move_back(TArray& that) { + if (that.empty() || &that == this) { + return; + } + void* dst = this->push_back_raw(that.size()); + // After move() returns, the contents of `dst` will have either been in-place initialized + // using a the move constructor (per-item from `that`'s elements), or will have been + // mem-copied into when MEM_MOVE is true (now valid objects). + that.move(dst); + // All items in `that` have either been destroyed (when MEM_MOVE is false) or should be + // considered invalid (when MEM_MOVE is true). Reset fSize to 0 directly to skip any further + // per-item destruction. + that.changeSize(0); + } + + T* begin() { + return fData; + } + const T* begin() const { + return fData; + } + + // It's safe to use fItemArray + fSize because if fItemArray is nullptr then adding 0 is + // valid and returns nullptr. See [expr.add] in the C++ standard. + T* end() { + if (fData == nullptr) { + SkASSERT(fSize == 0); + } + return fData + fSize; + } + const T* end() const { + if (fData == nullptr) { + SkASSERT(fSize == 0); + } + return fData + fSize; + } + T* data() { return fData; } + const T* data() const { return fData; } + int size() const { return fSize; } + size_t size_bytes() const { return Bytes(fSize); } + void resize(size_t count) { this->resize_back((int)count); } + + void clear() { + this->destroyAll(); + this->changeSize(0); + } + + void shrink_to_fit() { + if (!fOwnMemory || fSize == fCapacity) { + return; + } + this->unpoison(); + if (fSize == 0) { + sk_free(fData); + fData = nullptr; + fCapacity = 0; + } else { + SkSpan allocation = Allocate(fSize); + this->move(TCast(allocation.data())); + if (fOwnMemory) { + sk_free(fData); + } + // Poison is applied in `setDataFromBytes`. + this->setDataFromBytes(allocation); + } + } + + /** + * Get the i^th element. + */ + T& operator[] (int i) { + return fData[sk_collection_check_bounds(i, this->size())]; + } + + const T& operator[] (int i) const { + return fData[sk_collection_check_bounds(i, this->size())]; + } + + T& at(int i) { return (*this)[i]; } + const T& at(int i) const { return (*this)[i]; } + + /** + * equivalent to operator[](0) + */ + T& front() { + sk_collection_not_empty(this->empty()); + return fData[0]; + } + + const T& front() const { + sk_collection_not_empty(this->empty()); + return fData[0]; + } + + /** + * equivalent to operator[](size() - 1) + */ + T& back() { + sk_collection_not_empty(this->empty()); + return fData[fSize - 1]; + } + + const T& back() const { + sk_collection_not_empty(this->empty()); + return fData[fSize - 1]; + } + + /** + * equivalent to operator[](size()-1-i) + */ + T& fromBack(int i) { + return (*this)[fSize - i - 1]; + } + + const T& fromBack(int i) const { + return (*this)[fSize - i - 1]; + } + + bool operator==(const TArray& right) const { + int leftCount = this->size(); + if (leftCount != right.size()) { + return false; + } + for (int index = 0; index < leftCount; ++index) { + if (fData[index] != right.fData[index]) { + return false; + } + } + return true; + } + + bool operator!=(const TArray& right) const { + return !(*this == right); + } + + int capacity() const { + return fCapacity; + } + +protected: + // Creates an empty array that will use the passed storage block until it is insufficiently + // large to hold the entire array. + template + TArray(SkAlignedSTStorage* storage, int size = 0) { + static_assert(InitialCapacity >= 0); + SkASSERT(size >= 0); + SkASSERT(storage->get() != nullptr); + if (size > InitialCapacity) { + this->initData(size); + } else { + this->setDataFromBytes(*storage); + this->changeSize(size); + + // setDataFromBytes always sets fOwnMemory to true, but we are actually using static + // storage here, which shouldn't ever be freed. + fOwnMemory = false; + } + } + + // Copy a C array, using pre-allocated storage if preAllocCount >= count. Otherwise, storage + // will only be used when array shrinks to fit. + template + TArray(const T* array, int size, SkAlignedSTStorage* storage) + : TArray{storage, size} { + this->copy(array); + } + +private: + // Growth factors for checkRealloc. + static constexpr double kExactFit = 1.0; + static constexpr double kGrowing = 1.5; + + static constexpr int kMinHeapAllocCount = 8; + static_assert(SkIsPow2(kMinHeapAllocCount), "min alloc count not power of two."); + + // Note for 32-bit machines kMaxCapacity will be <= SIZE_MAX. For 64-bit machines it will + // just be INT_MAX if the sizeof(T) < 2^32. + static constexpr int kMaxCapacity = SkToInt(std::min(SIZE_MAX / sizeof(T), (size_t)INT_MAX)); + + void setDataFromBytes(SkSpan allocation) { + T* data = TCast(allocation.data()); + // We have gotten extra bytes back from the allocation limit, pin to kMaxCapacity. It + // would seem like the SkContainerAllocator should handle the divide, but it would have + // to a full divide instruction. If done here the size is known at compile, and usually + // can be implemented by a right shift. The full divide takes ~50X longer than the shift. + size_t size = std::min(allocation.size() / sizeof(T), SkToSizeT(kMaxCapacity)); + this->setData(SkSpan(data, size)); + } + + void setData(SkSpan array) { + this->unpoison(); + + fData = array.data(); + fCapacity = SkToU32(array.size()); + fOwnMemory = true; + + this->poison(); + } + + void unpoison() { +#ifdef SK_SANITIZE_ADDRESS + if (fData) { + // SkDebugf("UNPOISONING %p : 0 -> %zu\n", fData, Bytes(fCapacity)); + sk_asan_unpoison_memory_region(this->begin(), Bytes(fCapacity)); + } +#endif + } + + void poison() { +#ifdef SK_SANITIZE_ADDRESS + if (fData && fCapacity > fSize) { + // SkDebugf(" POISONING %p : %zu -> %zu\n", fData, Bytes(fSize), Bytes(fCapacity)); + sk_asan_poison_memory_region(this->end(), Bytes(fCapacity - fSize)); + } +#endif + } + + void changeSize(int n) { + this->unpoison(); + fSize = n; + this->poison(); + } + + // We disable Control-Flow Integrity sanitization (go/cfi) when casting item-array buffers. + // CFI flags this code as dangerous because we are casting `buffer` to a T* while the buffer's + // contents might still be uninitialized memory. When T has a vtable, this is especially risky + // because we could hypothetically access a virtual method on fItemArray and jump to an + // 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. + SK_CLANG_NO_SANITIZE("cfi") + static T* TCast(void* buffer) { + return (T*)buffer; + } + + static size_t Bytes(int n) { + SkASSERT(n <= kMaxCapacity); + return SkToSizeT(n) * sizeof(T); + } + + static SkSpan Allocate(int capacity, double growthFactor = 1.0) { + return SkContainerAllocator{sizeof(T), kMaxCapacity}.allocate(capacity, growthFactor); + } + + void initData(int count) { + this->setDataFromBytes(Allocate(count)); + this->changeSize(count); + } + + void destroyAll() { + if (!this->empty()) { + T* cursor = this->begin(); + T* const end = this->end(); + do { + cursor->~T(); + cursor++; + } while (cursor < end); + } + } + + /** In the following move and copy methods, 'dst' is assumed to be uninitialized raw storage. + * In the following move methods, 'src' is destroyed leaving behind uninitialized raw storage. + */ + void copy(const T* src) { + if constexpr (std::is_trivially_copyable_v) { + if (!this->empty() && src != nullptr) { + sk_careful_memcpy(fData, src, this->size_bytes()); + } + } else { + for (int i = 0; i < this->size(); ++i) { + new (fData + i) T(src[i]); + } + } + } + + void move(int dst, int src) { + if constexpr (MEM_MOVE) { + memcpy(static_cast(&fData[dst]), + static_cast(&fData[src]), + sizeof(T)); + } else { + new (&fData[dst]) T(std::move(fData[src])); + fData[src].~T(); + } + } + + void move(void* dst) { + if constexpr (MEM_MOVE) { + sk_careful_memcpy(dst, fData, Bytes(fSize)); + } else { + for (int i = 0; i < this->size(); ++i) { + new (static_cast(dst) + Bytes(i)) T(std::move(fData[i])); + fData[i].~T(); + } + } + } + + // Helper function that makes space for n objects, adjusts the count, but does not initialize + // the new objects. + void* push_back_raw(int n) { + this->checkRealloc(n, kGrowing); + void* ptr = fData + fSize; + this->changeSize(fSize + n); + return ptr; + } + + template + SK_ALWAYS_INLINE T* growAndConstructAtEnd(Args&&... args) { + SkSpan buffer = this->preallocateNewData(/*delta=*/1, kGrowing); + T* newT = new (TCast(buffer.data()) + fSize) T(std::forward(args)...); + this->installDataAndUpdateCapacity(buffer); + + return newT; + } + + void checkRealloc(int delta, double growthFactor) { + SkASSERT(delta >= 0); + SkASSERT(fSize >= 0); + SkASSERT(fCapacity >= 0); + + // Check if there are enough remaining allocated elements to satisfy the request. + if (this->capacity() - fSize < delta) { + // Looks like we need to reallocate. + this->installDataAndUpdateCapacity(this->preallocateNewData(delta, growthFactor)); + } + } + + SkSpan preallocateNewData(int delta, double growthFactor) { + SkASSERT(delta >= 0); + SkASSERT(fSize >= 0); + SkASSERT(fCapacity >= 0); + + // Don't overflow fSize or size_t later in the memory allocation. Overflowing memory + // allocation really only applies to fSizes on 32-bit machines; on 64-bit machines this + // will probably never produce a check. Since kMaxCapacity is bounded above by INT_MAX, + // this also checks the bounds of fSize. + if (delta > kMaxCapacity - fSize) { + sk_report_container_overflow_and_die(); + } + const int newCount = fSize + delta; + + return Allocate(newCount, growthFactor); + } + + void installDataAndUpdateCapacity(SkSpan allocation) { + this->move(TCast(allocation.data())); + if (fOwnMemory) { + sk_free(fData); + } + this->setDataFromBytes(allocation); + SkASSERT(fData != nullptr); + } + + T* fData{nullptr}; + int fSize{0}; + uint32_t fOwnMemory : 1; + uint32_t fCapacity : 31; +}; + +template static inline void swap(TArray& a, TArray& b) { + a.swap(b); +} + +// Subclass of TArray that contains a pre-allocated memory block for the array. +template > +class STArray : private SkAlignedSTStorage(Nreq), T>, + public TArray { + // We round up the requested array size to the next capacity multiple. + // This space would likely otherwise go to waste. + static constexpr int N = SkContainerAllocator::RoundUp(Nreq); + static_assert(Nreq > 0); + static_assert(N >= Nreq); + + using Storage = SkAlignedSTStorage; + +public: + STArray() + : Storage{} + , TArray(this) {} // Must use () to avoid confusion with initializer_list + // when T=bool because * are convertable to bool. + + STArray(const T* array, int count) + : Storage{} + , TArray{array, count, this} {} + + STArray(std::initializer_list data) + : STArray{data.begin(), SkToInt(data.size())} {} + + explicit STArray(int reserveCount) + : STArray() { this->reserve_exact(reserveCount); } + + STArray(const STArray& that) + : STArray() { *this = that; } + + explicit STArray(const TArray& that) + : STArray() { *this = that; } + + STArray(STArray&& that) + : STArray() { *this = std::move(that); } + + explicit STArray(TArray&& that) + : STArray() { *this = std::move(that); } + + STArray& operator=(const STArray& that) { + TArray::operator=(that); + return *this; + } + + STArray& operator=(const TArray& that) { + TArray::operator=(that); + return *this; + } + + STArray& operator=(STArray&& that) { + TArray::operator=(std::move(that)); + return *this; + } + + STArray& operator=(TArray&& that) { + TArray::operator=(std::move(that)); + return *this; + } + + // Force the use of TArray for data() and size(). + using TArray::data; + using TArray::size; +}; +} // namespace skia_private +#endif // SkTArray_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTDArray.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTDArray.h new file mode 100644 index 0000000000..fef454bdc4 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTDArray.h @@ -0,0 +1,235 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkTDArray_DEFINED +#define SkTDArray_DEFINED + +#include "include/private/base/SkAPI.h" +#include "include/private/base/SkAssert.h" +#include "include/private/base/SkDebug.h" +#include "include/private/base/SkTo.h" + +#include +#include +#include + +class SK_SPI SkTDStorage { +public: + explicit SkTDStorage(int sizeOfT); + SkTDStorage(const void* src, int size, int sizeOfT); + + // Copy + SkTDStorage(const SkTDStorage& that); + SkTDStorage& operator= (const SkTDStorage& that); + + // Move + SkTDStorage(SkTDStorage&& that); + SkTDStorage& operator= (SkTDStorage&& that); + + ~SkTDStorage(); + + void reset(); + void swap(SkTDStorage& that); + + // Size routines + bool empty() const { return fSize == 0; } + void clear() { fSize = 0; } + int size() const { return fSize; } + void resize(int newSize); + size_t size_bytes() const { return this->bytes(fSize); } + + // Capacity routines + int capacity() const { return fCapacity; } + void reserve(int newCapacity); + void shrink_to_fit(); + + void* data() { return fStorage; } + const void* data() const { return fStorage; } + + // Deletion routines + void erase(int index, int count); + // Removes the entry at 'index' and replaces it with the last array element + void removeShuffle(int index); + + // Insertion routines + void* prepend(); + + void append(); + void append(int count); + void* append(const void* src, int count); + + void* insert(int index); + void* insert(int index, int count, const void* src); + + void pop_back() { + SkASSERT(fSize > 0); + fSize--; + } + + friend bool operator==(const SkTDStorage& a, const SkTDStorage& b); + friend bool operator!=(const SkTDStorage& a, const SkTDStorage& b) { + return !(a == b); + } + +private: + size_t bytes(int n) const { return SkToSizeT(n * fSizeOfT); } + void* address(int n) { return fStorage + this->bytes(n); } + + // Adds delta to fSize. Crash if outside [0, INT_MAX] + int calculateSizeOrDie(int delta); + + // Move the tail of the array defined by the indexes tailStart and tailEnd to dstIndex. The + // elements at dstIndex are overwritten by the tail. + void moveTail(int dstIndex, int tailStart, int tailEnd); + + // Copy src into the array at dstIndex. + void copySrc(int dstIndex, const void* src, int count); + + const int fSizeOfT; + std::byte* fStorage{nullptr}; + int fCapacity{0}; // size of the allocation in fArray (#elements) + int fSize{0}; // logical number of elements (fSize <= fCapacity) +}; + +static inline void swap(SkTDStorage& a, SkTDStorage& b) { + a.swap(b); +} + +// SkTDArray implements a std::vector-like array for raw data-only objects that do not require +// construction or destruction. The constructor and destructor for T will not be called; T objects +// will always be moved via raw memcpy. Newly created T objects will contain uninitialized memory. +template class SkTDArray { +public: + SkTDArray() : fStorage{sizeof(T)} {} + SkTDArray(const T src[], int count) : fStorage{src, count, sizeof(T)} { } + SkTDArray(const std::initializer_list& list) : SkTDArray(list.begin(), list.size()) {} + + // Copy + SkTDArray(const SkTDArray& src) : SkTDArray(src.data(), src.size()) {} + SkTDArray& operator=(const SkTDArray& src) { + fStorage = src.fStorage; + return *this; + } + + // Move + SkTDArray(SkTDArray&& src) : fStorage{std::move(src.fStorage)} {} + SkTDArray& operator=(SkTDArray&& src) { + fStorage = std::move(src.fStorage); + return *this; + } + + friend bool operator==(const SkTDArray& a, const SkTDArray& b) { + return a.fStorage == b.fStorage; + } + friend bool operator!=(const SkTDArray& a, const SkTDArray& b) { return !(a == b); } + + void swap(SkTDArray& that) { + using std::swap; + swap(fStorage, that.fStorage); + } + + bool empty() const { return fStorage.empty(); } + + // Return the number of elements in the array + int size() const { return fStorage.size(); } + + // Return the total number of elements allocated. + // Note: capacity() - size() gives you the number of elements you can add without causing an + // allocation. + int capacity() const { return fStorage.capacity(); } + + // return the number of bytes in the array: count * sizeof(T) + size_t size_bytes() const { return fStorage.size_bytes(); } + + T* data() { return static_cast(fStorage.data()); } + const T* data() const { return static_cast(fStorage.data()); } + T* begin() { return this->data(); } + const T* begin() const { return this->data(); } + T* end() { return this->data() + this->size(); } + const T* end() const { return this->data() + this->size(); } + + T& operator[](int index) { + return this->data()[sk_collection_check_bounds(index, this->size())]; + } + const T& operator[](int index) const { + return this->data()[sk_collection_check_bounds(index, this->size())]; + } + + const T& back() const { + sk_collection_not_empty(this->empty()); + return this->data()[this->size() - 1]; + } + T& back() { + sk_collection_not_empty(this->empty()); + return this->data()[this->size() - 1]; + } + + void reset() { + fStorage.reset(); + } + + void clear() { + fStorage.clear(); + } + + // Sets the number of elements in the array. + // If the array does not have space for count elements, it will increase + // the storage allocated to some amount greater than that required. + // It will never shrink the storage. + void resize(int count) { + fStorage.resize(count); + } + + void reserve(int n) { + fStorage.reserve(n); + } + + T* append() { + fStorage.append(); + return this->end() - 1; + } + T* append(int count) { + fStorage.append(count); + return this->end() - count; + } + T* append(int count, const T* src) { + return static_cast(fStorage.append(src, count)); + } + + T* insert(int index) { + return static_cast(fStorage.insert(index)); + } + T* insert(int index, int count, const T* src = nullptr) { + return static_cast(fStorage.insert(index, count, src)); + } + + void remove(int index, int count = 1) { + fStorage.erase(index, count); + } + + void removeShuffle(int index) { + fStorage.removeShuffle(index); + } + + // routines to treat the array like a stack + void push_back(const T& v) { + this->append(); + this->back() = v; + } + void pop_back() { fStorage.pop_back(); } + + void shrink_to_fit() { + fStorage.shrink_to_fit(); + } + +private: + SkTDStorage fStorage; +}; + +template static inline void swap(SkTDArray& a, SkTDArray& b) { a.swap(b); } + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTFitsIn.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTFitsIn.h new file mode 100644 index 0000000000..365748abef --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTFitsIn.h @@ -0,0 +1,105 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkTFitsIn_DEFINED +#define SkTFitsIn_DEFINED + +#include "include/private/base/SkDebug.h" + +#include +#include +#include + +/** + * std::underlying_type is only defined for enums. For integral types, we just want the type. + */ +template +struct sk_strip_enum { + typedef T type; +}; + +template +struct sk_strip_enum::value>::type> { + typedef typename std::underlying_type::type type; +}; + + +/** + * In C++ an unsigned to signed cast where the source value cannot be represented in the destination + * type results in an implementation defined destination value. Unlike C, C++ does not allow a trap. + * This makes "(S)(D)s == s" a possibly useful test. However, there are two cases where this is + * incorrect: + * + * when testing if a value of a smaller signed type can be represented in a larger unsigned type + * (int8_t)(uint16_t)-1 == -1 => (int8_t)0xFFFF == -1 => [implementation defined] == -1 + * + * when testing if a value of a larger unsigned type can be represented in a smaller signed type + * (uint16_t)(int8_t)0xFFFF == 0xFFFF => (uint16_t)-1 == 0xFFFF => 0xFFFF == 0xFFFF => true. + * + * Consider the cases: + * u = unsigned, less digits + * U = unsigned, more digits + * s = signed, less digits + * S = signed, more digits + * v is the value we're considering. + * + * u -> U: (u)(U)v == v, trivially true + * U -> u: (U)(u)v == v, both casts well defined, test works + * s -> S: (s)(S)v == v, trivially true + * S -> s: (S)(s)v == v, first cast implementation value, second cast defined, test works + * s -> U: (s)(U)v == v, *this is bad*, the second cast results in implementation defined value + * S -> u: (S)(u)v == v, the second cast is required to prevent promotion of rhs to unsigned + * u -> S: (u)(S)v == v, trivially true + * U -> s: (U)(s)v == v, *this is bad*, + * first cast results in implementation defined value, + * second cast is defined. However, this creates false positives + * uint16_t x = 0xFFFF + * (uint16_t)(int8_t)x == x + * => (uint16_t)-1 == x + * => 0xFFFF == x + * => true + * + * So for the eight cases three are trivially true, three more are valid casts, and two are special. + * The two 'full' checks which otherwise require two comparisons are valid cast checks. + * The two remaining checks s -> U [v >= 0] and U -> s [v <= max(s)] can be done with one op. + */ + +template +static constexpr inline +typename std::enable_if<(std::is_integral::value || std::is_enum::value) && + (std::is_integral::value || std::is_enum::value), bool>::type +/*bool*/ SkTFitsIn(S src) { + // Ensure that is_signed and is_unsigned are passed the arithmetic underlyng types of enums. + using Sa = typename sk_strip_enum::type; + using Da = typename sk_strip_enum::type; + + // SkTFitsIn() is used in public headers, so needs to be written targeting at most C++11. + return + + // E.g. (int8_t)(uint8_t) int8_t(-1) == -1, but the uint8_t == 255, not -1. + (std::is_signed::value && std::is_unsigned::value && sizeof(Sa) <= sizeof(Da)) ? + (S)0 <= src : + + // E.g. (uint8_t)(int8_t) uint8_t(255) == 255, but the int8_t == -1. + (std::is_signed::value && std::is_unsigned::value && sizeof(Da) <= sizeof(Sa)) ? + src <= (S)std::numeric_limits::max() : + +#if !defined(SK_DEBUG) && !defined(__MSVC_RUNTIME_CHECKS ) + // Correct (simple) version. This trips up MSVC's /RTCc run-time checking. + (S)(D)src == src; +#else + // More complex version that's safe with /RTCc. Used in all debug builds, for coverage. + (std::is_signed::value) ? + (intmax_t)src >= (intmax_t)std::numeric_limits::min() && + (intmax_t)src <= (intmax_t)std::numeric_limits::max() : + + // std::is_unsigned ? + (uintmax_t)src <= (uintmax_t)std::numeric_limits::max(); +#endif +} + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTLogic.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTLogic.h new file mode 100644 index 0000000000..26f363c946 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTLogic.h @@ -0,0 +1,56 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * + * This header provides some std:: features early in the skstd namespace + * and several Skia-specific additions in the sknonstd namespace. + */ + +#ifndef SkTLogic_DEFINED +#define SkTLogic_DEFINED + +#include +#include +#include "include/private/base/SkTo.h" + +// The sknonstd namespace contains things we would like to be proposed and feel std-ish. +namespace sknonstd { + +// The name 'copy' here is fraught with peril. In this case it means 'append', not 'overwrite'. +// Alternate proposed names are 'propagate', 'augment', or 'append' (and 'add', but already taken). +// std::experimental::propagate_const already exists for other purposes in TSv2. +// These also follow the pattern used by boost. +template struct copy_const { + using type = std::conditional_t::value, std::add_const_t, D>; +}; +template using copy_const_t = typename copy_const::type; + +template struct copy_volatile { + using type = std::conditional_t::value, std::add_volatile_t, D>; +}; +template using copy_volatile_t = typename copy_volatile::type; + +template struct copy_cv { + using type = copy_volatile_t, S>; +}; +template using copy_cv_t = typename copy_cv::type; + +// The name 'same' here means 'overwrite'. +// Alternate proposed names are 'replace', 'transfer', or 'qualify_from'. +// same_xxx can be written as copy_xxx, S> +template using same_const = copy_const, S>; +template using same_const_t = typename same_const::type; +template using same_volatile =copy_volatile,S>; +template using same_volatile_t = typename same_volatile::type; +template using same_cv = copy_cv, S>; +template using same_cv_t = typename same_cv::type; + +} // namespace sknonstd + +template +constexpr int SkCount(const Container& c) { return SkTo(std::size(c)); } + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTPin.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTPin.h new file mode 100644 index 0000000000..c824c44640 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTPin.h @@ -0,0 +1,23 @@ +/* + * Copyright 2020 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkTPin_DEFINED +#define SkTPin_DEFINED + +#include + +/** @return x pinned (clamped) between lo and hi, inclusively. + + Unlike std::clamp(), SkTPin() always returns a value between lo and hi. + If x is NaN, SkTPin() returns lo but std::clamp() returns NaN. +*/ +template +static constexpr const T& SkTPin(const T& x, const T& lo, const T& hi) { + return std::max(lo, std::min(x, hi)); +} + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTemplates.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTemplates.h new file mode 100644 index 0000000000..3c9ca55125 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTemplates.h @@ -0,0 +1,446 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkTemplates_DEFINED +#define SkTemplates_DEFINED + +#include "include/private/base/SkAlign.h" +#include "include/private/base/SkAssert.h" +#include "include/private/base/SkDebug.h" +#include "include/private/base/SkMalloc.h" +#include "include/private/base/SkTLogic.h" +#include "include/private/base/SkTo.h" + +#include +#include +#include +#include +#include +#include +#include + + +/** \file SkTemplates.h + + This file contains light-weight template classes for type-safe and exception-safe + resource management. +*/ + +/** + * Marks a local variable as known to be unused (to avoid warnings). + * Note that this does *not* prevent the local variable from being optimized away. + */ +template inline void sk_ignore_unused_variable(const T&) { } + +/** + * This is a general purpose absolute-value function. + * See SkAbs32 in (SkSafe32.h) for a 32-bit int specific version that asserts. + */ +template static inline T SkTAbs(T value) { + if (value < 0) { + value = -value; + } + return value; +} + +/** + * Returns a pointer to a D which comes immediately after S[count]. + */ +template inline D* SkTAfter(S* ptr, size_t count = 1) { + return reinterpret_cast(ptr + count); +} + +/** + * Returns a pointer to a D which comes byteOffset bytes after S. + */ +template inline D* SkTAddOffset(S* ptr, ptrdiff_t byteOffset) { + // The intermediate char* has the same cv-ness as D as this produces better error messages. + // This relies on the fact that reinterpret_cast can add constness, but cannot remove it. + return reinterpret_cast(reinterpret_cast*>(ptr) + byteOffset); +} + +template struct SkOverloadedFunctionObject { + template + auto operator()(Args&&... args) const -> decltype(P(std::forward(args)...)) { + return P(std::forward(args)...); + } +}; + +template using SkFunctionObject = + SkOverloadedFunctionObject, F>; + +/** \class SkAutoTCallVProc + + Call a function when this goes out of scope. The template uses two + parameters, the object, and a function that is to be called in the destructor. + If release() is called, the object reference is set to null. If the object + reference is null when the destructor is called, we do not call the + function. +*/ +template class SkAutoTCallVProc + : public std::unique_ptr> { + using inherited = std::unique_ptr>; +public: + using inherited::inherited; + SkAutoTCallVProc(const SkAutoTCallVProc&) = delete; + SkAutoTCallVProc(SkAutoTCallVProc&& that) : inherited(std::move(that)) {} + + operator T*() const { return this->get(); } +}; + + +namespace skia_private { +/** Allocate an array of T elements, and free the array in the destructor + */ +template class AutoTArray { +public: + AutoTArray() {} + // Allocate size number of T elements + explicit AutoTArray(size_t size) { + fSize = check_size_bytes_too_big(size); + fData.reset(size > 0 ? new T[size] : nullptr); + } + + // TODO: remove when all uses are gone. + explicit AutoTArray(int size) : AutoTArray(SkToSizeT(size)) {} + + AutoTArray(AutoTArray&& other) : fData(std::move(other.fData)) { + fSize = std::exchange(other.fSize, 0); + } + AutoTArray& operator=(AutoTArray&& other) { + if (this != &other) { + fData = std::move(other.fData); + fSize = std::exchange(other.fSize, 0); + } + return *this; + } + + // Reallocates given a new count. Reallocation occurs even if new count equals old count. + void reset(size_t count = 0) { + *this = AutoTArray(count); + } + + T* get() const { return fData.get(); } + + T& operator[](size_t index) const { + return fData[sk_collection_check_bounds(index, fSize)]; + } + + const T* data() const { return fData.get(); } + T* data() { return fData.get(); } + + size_t size() const { return fSize; } + bool empty() const { return fSize == 0; } + size_t size_bytes() const { return sizeof(T) * fSize; } + + T* begin() { + return fData; + } + const T* begin() const { + return fData; + } + + // It's safe to use fItemArray + fSize because if fItemArray is nullptr then adding 0 is + // valid and returns nullptr. See [expr.add] in the C++ standard. + T* end() { + if (fData == nullptr) { + SkASSERT(fSize == 0); + } + return fData + fSize; + } + const T* end() const { + if (fData == nullptr) { + SkASSERT(fSize == 0); + } + return fData + fSize; + } + +private: + std::unique_ptr fData; + size_t fSize = 0; +}; + +/** Wraps AutoTArray, with room for kCountRequested elements preallocated. + */ +template class AutoSTArray { +public: + AutoSTArray(AutoSTArray&&) = delete; + AutoSTArray(const AutoSTArray&) = delete; + AutoSTArray& operator=(AutoSTArray&&) = delete; + AutoSTArray& operator=(const AutoSTArray&) = delete; + + /** Initialize with no objects */ + AutoSTArray() { + fArray = nullptr; + fCount = 0; + } + + /** Allocate count number of T elements + */ + AutoSTArray(int count) { + fArray = nullptr; + fCount = 0; + this->reset(count); + } + + ~AutoSTArray() { + this->reset(0); + } + + /** Destroys previous objects in the array and default constructs count number of objects */ + void reset(int count) { + T* start = fArray; + T* iter = start + fCount; + while (iter > start) { + (--iter)->~T(); + } + + SkASSERT(count >= 0); + if (fCount != count) { + if (fCount > kCount) { + // 'fArray' was allocated last time so free it now + SkASSERT((T*) fStorage != fArray); + sk_free(fArray); + } + + if (count > kCount) { + fArray = (T*) sk_malloc_throw(count, sizeof(T)); + } else if (count > 0) { + fArray = (T*) fStorage; + } else { + fArray = nullptr; + } + + fCount = count; + } + + iter = fArray; + T* stop = fArray + count; + while (iter < stop) { + new (iter++) T; + } + } + + /** Return the number of T elements in the array + */ + int count() const { return fCount; } + + /** Return the array of T elements. Will be NULL if count == 0 + */ + T* get() const { return fArray; } + + T* begin() { return fArray; } + + const T* begin() const { return fArray; } + + T* end() { return fArray + fCount; } + + const T* end() const { return fArray + fCount; } + + /** Return the nth element in the array + */ + T& operator[](int index) const { + return fArray[sk_collection_check_bounds(index, fCount)]; + } + + /** Aliases matching other types, like std::vector. */ + const T* data() const { return fArray; } + T* data() { return fArray; } + size_t size() const { return fCount; } + +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 + ? kMaxBytes / sizeof(T) + : kCountRequested; +#else + static const int kCount = kCountRequested; +#endif + + int fCount; + T* fArray; + alignas(T) char fStorage[kCount * sizeof(T)]; +}; + +/** Manages an array of T elements, freeing the array in the destructor. + * Does NOT call any constructors/destructors on T (T must be POD). + */ +template ::value && + std::is_trivially_destructible::value>> +class AutoTMalloc { +public: + /** Takes ownership of the ptr. The ptr must be a value which can be passed to sk_free. */ + explicit AutoTMalloc(T* ptr = nullptr) : fPtr(ptr) {} + + /** Allocates space for 'count' Ts. */ + explicit AutoTMalloc(size_t count) + : fPtr(count ? (T*)sk_malloc_throw(count, sizeof(T)) : nullptr) {} + + AutoTMalloc(AutoTMalloc&&) = default; + AutoTMalloc& operator=(AutoTMalloc&&) = default; + + /** Resize the memory area pointed to by the current ptr preserving contents. */ + void realloc(size_t count) { + fPtr.reset(count ? (T*)sk_realloc_throw(fPtr.release(), count * sizeof(T)) : nullptr); + } + + /** Resize the memory area pointed to by the current ptr without preserving contents. */ + T* reset(size_t count = 0) { + fPtr.reset(count ? (T*)sk_malloc_throw(count, sizeof(T)) : nullptr); + return this->get(); + } + + T* get() const { return fPtr.get(); } + + operator T*() { return fPtr.get(); } + + operator const T*() const { return fPtr.get(); } + + T& operator[](int index) { return fPtr.get()[index]; } + + const T& operator[](int index) const { return fPtr.get()[index]; } + + /** Aliases matching other types, like std::vector. */ + const T* data() const { return fPtr.get(); } + T* data() { return fPtr.get(); } + + /** + * Transfer ownership of the ptr to the caller, setting the internal + * pointer to NULL. Note that this differs from get(), which also returns + * the pointer, but it does not transfer ownership. + */ + T* release() { return fPtr.release(); } + +private: + std::unique_ptr> fPtr; +}; + +template ::value && + std::is_trivially_destructible::value>> +class AutoSTMalloc { +public: + AutoSTMalloc() : fPtr(fTStorage) {} + + AutoSTMalloc(size_t count) { + if (count > kCount) { + fPtr = (T*)sk_malloc_throw(count, sizeof(T)); + } else if (count) { + fPtr = fTStorage; + } else { + fPtr = nullptr; + } + } + + AutoSTMalloc(AutoSTMalloc&&) = delete; + AutoSTMalloc(const AutoSTMalloc&) = delete; + AutoSTMalloc& operator=(AutoSTMalloc&&) = delete; + AutoSTMalloc& operator=(const AutoSTMalloc&) = delete; + + ~AutoSTMalloc() { + if (fPtr != fTStorage) { + sk_free(fPtr); + } + } + + // doesn't preserve contents + T* reset(size_t count) { + if (fPtr != fTStorage) { + sk_free(fPtr); + } + if (count > kCount) { + fPtr = (T*)sk_malloc_throw(count, sizeof(T)); + } else if (count) { + fPtr = fTStorage; + } else { + fPtr = nullptr; + } + return fPtr; + } + + T* get() const { return fPtr; } + + operator T*() { + return fPtr; + } + + operator const T*() const { + return fPtr; + } + + T& operator[](int index) { + return fPtr[index]; + } + + const T& operator[](int index) const { + return fPtr[index]; + } + + /** Aliases matching other types, like std::vector. */ + const T* data() const { return fPtr; } + T* data() { return fPtr; } + + // Reallocs the array, can be used to shrink the allocation. Makes no attempt to be intelligent + void realloc(size_t count) { + if (count > kCount) { + if (fPtr == fTStorage) { + fPtr = (T*)sk_malloc_throw(count, sizeof(T)); + memcpy((void*)fPtr, fTStorage, kCount * sizeof(T)); + } else { + fPtr = (T*)sk_realloc_throw(fPtr, count, sizeof(T)); + } + } else if (count) { + if (fPtr != fTStorage) { + fPtr = (T*)sk_realloc_throw(fPtr, count, sizeof(T)); + } + } else { + this->reset(0); + } + } + +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); +#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 + ? kMaxBytes / sizeof(T) + : kCountWithPadding; +#else + static const size_t kCount = kCountWithPadding; +#endif + + T* fPtr; + union { + uint32_t fStorage32[SkAlign4(kCount*sizeof(T)) >> 2]; + T fTStorage[1]; // do NOT want to invoke T::T() + }; +}; + +using UniqueVoidPtr = std::unique_ptr>; + +} // namespace skia_private + +template +constexpr auto SkMakeArrayFromIndexSequence(C c, std::index_sequence is) +-> std::array())), sizeof...(Is)> { + return {{ c(Is)... }}; +} + +template constexpr auto SkMakeArray(C c) +-> std::array::value_type>())), N> { + return SkMakeArrayFromIndexSequence(c, std::make_index_sequence{}); +} + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkThreadAnnotations.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkThreadAnnotations.h new file mode 100644 index 0000000000..a67fbaaca0 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkThreadAnnotations.h @@ -0,0 +1,104 @@ +/* + * Copyright 2019 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkThreadAnnotations_DEFINED +#define SkThreadAnnotations_DEFINED + +#include "include/private/base/SkFeatures.h" // IWYU pragma: keep + +// The bulk of this code is cribbed from: +// http://clang.llvm.org/docs/ThreadSafetyAnalysis.html + +#if defined(__clang__) && (!defined(SWIG)) +#define SK_THREAD_ANNOTATION_ATTRIBUTE(x) __attribute__((x)) +#else +#define SK_THREAD_ANNOTATION_ATTRIBUTE(x) // no-op +#endif + +#define SK_CAPABILITY(x) \ + SK_THREAD_ANNOTATION_ATTRIBUTE(capability(x)) + +#define SK_SCOPED_CAPABILITY \ + SK_THREAD_ANNOTATION_ATTRIBUTE(scoped_lockable) + +#define SK_GUARDED_BY(x) \ + SK_THREAD_ANNOTATION_ATTRIBUTE(guarded_by(x)) + +#define SK_PT_GUARDED_BY(x) \ + SK_THREAD_ANNOTATION_ATTRIBUTE(pt_guarded_by(x)) + +#define SK_ACQUIRED_BEFORE(...) \ + SK_THREAD_ANNOTATION_ATTRIBUTE(acquired_before(__VA_ARGS__)) + +#define SK_ACQUIRED_AFTER(...) \ + SK_THREAD_ANNOTATION_ATTRIBUTE(acquired_after(__VA_ARGS__)) + +#define SK_REQUIRES(...) \ + SK_THREAD_ANNOTATION_ATTRIBUTE(requires_capability(__VA_ARGS__)) + +#define SK_REQUIRES_SHARED(...) \ + SK_THREAD_ANNOTATION_ATTRIBUTE(requires_shared_capability(__VA_ARGS__)) + +#define SK_ACQUIRE(...) \ + SK_THREAD_ANNOTATION_ATTRIBUTE(acquire_capability(__VA_ARGS__)) + +#define SK_ACQUIRE_SHARED(...) \ + SK_THREAD_ANNOTATION_ATTRIBUTE(acquire_shared_capability(__VA_ARGS__)) + +// Would be SK_RELEASE, but that is already in use as SK_DEBUG vs. SK_RELEASE. +#define SK_RELEASE_CAPABILITY(...) \ + SK_THREAD_ANNOTATION_ATTRIBUTE(release_capability(__VA_ARGS__)) + +// For symmetry with SK_RELEASE_CAPABILITY. +#define SK_RELEASE_SHARED_CAPABILITY(...) \ + SK_THREAD_ANNOTATION_ATTRIBUTE(release_shared_capability(__VA_ARGS__)) + +#define SK_TRY_ACQUIRE(...) \ + SK_THREAD_ANNOTATION_ATTRIBUTE(try_acquire_capability(__VA_ARGS__)) + +#define SK_TRY_ACQUIRE_SHARED(...) \ + SK_THREAD_ANNOTATION_ATTRIBUTE(try_acquire_shared_capability(__VA_ARGS__)) + +#define SK_EXCLUDES(...) \ + SK_THREAD_ANNOTATION_ATTRIBUTE(locks_excluded(__VA_ARGS__)) + +#define SK_ASSERT_CAPABILITY(x) \ + SK_THREAD_ANNOTATION_ATTRIBUTE(assert_capability(x)) + +#define SK_ASSERT_SHARED_CAPABILITY(x) \ + SK_THREAD_ANNOTATION_ATTRIBUTE(assert_shared_capability(x)) + +#define SK_RETURN_CAPABILITY(x) \ + SK_THREAD_ANNOTATION_ATTRIBUTE(lock_returned(x)) + +#define SK_NO_THREAD_SAFETY_ANALYSIS \ + SK_THREAD_ANNOTATION_ATTRIBUTE(no_thread_safety_analysis) + +#if defined(SK_BUILD_FOR_GOOGLE3) && !defined(SK_BUILD_FOR_WASM_IN_GOOGLE3) \ + && !defined(SK_BUILD_FOR_WIN) + extern "C" { + void __google_cxa_guard_acquire_begin(void) __attribute__((weak)); + void __google_cxa_guard_acquire_end (void) __attribute__((weak)); + } + static inline void sk_potentially_blocking_region_begin() { + if (&__google_cxa_guard_acquire_begin) { + __google_cxa_guard_acquire_begin(); + } + } + static inline void sk_potentially_blocking_region_end() { + if (&__google_cxa_guard_acquire_end) { + __google_cxa_guard_acquire_end(); + } + } + #define SK_POTENTIALLY_BLOCKING_REGION_BEGIN sk_potentially_blocking_region_begin() + #define SK_POTENTIALLY_BLOCKING_REGION_END sk_potentially_blocking_region_end() +#else + #define SK_POTENTIALLY_BLOCKING_REGION_BEGIN + #define SK_POTENTIALLY_BLOCKING_REGION_END +#endif + +#endif // SkThreadAnnotations_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkThreadID.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkThreadID.h new file mode 100644 index 0000000000..18984884c9 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkThreadID.h @@ -0,0 +1,23 @@ +/* + * 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 SkThreadID_DEFINED +#define SkThreadID_DEFINED + +#include "include/private/base/SkAPI.h" +#include "include/private/base/SkDebug.h" + +#include + +typedef int64_t SkThreadID; + +// SkMutex.h uses SkGetThreadID in debug only code. +SkDEBUGCODE(SK_SPI) SkThreadID SkGetThreadID(); + +const SkThreadID kIllegalThreadID = 0; + +#endif // SkThreadID_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTo.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTo.h new file mode 100644 index 0000000000..51ccafeeaf --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTo.h @@ -0,0 +1,39 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SkTo_DEFINED +#define SkTo_DEFINED + +#include "include/private/base/SkAssert.h" +#include "include/private/base/SkTFitsIn.h" + +#include +#include + +template constexpr D SkTo(S s) { + return SkASSERT(SkTFitsIn(s)), + static_cast(s); +} + +template constexpr int8_t SkToS8(S x) { return SkTo(x); } +template constexpr uint8_t SkToU8(S x) { return SkTo(x); } +template constexpr int16_t SkToS16(S x) { return SkTo(x); } +template constexpr uint16_t SkToU16(S x) { return SkTo(x); } +template constexpr int32_t SkToS32(S x) { return SkTo(x); } +template constexpr uint32_t SkToU32(S x) { return SkTo(x); } +template constexpr int64_t SkToS64(S x) { return SkTo(x); } +template constexpr uint64_t SkToU64(S x) { return SkTo(x); } +template constexpr int SkToInt(S x) { return SkTo(x); } +template constexpr unsigned SkToUInt(S x) { return SkTo(x); } +template constexpr size_t SkToSizeT(S x) { return SkTo(x); } + +/** @return false or true based on the condition +*/ +template static constexpr bool SkToBool(const T& x) { + return (bool)x; +} + +#endif // SkTo_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTypeTraits.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTypeTraits.h new file mode 100644 index 0000000000..736f789776 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/base/SkTypeTraits.h @@ -0,0 +1,33 @@ +// 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 SkTypeTraits_DEFINED +#define SkTypeTraits_DEFINED + +#include +#include + +// Trait for identifying types which are relocatable via memcpy, for container optimizations. +template +struct sk_has_trivially_relocatable_member : std::false_type {}; + +// Types can declare themselves trivially relocatable with a public +// using sk_is_trivially_relocatable = std::true_type; +template +struct sk_has_trivially_relocatable_member> + : T::sk_is_trivially_relocatable {}; + +// By default, all trivially copyable types are trivially relocatable. +template +struct sk_is_trivially_relocatable + : std::disjunction, sk_has_trivially_relocatable_member>{}; + +// Here be some dragons: while technically not guaranteed, we count on all sane unique_ptr +// implementations to be trivially relocatable. +template +struct sk_is_trivially_relocatable> : std::true_type {}; + +template +inline constexpr bool sk_is_trivially_relocatable_v = sk_is_trivially_relocatable::value; + +#endif // SkTypeTraits_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/GrDeferredDisplayList.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/GrDeferredDisplayList.h new file mode 100644 index 0000000000..e54b51c43b --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/GrDeferredDisplayList.h @@ -0,0 +1,120 @@ +/* + * Copyright 2023 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrDeferredDisplayList_DEFINED +#define GrDeferredDisplayList_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkTypes.h" +#include "include/gpu/GrRecordingContext.h" +#include "include/private/base/SkTArray.h" +#include "include/private/chromium/GrSurfaceCharacterization.h" + +class GrDirectContext; +class GrRenderTargetProxy; +class GrRenderTask; +class GrDeferredDisplayListPriv; +class SkSurface; + +/* + * This class contains pre-processed gpu operations that can be replayed into + * an SkSurface via SkSurface::draw(GrDeferredDisplayList*). + */ +class GrDeferredDisplayList : public SkNVRefCnt { +public: + SK_API ~GrDeferredDisplayList(); + + SK_API const GrSurfaceCharacterization& characterization() const { + return fCharacterization; + } + /** + * Iterate through the programs required by the DDL. + */ + class SK_API ProgramIterator { + public: + ProgramIterator(GrDirectContext*, GrDeferredDisplayList*); + ~ProgramIterator(); + + // This returns true if any work was done. Getting a cache hit does not count as work. + bool compile(); + bool done() const; + void next(); + + private: + GrDirectContext* fDContext; + const skia_private::TArray& fProgramData; + int fIndex; + }; + + // Provides access to functions that aren't part of the public API. + GrDeferredDisplayListPriv priv(); + const GrDeferredDisplayListPriv priv() const; // NOLINT(readability-const-return-type) + +private: + friend class GrDrawingManager; // for access to 'fRenderTasks', 'fLazyProxyData', 'fArenas' + friend class GrDeferredDisplayListRecorder; // for access to 'fLazyProxyData' + friend class GrDeferredDisplayListPriv; + + // This object is the source from which the lazy proxy backing the DDL will pull its backing + // texture when the DDL is replayed. It has to be separately ref counted bc the lazy proxy + // can outlive the DDL. + class LazyProxyData : public SkRefCnt { + public: + // Upon being replayed - this field will be filled in (by the DrawingManager) with the + // proxy backing the destination SkSurface. Note that, since there is no good place to + // clear it, it can become a dangling pointer. Additionally, since the renderTargetProxy + // doesn't get a ref here, the SkSurface that owns it must remain alive until the DDL + // is flushed. + // TODO: the drawing manager could ref the renderTargetProxy for the DDL and then add + // a renderingTask to unref it after the DDL's ops have been executed. + GrRenderTargetProxy* fReplayDest = nullptr; + }; + + SK_API GrDeferredDisplayList(const GrSurfaceCharacterization& characterization, + sk_sp fTargetProxy, + sk_sp); + + const skia_private::TArray& programData() const { + return fProgramData; + } + + const GrSurfaceCharacterization fCharacterization; + + // These are ordered such that the destructor cleans op tasks up first (which may refer back + // to the arena and memory pool in their destructors). + GrRecordingContext::OwnedArenas fArenas; + skia_private::TArray> fRenderTasks; + + skia_private::TArray fProgramData; + sk_sp fTargetProxy; + sk_sp fLazyProxyData; +}; + +namespace skgpu::ganesh { +/** Draws the deferred display list created via a GrDeferredDisplayListRecorder. + If the deferred display list is not compatible with the surface, the draw is skipped + and false is return. + + The xOffset and yOffset parameters are experimental and, if not both zero, will cause + the draw to be ignored. + When implemented, if xOffset or yOffset are non-zero, the DDL will be drawn offset by that + amount into the surface. + + @param SkSurface The surface to apply the commands to, cannot be nullptr. + @param ddl drawing commands, cannot be nullptr. + @return false if ddl is not compatible + + example: https://fiddle.skia.org/c/@Surface_draw_2 +*/ +SK_API bool DrawDDL(SkSurface*, + sk_sp ddl); + +SK_API bool DrawDDL(sk_sp, + sk_sp ddl); +} + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/GrDeferredDisplayListRecorder.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/GrDeferredDisplayListRecorder.h new file mode 100644 index 0000000000..f66cb94d0c --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/GrDeferredDisplayListRecorder.h @@ -0,0 +1,62 @@ +/* + * Copyright 2023 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrDeferredDisplayListRecorder_DEFINED +#define GrDeferredDisplayListRecorder_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkTypes.h" +#include "include/private/chromium/GrDeferredDisplayList.h" +#include "include/private/chromium/GrSurfaceCharacterization.h" + +class GrRecordingContext; +class GrRenderTargetProxy; +class SkCanvas; +class SkSurface; + +/* + * This class is intended to be used as: + * Get a GrSurfaceCharacterization representing the intended gpu-backed destination SkSurface + * Create one of these (a GrDeferredDisplayListRecorder) on the stack + * Get the canvas and render into it + * Snap off and hold on to a GrDeferredDisplayList + * Once your app actually needs the pixels, call skgpu::ganesh::DrawDDL(GrDeferredDisplayList*) + * + * This class never accesses the GPU but performs all the cpu work it can. It + * is thread-safe (i.e., one can break a scene into tiles and perform their cpu-side + * work in parallel ahead of time). + */ +class SK_API GrDeferredDisplayListRecorder { +public: + GrDeferredDisplayListRecorder(const GrSurfaceCharacterization&); + ~GrDeferredDisplayListRecorder(); + + const GrSurfaceCharacterization& characterization() const { + return fCharacterization; + } + + // The backing canvas will become invalid (and this entry point will return + // null) once 'detach' is called. + // Note: ownership of the SkCanvas is not transferred via this call. + SkCanvas* getCanvas(); + + sk_sp detach(); + +private: + GrDeferredDisplayListRecorder(const GrDeferredDisplayListRecorder&) = delete; + GrDeferredDisplayListRecorder& operator=(const GrDeferredDisplayListRecorder&) = delete; + + bool init(); + + const GrSurfaceCharacterization fCharacterization; + sk_sp fContext; + sk_sp fTargetProxy; + sk_sp fLazyProxyData; + sk_sp fSurface; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/GrPromiseImageTexture.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/GrPromiseImageTexture.h new file mode 100644 index 0000000000..0f144c92f1 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/GrPromiseImageTexture.h @@ -0,0 +1,43 @@ +/* + * Copyright 2023 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrPromiseImageTexture_DEFINED +#define GrPromiseImageTexture_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkTypes.h" +#include "include/gpu/GrBackendSurface.h" +/** + * This type is used to fulfill textures for PromiseImages. Once an instance is returned from a + * PromiseImageTextureFulfillProc the GrBackendTexture it wraps must remain valid until the + * corresponding PromiseImageTextureReleaseProc is called. + */ +class SK_API GrPromiseImageTexture : public SkNVRefCnt { +public: + GrPromiseImageTexture() = delete; + GrPromiseImageTexture(const GrPromiseImageTexture&) = delete; + GrPromiseImageTexture(GrPromiseImageTexture&&) = delete; + ~GrPromiseImageTexture(); + GrPromiseImageTexture& operator=(const GrPromiseImageTexture&) = delete; + GrPromiseImageTexture& operator=(GrPromiseImageTexture&&) = delete; + + static sk_sp Make(const GrBackendTexture& backendTexture) { + if (!backendTexture.isValid()) { + return nullptr; + } + return sk_sp(new GrPromiseImageTexture(backendTexture)); + } + + GrBackendTexture backendTexture() const { return fBackendTexture; } + +private: + explicit GrPromiseImageTexture(const GrBackendTexture& backendTexture); + + GrBackendTexture fBackendTexture; +}; + +#endif // GrPromiseImageTexture_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/GrSurfaceCharacterization.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/GrSurfaceCharacterization.h new file mode 100644 index 0000000000..ecf3f92f9d --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/GrSurfaceCharacterization.h @@ -0,0 +1,211 @@ +/* + * Copyright 2023 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrSurfaceCharacterization_DEFINED +#define GrSurfaceCharacterization_DEFINED + +#include "include/core/SkColorSpace.h" // IWYU pragma: keep +#include "include/core/SkColorType.h" +#include "include/core/SkImageInfo.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkSize.h" +#include "include/core/SkSurfaceProps.h" +#include "include/core/SkTypes.h" +#include "include/gpu/GpuTypes.h" +#include "include/gpu/GrBackendSurface.h" +#include "include/gpu/GrContextThreadSafeProxy.h" +#include "include/gpu/GrTypes.h" +#include "include/private/base/SkDebug.h" + +#include +#include + +/** \class GrSurfaceCharacterization + A surface characterization contains all the information Ganesh requires to makes its internal + rendering decisions. When passed into a GrDeferredDisplayListRecorder it will copy the + data and pass it on to the GrDeferredDisplayList if/when it is created. Note that both of + those objects (the Recorder and the DisplayList) will take a ref on the + GrContextThreadSafeProxy and SkColorSpace objects. +*/ +class SK_API GrSurfaceCharacterization { +public: + enum class Textureable : bool { kNo = false, kYes = true }; + enum class UsesGLFBO0 : bool { kNo = false, kYes = true }; + // This flag indicates that the backing VkImage for this Vulkan surface will have the + // VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT set. This bit allows skia to handle advanced blends + // more optimally in a shader by being able to directly read the dst values. + enum class VkRTSupportsInputAttachment : bool { kNo = false, kYes = true }; + // This flag indicates if the surface is wrapping a raw Vulkan secondary command buffer. + enum class VulkanSecondaryCBCompatible : bool { kNo = false, kYes = true }; + + GrSurfaceCharacterization() + : fCacheMaxResourceBytes(0) + , fOrigin(kBottomLeft_GrSurfaceOrigin) + , fSampleCnt(0) + , fIsTextureable(Textureable::kYes) + , fIsMipmapped(skgpu::Mipmapped::kYes) + , fUsesGLFBO0(UsesGLFBO0::kNo) + , fVulkanSecondaryCBCompatible(VulkanSecondaryCBCompatible::kNo) + , fIsProtected(skgpu::Protected::kNo) + , fSurfaceProps() {} + + GrSurfaceCharacterization(GrSurfaceCharacterization&&) = default; + GrSurfaceCharacterization& operator=(GrSurfaceCharacterization&&) = default; + + GrSurfaceCharacterization(const GrSurfaceCharacterization&) = default; + GrSurfaceCharacterization& operator=(const GrSurfaceCharacterization& other) = default; + bool operator==(const GrSurfaceCharacterization& other) const; + bool operator!=(const GrSurfaceCharacterization& other) const { + return !(*this == other); + } + + /* + * Return a new surface characterization with the only difference being a different width + * and height + */ + GrSurfaceCharacterization createResized(int width, int height) const; + + /* + * Return a new surface characterization with only a replaced color space + */ + GrSurfaceCharacterization createColorSpace(sk_sp) const; + + /* + * Return a new surface characterization with the backend format replaced. A colorType + * must also be supplied to indicate the interpretation of the new format. + */ + GrSurfaceCharacterization createBackendFormat(SkColorType colorType, + const GrBackendFormat& backendFormat) const; + + /* + * Return a new surface characterization with just a different use of FBO0 (in GL) + */ + GrSurfaceCharacterization createFBO0(bool usesGLFBO0) const; + + GrContextThreadSafeProxy* contextInfo() const { return fContextInfo.get(); } + sk_sp refContextInfo() const { return fContextInfo; } + size_t cacheMaxResourceBytes() const { return fCacheMaxResourceBytes; } + + bool isValid() const { return kUnknown_SkColorType != fImageInfo.colorType(); } + + const SkImageInfo& imageInfo() const { return fImageInfo; } + const GrBackendFormat& backendFormat() const { return fBackendFormat; } + GrSurfaceOrigin origin() const { return fOrigin; } + SkISize dimensions() const { return fImageInfo.dimensions(); } + int width() const { return fImageInfo.width(); } + int height() const { return fImageInfo.height(); } + SkColorType colorType() const { return fImageInfo.colorType(); } + int sampleCount() const { return fSampleCnt; } + bool isTextureable() const { return Textureable::kYes == fIsTextureable; } + bool isMipMapped() const { return skgpu::Mipmapped::kYes == fIsMipmapped; } + bool usesGLFBO0() const { return UsesGLFBO0::kYes == fUsesGLFBO0; } + bool vkRTSupportsInputAttachment() const { + return VkRTSupportsInputAttachment::kYes == fVkRTSupportsInputAttachment; + } + bool vulkanSecondaryCBCompatible() const { + return VulkanSecondaryCBCompatible::kYes == fVulkanSecondaryCBCompatible; + } + skgpu::Protected isProtected() const { return fIsProtected; } + SkColorSpace* colorSpace() const { return fImageInfo.colorSpace(); } + sk_sp refColorSpace() const { return fImageInfo.refColorSpace(); } + const SkSurfaceProps& surfaceProps()const { return fSurfaceProps; } + +private: + friend class SkSurface_Ganesh; // for 'set' & 'config' + friend class GrVkSecondaryCBDrawContext; // for 'set' & 'config' + friend class GrContextThreadSafeProxy; // for private ctor + friend class GrVkContextThreadSafeProxy; // for private ctor + friend class GrDeferredDisplayListRecorder; // for 'config' + friend class SkSurface; // for 'config' + + SkDEBUGCODE(void validate() const;) + + GrSurfaceCharacterization(sk_sp contextInfo, + size_t cacheMaxResourceBytes, + const SkImageInfo& ii, + const GrBackendFormat& backendFormat, + GrSurfaceOrigin origin, + int sampleCnt, + Textureable isTextureable, + skgpu::Mipmapped isMipmapped, + UsesGLFBO0 usesGLFBO0, + VkRTSupportsInputAttachment vkRTSupportsInputAttachment, + VulkanSecondaryCBCompatible vulkanSecondaryCBCompatible, + skgpu::Protected isProtected, + const SkSurfaceProps& surfaceProps) + : fContextInfo(std::move(contextInfo)) + , fCacheMaxResourceBytes(cacheMaxResourceBytes) + , fImageInfo(ii) + , fBackendFormat(std::move(backendFormat)) + , fOrigin(origin) + , fSampleCnt(sampleCnt) + , fIsTextureable(isTextureable) + , fIsMipmapped(isMipmapped) + , fUsesGLFBO0(usesGLFBO0) + , fVkRTSupportsInputAttachment(vkRTSupportsInputAttachment) + , fVulkanSecondaryCBCompatible(vulkanSecondaryCBCompatible) + , fIsProtected(isProtected) + , fSurfaceProps(surfaceProps) { + if (fSurfaceProps.flags() & SkSurfaceProps::kDynamicMSAA_Flag) { + // Dynamic MSAA is not currently supported with DDL. + *this = {}; + } + SkDEBUGCODE(this->validate()); + } + + void set(sk_sp contextInfo, + size_t cacheMaxResourceBytes, + const SkImageInfo& ii, + const GrBackendFormat& backendFormat, + GrSurfaceOrigin origin, + int sampleCnt, + Textureable isTextureable, + skgpu::Mipmapped isMipmapped, + UsesGLFBO0 usesGLFBO0, + VkRTSupportsInputAttachment vkRTSupportsInputAttachment, + VulkanSecondaryCBCompatible vulkanSecondaryCBCompatible, + skgpu::Protected isProtected, + const SkSurfaceProps& surfaceProps) { + if (surfaceProps.flags() & SkSurfaceProps::kDynamicMSAA_Flag) { + // Dynamic MSAA is not currently supported with DDL. + *this = {}; + } else { + fContextInfo = std::move(contextInfo); + fCacheMaxResourceBytes = cacheMaxResourceBytes; + + fImageInfo = ii; + fBackendFormat = std::move(backendFormat); + fOrigin = origin; + fSampleCnt = sampleCnt; + fIsTextureable = isTextureable; + fIsMipmapped = isMipmapped; + fUsesGLFBO0 = usesGLFBO0; + fVkRTSupportsInputAttachment = vkRTSupportsInputAttachment; + fVulkanSecondaryCBCompatible = vulkanSecondaryCBCompatible; + fIsProtected = isProtected; + fSurfaceProps = surfaceProps; + } + SkDEBUGCODE(this->validate()); + } + + sk_sp fContextInfo; + size_t fCacheMaxResourceBytes; + + SkImageInfo fImageInfo; + GrBackendFormat fBackendFormat; + GrSurfaceOrigin fOrigin; + int fSampleCnt; + Textureable fIsTextureable; + skgpu::Mipmapped fIsMipmapped; + UsesGLFBO0 fUsesGLFBO0; + VkRTSupportsInputAttachment fVkRTSupportsInputAttachment; + VulkanSecondaryCBCompatible fVulkanSecondaryCBCompatible; + skgpu::Protected fIsProtected; + SkSurfaceProps fSurfaceProps; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/GrVkSecondaryCBDrawContext.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/GrVkSecondaryCBDrawContext.h new file mode 100644 index 0000000000..d0348312bd --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/GrVkSecondaryCBDrawContext.h @@ -0,0 +1,131 @@ +/* + * Copyright 2019 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrVkSecondaryCBDrawContext_DEFINED +#define GrVkSecondaryCBDrawContext_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkSurfaceProps.h" +#include "include/core/SkTypes.h" + +#include + +class GrBackendSemaphore; +class GrDeferredDisplayList; +class GrRecordingContext; +class GrSurfaceCharacterization; +struct GrVkDrawableInfo; +namespace skgpu::ganesh { +class Device; +} +class SkCanvas; +struct SkImageInfo; +class SkSurfaceProps; + +/** + * This class is a private header that is intended to only be used inside of Chromium. This requires + * Chromium to burrow in and include this specifically since it is not part of skia's public include + * directory. + */ + +/** + * This class is used to draw into an external Vulkan secondary command buffer that is imported + * by the client. The secondary command buffer that gets imported must already have had begin called + * on it with VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT. Thus any draws to the imported + * command buffer cannot require changing the render pass. This requirement means that certain types + * of draws will not be supported when using a GrVkSecondaryCBDrawContext. This includes: + * Draws that require a dst copy for blending will be dropped + * Text draws will be dropped (these may require intermediate uploads of text data) + * Read and Write pixels will not work + * Any other draw that requires a copy will fail (this includes using backdrop filter with save + * layer). + * Stenciling is also disabled, but that should not restrict any actual draws from working. + * + * While using a GrVkSecondaryCBDrawContext, the client can also draw into normal SkSurfaces and + * then draw those SkSufaces (as SkImages) into the GrVkSecondaryCBDrawContext. If any of the + * previously mentioned unsupported draws are needed by the client, they can draw them into an + * offscreen surface, and then draw that into the GrVkSecondaryCBDrawContext. + * + * After all drawing to the GrVkSecondaryCBDrawContext has been done, the client must call flush() + * on the GrVkSecondaryCBDrawContext to actually fill in the secondary VkCommandBuffer with the + * draws. + * + * Additionally, the client must keep the GrVkSecondaryCBDrawContext alive until the secondary + * VkCommandBuffer has been submitted and all work finished on the GPU. Before deleting the + * GrVkSecondaryCBDrawContext, the client must call releaseResources() so that Skia can cleanup + * any internal objects that were created for the draws into the secondary command buffer. + */ +class SK_SPI GrVkSecondaryCBDrawContext : public SkRefCnt { +public: + static sk_sp Make(GrRecordingContext*, + const SkImageInfo&, + const GrVkDrawableInfo&, + const SkSurfaceProps* props); + + ~GrVkSecondaryCBDrawContext() override; + + SkCanvas* getCanvas(); + + // Records all the draws to the imported secondary command buffer and sets any dependent + // offscreen draws to the GPU. + void flush(); + + /** Inserts a list of GPU semaphores that Skia will have the driver wait on before executing + commands for this secondary CB. The wait semaphores will get added to the VkCommandBuffer + owned by this GrContext when flush() is called, and not the command buffer which the + Secondary CB is from. This will guarantee that the driver waits on the semaphores before + the secondary command buffer gets executed. We will submit the semphore to wait at + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT and VK_PIPELINE_STAGE_TRANSFER_BIT. If this + call returns false, then the GPU back end will not wait on any passed in semaphores, and the + client will still own the semaphores, regardless of the value of deleteSemaphoresAfterWait. + + If deleteSemaphoresAfterWait is false then Skia will not delete the semaphores. In this case + it is the client's responsibility to not destroy or attempt to reuse the semaphores until it + knows that Skia has finished waiting on them. This can be done by using finishedProcs + on flush calls. + + @param numSemaphores size of waitSemaphores array + @param waitSemaphores array of semaphore containers + @paramm deleteSemaphoresAfterWait who owns and should delete the semaphores + @return true if GPU is waiting on semaphores + */ + bool wait(int numSemaphores, + const GrBackendSemaphore waitSemaphores[], + bool deleteSemaphoresAfterWait = true); + + // This call will release all resources held by the draw context. The client must call + // releaseResources() before deleting the drawing context. However, the resources also include + // any Vulkan resources that were created and used for draws. Therefore the client must only + // call releaseResources() after submitting the secondary command buffer, and waiting for it to + // finish on the GPU. If it is called earlier then some vulkan objects may be deleted while they + // are still in use by the GPU. + void releaseResources(); + + const SkSurfaceProps& props() const { return fProps; } + + // TODO: Fill out these calls to support DDL + bool characterize(GrSurfaceCharacterization* characterization) const; + +#ifndef SK_DDL_IS_UNIQUE_POINTER + bool draw(sk_sp deferredDisplayList); +#else + bool draw(const GrDeferredDisplayList* deferredDisplayList); +#endif + + bool isCompatible(const GrSurfaceCharacterization& characterization) const; + +private: + explicit GrVkSecondaryCBDrawContext(sk_sp, const SkSurfaceProps*); + + sk_sp fDevice; + std::unique_ptr fCachedCanvas; + const SkSurfaceProps fProps; + + using INHERITED = SkRefCnt; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/SkChromeRemoteGlyphCache.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/SkChromeRemoteGlyphCache.h new file mode 100644 index 0000000000..3a71a45e17 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/SkChromeRemoteGlyphCache.h @@ -0,0 +1,150 @@ +/* + * Copyright 2021 Google LLC. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkChromeRemoteGlyphCache_DEFINED +#define SkChromeRemoteGlyphCache_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkTypeface.h" +#include "include/private/base/SkAPI.h" + +#include +#include +#include +#include + +class SkAutoDescriptor; +class SkCanvas; +class SkColorSpace; +class SkStrikeCache; +class SkStrikeClientImpl; +class SkStrikeServerImpl; +class SkSurfaceProps; +namespace sktext::gpu { class Slug; } + +using SkDiscardableHandleId = uint32_t; +// This class is not thread-safe. +class SkStrikeServer { +public: + // An interface used by the server to create handles for pinning SkStrike + // entries on the remote client. + class DiscardableHandleManager { + public: + SK_SPI virtual ~DiscardableHandleManager() = default; + + // Creates a new *locked* handle and returns a unique ID that can be used to identify + // it on the remote client. + SK_SPI virtual SkDiscardableHandleId createHandle() = 0; + + // Returns true if the handle could be successfully locked. The server can + // assume it will remain locked until the next set of serialized entries is + // pulled from the SkStrikeServer. + // If returns false, the cache entry mapped to the handle has been deleted + // on the client. Any subsequent attempts to lock the same handle are not + // allowed. + SK_SPI virtual bool lockHandle(SkDiscardableHandleId) = 0; + + // Returns true if a handle has been deleted on the remote client. It is + // invalid to use a handle id again with this manager once this returns true. + SK_SPI virtual bool isHandleDeleted(SkDiscardableHandleId) = 0; + }; + + SK_SPI explicit SkStrikeServer(DiscardableHandleManager* discardableHandleManager); + SK_SPI ~SkStrikeServer(); + + // Create an analysis SkCanvas used to populate the SkStrikeServer with ops + // which will be serialized and rendered using the SkStrikeClient. + SK_API std::unique_ptr makeAnalysisCanvas(int width, int height, + const SkSurfaceProps& props, + sk_sp colorSpace, + bool DFTSupport, + bool DFTPerspSupport = true); + + // Serializes the strike data captured using a canvas returned by ::makeAnalysisCanvas. Any + // handles locked using the DiscardableHandleManager will be assumed to be + // unlocked after this call. + SK_SPI void writeStrikeData(std::vector* memory); + + // Testing helpers + void setMaxEntriesInDescriptorMapForTesting(size_t count); + size_t remoteStrikeMapSizeForTesting() const; + +private: + SkStrikeServerImpl* impl(); + + std::unique_ptr fImpl; +}; + +class SkStrikeClient { +public: + // This enum is used in histogram reporting in chromium. Please don't re-order the list of + // entries, and consider it to be append-only. + enum CacheMissType : uint32_t { + // Hard failures where no fallback could be found. + kFontMetrics = 0, + kGlyphMetrics = 1, + kGlyphImage = 2, + kGlyphPath = 3, + + // (DEPRECATED) The original glyph could not be found and a fallback was used. + kGlyphMetricsFallback = 4, + kGlyphPathFallback = 5, + + kGlyphDrawable = 6, + kLast = kGlyphDrawable + }; + + // An interface to delete handles that may be pinned by the remote server. + class DiscardableHandleManager : public SkRefCnt { + public: + ~DiscardableHandleManager() override = default; + + // Returns true if the handle was unlocked and can be safely deleted. Once + // successful, subsequent attempts to delete the same handle are invalid. + virtual bool deleteHandle(SkDiscardableHandleId) = 0; + + virtual void assertHandleValid(SkDiscardableHandleId) {} + + virtual void notifyCacheMiss(CacheMissType type, int fontSize) = 0; + + struct ReadFailureData { + size_t memorySize; + size_t bytesRead; + uint64_t typefaceSize; + uint64_t strikeCount; + uint64_t glyphImagesCount; + uint64_t glyphPathsCount; + }; + virtual void notifyReadFailure(const ReadFailureData& data) {} + }; + + SK_SPI explicit SkStrikeClient(sk_sp, + bool isLogging = true, + SkStrikeCache* strikeCache = nullptr); + SK_SPI ~SkStrikeClient(); + + // Deserializes the strike data from a SkStrikeServer. All messages generated + // from a server when serializing the ops must be deserialized before the op + // is rasterized. + // Returns false if the data is invalid. + SK_SPI bool readStrikeData(const volatile void* memory, size_t memorySize); + + // Given a descriptor re-write the Rec mapping the typefaceID from the renderer to the + // corresponding typefaceID on the GPU. + SK_SPI bool translateTypefaceID(SkAutoDescriptor* descriptor) const; + + // Testing helpers + sk_sp retrieveTypefaceUsingServerIDForTest(SkTypefaceID) const; + + // Given a buffer, unflatten into a slug making sure to do the typefaceID translation from + // renderer to GPU. Returns nullptr if there was a problem. + sk_sp deserializeSlugForTest(const void* data, size_t size) const; + +private: + std::unique_ptr fImpl; +}; +#endif // SkChromeRemoteGlyphCache_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/SkDiscardableMemory.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/SkDiscardableMemory.h new file mode 100644 index 0000000000..3aa9870360 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/SkDiscardableMemory.h @@ -0,0 +1,70 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkDiscardableMemory_DEFINED +#define SkDiscardableMemory_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/core/SkTypes.h" + +/** + * Interface for discardable memory. Implementation is provided by the + * embedder. + */ +class SK_SPI SkDiscardableMemory { +public: + /** + * Factory method that creates, initializes and locks an SkDiscardableMemory + * object. If either of these steps fails, a nullptr pointer will be returned. + */ + static SkDiscardableMemory* Create(size_t bytes); + + /** + * Factory class that creates, initializes and locks an SkDiscardableMemory + * object. If either of these steps fails, a nullptr pointer will be returned. + */ + class Factory : public SkRefCnt { + public: + virtual SkDiscardableMemory* create(size_t bytes) = 0; + private: + using INHERITED = SkRefCnt; + }; + + /** Must not be called while locked. + */ + virtual ~SkDiscardableMemory() {} + + /** + * Locks the memory, prevent it from being discarded. Once locked. you may + * obtain a pointer to that memory using the data() method. + * + * lock() may return false, indicating that the underlying memory was + * discarded and that the lock failed. + * + * Nested calls to lock are not allowed. + */ + [[nodiscard]] virtual bool lock() = 0; + + /** + * Returns the current pointer for the discardable memory. This call is ONLY + * valid when the discardable memory object is locked. + */ + virtual void* data() = 0; + + /** + * Unlock the memory so that it can be purged by the system. Must be called + * after every successful lock call. + */ + virtual void unlock() = 0; + +protected: + SkDiscardableMemory() = default; + SkDiscardableMemory(const SkDiscardableMemory&) = delete; + SkDiscardableMemory& operator=(const SkDiscardableMemory&) = delete; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/SkImageChromium.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/SkImageChromium.h new file mode 100644 index 0000000000..7c62ba581b --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/SkImageChromium.h @@ -0,0 +1,117 @@ +/* + * Copyright 2023 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkImageChromium_DEFINED +#define SkImageChromium_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/private/base/SkAPI.h" + +class GrBackendFormat; +class GrContextThreadSafeProxy; +class GrPromiseImageTexture; +class GrDirectContext; +class GrYUVABackendTextureInfo; +class SkColorSpace; +class SkImage; +enum SkAlphaType : int; +enum SkColorType : int; +enum GrSurfaceOrigin : int; +namespace skgpu { +enum class Mipmapped : bool; +} +struct SkISize; + +/** + * These functions expose features that are only for external use in Chromium. + */ + +namespace SkImages { + +using PromiseImageTextureContext = void*; +using PromiseImageTextureFulfillProc = sk_sp (*)(PromiseImageTextureContext); +using PromiseImageTextureReleaseProc = void (*)(PromiseImageTextureContext); + +/** Create a new GPU-backed SkImage that is very similar to an SkImage created by BorrowTextureFrom. + The difference is that the caller need not have created the texture nor populated it with the + image pixel data. Moreover, the SkImage may be created on a thread as the creation of the + image does not require access to the backend API or GrDirectContext. Instead of passing a + GrBackendTexture the client supplies a description of the texture consisting of + GrBackendFormat, width, height, and skgpu::Mipmapped state. The resulting SkImage can be drawn + to a GrDeferredDisplayListRecorder or directly to a GPU-backed SkSurface. + When the actual texture is required to perform a backend API draw, textureFulfillProc will + be called to receive a GrBackendTexture. The properties of the GrBackendTexture must match + those set during the SkImage creation, and it must refer to a valid existing texture in the + backend API context/device, and be populated with the image pixel data. The texture cannot + be deleted until textureReleaseProc is called. + There is at most one call to each of textureFulfillProc and textureReleaseProc. + textureReleaseProc is always called even if image creation fails or if the + image is never fulfilled (e.g. it is never drawn or all draws are clipped out) + @param gpuContextProxy the thread-safe proxy of the gpu context. required. + @param backendFormat format of promised gpu texture + @param dimensions width & height of promised gpu texture + @param mipmapped mip mapped state of promised gpu texture + @param origin surface origin of promised gpu texture + @param colorType color type of promised gpu texture + @param alphaType alpha type of promised gpu texture + @param colorSpace range of colors; may be nullptr + @param textureFulfillProc function called to get actual gpu texture + @param textureReleaseProc function called when texture can be deleted + @param textureContext state passed to textureFulfillProc and textureReleaseProc + @return created SkImage, or nullptr +*/ +SK_API sk_sp PromiseTextureFrom(sk_sp gpuContextProxy, + const GrBackendFormat& backendFormat, + SkISize dimensions, + skgpu::Mipmapped mipmapped, + GrSurfaceOrigin origin, + SkColorType colorType, + SkAlphaType alphaType, + sk_sp colorSpace, + PromiseImageTextureFulfillProc textureFulfillProc, + PromiseImageTextureReleaseProc textureReleaseProc, + PromiseImageTextureContext textureContext); + +/** This is similar to 'PromiseTextureFrom' but it creates a GPU-backed SkImage from YUV[A] data. + The source data may be planar (i.e. spread across multiple textures). In + the extreme Y, U, V, and A are all in different planes and thus the image is specified by + four textures. 'backendTextureInfo' describes the planar arrangement, texture formats, + conversion to RGB, and origin of the textures. Separate 'textureFulfillProc' and + 'textureReleaseProc' calls are made for each texture. Each texture has its own + PromiseImageTextureContext. If 'backendTextureInfo' is not valid then no release proc + calls are made. Otherwise, the calls will be made even on failure. 'textureContexts' has one + entry for each of the up to four textures, as indicated by 'backendTextureInfo'. + Currently the mip mapped property of 'backendTextureInfo' is ignored. However, in the + near future it will be required that if it is kYes then textureFulfillProc must return + a mip mapped texture for each plane in order to successfully draw the image. + @param gpuContextProxy the thread-safe proxy of the gpu context. required. + @param backendTextureInfo info about the promised yuva gpu texture + @param imageColorSpace range of colors; may be nullptr + @param textureFulfillProc function called to get actual gpu texture + @param textureReleaseProc function called when texture can be deleted + @param textureContexts state passed to textureFulfillProc and textureReleaseProc + @return created SkImage, or nullptr +*/ +SK_API sk_sp PromiseTextureFromYUVA(sk_sp gpuContextProxy, + const GrYUVABackendTextureInfo& backendTextureInfo, + sk_sp imageColorSpace, + PromiseImageTextureFulfillProc textureFulfillProc, + PromiseImageTextureReleaseProc textureReleaseProc, + PromiseImageTextureContext textureContexts[]); + +/** Returns the GPU context associated with this image or nullptr if the image is not Ganesh-backed. + We expose this only to help transition certain API calls and do not intend for this to stick + around forever. +*/ +SK_API GrDirectContext* GetContext(const SkImage* src); +inline GrDirectContext* GetContext(const sk_sp& src) { + return GetContext(src.get()); +} + +} // namespace SkImages + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/Slug.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/Slug.h new file mode 100644 index 0000000000..0ee6082b8a --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/chromium/Slug.h @@ -0,0 +1,72 @@ +/* + * Copyright 2021 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef sktext_gpu_Slug_DEFINED +#define sktext_gpu_Slug_DEFINED + +#include "include/core/SkRect.h" +#include "include/core/SkRefCnt.h" +#include "include/private/base/SkAPI.h" + +#include +#include + +class SkCanvas; +class SkData; +class SkPaint; +class SkReadBuffer; +class SkStrikeClient; +class SkTextBlob; +class SkWriteBuffer; +struct SkDeserialProcs; +struct SkPoint; + +namespace sktext::gpu { +// Slug encapsulates an SkTextBlob at a specific origin, using a specific paint. It can be +// manipulated using matrix and clip changes to the canvas. If the canvas is transformed, then +// the Slug will also transform with smaller glyphs using bi-linear interpolation to render. You +// can think of a Slug as making a rubber stamp out of a SkTextBlob. +class SK_API Slug : public SkRefCnt { +public: + // Return nullptr if the blob would not draw. This is not because of clipping, but because of + // some paint optimization. The Slug is captured as if drawn using drawTextBlob. + static sk_sp ConvertBlob( + SkCanvas* canvas, const SkTextBlob& blob, SkPoint origin, const SkPaint& paint); + + // Serialize the slug. + sk_sp serialize() const; + size_t serialize(void* buffer, size_t size) const; + + // Set the client parameter to the appropriate SkStrikeClient when typeface ID translation + // is needed. + static sk_sp Deserialize(const void* data, + size_t size, + const SkStrikeClient* client = nullptr); + static sk_sp MakeFromBuffer(SkReadBuffer& buffer); + + // Allows clients to deserialize SkPictures that contain slug data + static void AddDeserialProcs(SkDeserialProcs* procs, const SkStrikeClient* client = nullptr); + + // Draw the Slug obeying the canvas's mapping and clipping. + void draw(SkCanvas* canvas, const SkPaint& paint) const; + + virtual SkRect sourceBounds() const = 0; + virtual SkRect sourceBoundsWithOrigin () const = 0; + + virtual void doFlatten(SkWriteBuffer&) const = 0; + + uint32_t uniqueID() const { return fUniqueID; } + +private: + static uint32_t NextUniqueID(); + const uint32_t fUniqueID{NextUniqueID()}; +}; + + +} // namespace sktext::gpu + +#endif // sktext_gpu_Slug_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/ganesh/GrContext_Base.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/ganesh/GrContext_Base.h new file mode 100644 index 0000000000..450bea411b --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/ganesh/GrContext_Base.h @@ -0,0 +1,104 @@ +/* + * Copyright 2019 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrContext_Base_DEFINED +#define GrContext_Base_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/gpu/GrTypes.h" +#include "include/private/base/SkAPI.h" + +#include + +class GrBaseContextPriv; +class GrCaps; +class GrContextThreadSafeProxy; +class GrDirectContext; +class GrImageContext; +class GrRecordingContext; +enum SkColorType : int; +enum class SkTextureCompressionType; +struct GrContextOptions; +class GrBackendFormat; + +class GrContext_Base : public SkRefCnt { +public: + ~GrContext_Base() override; + + /* + * Safely downcast to a GrDirectContext. + */ + virtual GrDirectContext* asDirectContext() { return nullptr; } + + /* + * The 3D API backing this context + */ + SK_API GrBackendApi backend() const; + + /* + * Retrieve the default GrBackendFormat for a given SkColorType and renderability. + * It is guaranteed that this backend format will be the one used by the GrContext + * SkColorType and GrSurfaceCharacterization-based createBackendTexture methods. + * + * The caller should check that the returned format is valid. + */ + SK_API GrBackendFormat defaultBackendFormat(SkColorType, GrRenderable) const; + + SK_API GrBackendFormat compressedBackendFormat(SkTextureCompressionType) const; + + /** + * Gets the maximum supported sample count for a color type. 1 is returned if only non-MSAA + * rendering is supported for the color type. 0 is returned if rendering to this color type + * is not supported at all. + */ + SK_API int maxSurfaceSampleCountForColorType(SkColorType colorType) const; + + // TODO: When the public version is gone, rename to refThreadSafeProxy and add raw ptr ver. + sk_sp threadSafeProxy(); + + // Provides access to functions that aren't part of the public API. + GrBaseContextPriv priv(); + const GrBaseContextPriv priv() const; // NOLINT(readability-const-return-type) + +protected: + friend class GrBaseContextPriv; // for hidden functions + + GrContext_Base(sk_sp); + + virtual bool init(); + + /** + * An identifier for this context. The id is used by all compatible contexts. For example, + * if SkImages are created on one thread using an image creation context, then fed into a + * DDL Recorder on second thread (which has a recording context) and finally replayed on + * a third thread with a direct context, then all three contexts will report the same id. + * It is an error for an image to be used with contexts that report different ids. + */ + uint32_t contextID() const; + + bool matches(GrContext_Base* candidate) const { + return candidate && candidate->contextID() == this->contextID(); + } + + /* + * The options in effect for this context + */ + const GrContextOptions& options() const; + + const GrCaps* caps() const; + sk_sp refCaps() const; + + virtual GrImageContext* asImageContext() { return nullptr; } + virtual GrRecordingContext* asRecordingContext() { return nullptr; } + + sk_sp fThreadSafeProxy; + +private: + using INHERITED = SkRefCnt; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/ganesh/GrD3DTypesMinimal.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/ganesh/GrD3DTypesMinimal.h new file mode 100644 index 0000000000..9d6156d621 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/ganesh/GrD3DTypesMinimal.h @@ -0,0 +1,74 @@ +/* + * Copyright 2020 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrD3DTypesMinimal_DEFINED +#define GrD3DTypesMinimal_DEFINED + +// Minimal definitions of Direct3D types, without including d3d12.h + +#include "include/core/SkRefCnt.h" + +#include + +#include "include/gpu/GrTypes.h" + +struct ID3D12Resource; +class GrD3DResourceState; +typedef int GrD3DResourceStateEnum; +struct GrD3DSurfaceInfo; +struct GrD3DTextureResourceInfo; +struct GrD3DTextureResourceSpec; +struct GrD3DFenceInfo; + +// This struct is to used to store the the actual information about the Direct3D backend image on +// GrBackendTexture and GrBackendRenderTarget. When a client calls getD3DTextureInfo on a +// GrBackendTexture/RenderTarget, we use the GrD3DBackendSurfaceInfo to create a snapshot +// GrD3DTextureResourceInfo object. Internally, this uses a ref count GrD3DResourceState object to +// track the current D3D12_RESOURCE_STATES which can be shared with an internal GrD3DTextureResource +// so that state updates can be seen by all users of the texture. +struct GrD3DBackendSurfaceInfo { + GrD3DBackendSurfaceInfo(const GrD3DTextureResourceInfo& info, GrD3DResourceState* state); + + void cleanup(); + + GrD3DBackendSurfaceInfo& operator=(const GrD3DBackendSurfaceInfo&) = delete; + + // Assigns the passed in GrD3DBackendSurfaceInfo to this object. if isValid is true we will also + // attempt to unref the old fLayout on this object. + void assign(const GrD3DBackendSurfaceInfo&, bool isValid); + + void setResourceState(GrD3DResourceStateEnum state); + + sk_sp getGrD3DResourceState() const; + + GrD3DTextureResourceInfo snapTextureResourceInfo() const; + + bool isProtected() const; +#if defined(GR_TEST_UTILS) + bool operator==(const GrD3DBackendSurfaceInfo& that) const; +#endif + +private: + GrD3DTextureResourceInfo* fTextureResourceInfo; + GrD3DResourceState* fResourceState; +}; + +struct GrD3DTextureResourceSpecHolder { +public: + GrD3DTextureResourceSpecHolder(const GrD3DSurfaceInfo&); + + void cleanup(); + + GrD3DSurfaceInfo getSurfaceInfo(uint32_t sampleCount, + uint32_t levelCount, + skgpu::Protected isProtected) const; + +private: + GrD3DTextureResourceSpec* fSpec; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/ganesh/GrImageContext.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/ganesh/GrImageContext.h new file mode 100644 index 0000000000..a8c81e2d22 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/ganesh/GrImageContext.h @@ -0,0 +1,56 @@ +/* + * Copyright 2019 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrImageContext_DEFINED +#define GrImageContext_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/private/base/SingleOwner.h" +#include "include/private/base/SkAPI.h" +#include "include/private/gpu/ganesh/GrContext_Base.h" + +class GrContextThreadSafeProxy; +class GrImageContextPriv; + +// This is now just a view on a ThreadSafeProxy, that SkImages can attempt to +// downcast to a GrDirectContext as a backdoor to some operations. Once we remove the backdoors, +// this goes away and SkImages just hold ThreadSafeProxies. +class GrImageContext : public GrContext_Base { +public: + ~GrImageContext() override; + + // Provides access to functions that aren't part of the public API. + GrImageContextPriv priv(); + const GrImageContextPriv priv() const; // NOLINT(readability-const-return-type) + +protected: + friend class GrImageContextPriv; // for hidden functions + + GrImageContext(sk_sp); + + SK_API virtual void abandonContext(); + SK_API virtual bool abandoned(); + + /** This is only useful for debug purposes */ + skgpu::SingleOwner* singleOwner() const { return &fSingleOwner; } + + GrImageContext* asImageContext() override { return this; } + +private: + // When making promise images, we currently need a placeholder GrImageContext instance to give + // to the SkImage that has no real power, just a wrapper around the ThreadSafeProxy. + // TODO: De-power SkImage to ThreadSafeProxy or at least figure out a way to share one instance. + static sk_sp MakeForPromiseImage(sk_sp); + + // In debug builds we guard against improper thread handling + // This guard is passed to the GrDrawingManager and, from there to all the + // GrSurfaceDrawContexts. It is also passed to the GrResourceProvider and SkGpuDevice. + // TODO: Move this down to GrRecordingContext. + mutable skgpu::SingleOwner fSingleOwner; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/ganesh/GrTextureGenerator.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/ganesh/GrTextureGenerator.h new file mode 100644 index 0000000000..a8902d863e --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/ganesh/GrTextureGenerator.h @@ -0,0 +1,66 @@ +/* + * Copyright 2023 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrTextureGenerator_DEFINED +#define GrTextureGenerator_DEFINED + +#include "include/core/SkImageGenerator.h" +#include "include/gpu/GrTypes.h" +#include "include/private/base/SkAPI.h" + +#include + +class GrRecordingContext; +class GrSurfaceProxyView; +enum class GrImageTexGenPolicy : int; +namespace skgpu { enum class Mipmapped : bool; } +struct SkImageInfo; + +class SK_API GrTextureGenerator : public SkImageGenerator { +public: + bool isTextureGenerator() const final { return true; } + + /** + * If the generator can natively/efficiently return its pixels as a GPU image (backed by a + * texture) this will return that image. If not, this will return NULL. + * + * Regarding the GrRecordingContext parameter: + * + * It must be non-NULL. The generator should only succeed if: + * - its internal context is the same + * - it can somehow convert its texture into one that is valid for the provided context. + * + * If the mipmapped parameter is kYes, the generator should try to create a TextureProxy that + * at least has the mip levels allocated and the base layer filled in. If this is not possible, + * the generator is allowed to return a non mipped proxy, but this will have some additional + * overhead in later allocating mips and copying of the base layer. + * + * GrImageTexGenPolicy determines whether or not a new texture must be created (and its budget + * status) or whether this may (but is not required to) return a pre-existing texture that is + * retained by the generator (kDraw). + */ + GrSurfaceProxyView generateTexture(GrRecordingContext*, + const SkImageInfo& info, + skgpu::Mipmapped mipmapped, + GrImageTexGenPolicy); + + // External clients should override GrExternalTextureGenerator instead of trying to implement + // this (which uses private Skia types) + virtual GrSurfaceProxyView onGenerateTexture(GrRecordingContext*, const SkImageInfo&, + skgpu::Mipmapped, GrImageTexGenPolicy) = 0; + + // Most internal SkImageGenerators produce textures and views that use kTopLeft_GrSurfaceOrigin. + // If the generator may produce textures with different origins (e.g. + // GrAHardwareBufferImageGenerator) it should override this function to return the correct + // origin. Implementations should be thread-safe. + virtual GrSurfaceOrigin origin() const { return kTopLeft_GrSurfaceOrigin; } + +protected: + GrTextureGenerator(const SkImageInfo& info, uint32_t uniqueId = kNeedNewImageUniqueID); +}; + +#endif // GrTextureGenerator_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/ganesh/GrTypesPriv.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/ganesh/GrTypesPriv.h new file mode 100644 index 0000000000..5568cb54de --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/ganesh/GrTypesPriv.h @@ -0,0 +1,1007 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrTypesPriv_DEFINED +#define GrTypesPriv_DEFINED + +#include "include/core/SkColor.h" +#include "include/core/SkColorType.h" +#include "include/core/SkData.h" +#include "include/core/SkPath.h" +#include "include/core/SkPathTypes.h" +#include "include/core/SkRefCnt.h" +#include "include/gpu/GrTypes.h" +#include "include/private/base/SkAssert.h" +#include "include/private/base/SkDebug.h" +#include "include/private/base/SkMacros.h" +#include "include/private/base/SkTypeTraits.h" + +#include +#include +#include +#include + +class GrSurfaceProxy; + +namespace skgpu { +enum class Mipmapped : bool; +} + +/** + * divide, rounding up + */ + +static inline constexpr size_t GrSizeDivRoundUp(size_t x, size_t y) { return (x + (y - 1)) / y; } + +/** + * Geometric primitives used for drawing. + */ +enum class GrPrimitiveType : uint8_t { + kTriangles, + kTriangleStrip, + kPoints, + kLines, // 1 pix wide only + kLineStrip, // 1 pix wide only +}; +static constexpr int kNumGrPrimitiveTypes = (int)GrPrimitiveType::kLineStrip + 1; + +static constexpr bool GrIsPrimTypeLines(GrPrimitiveType type) { + return GrPrimitiveType::kLines == type || GrPrimitiveType::kLineStrip == type; +} + +enum class GrPrimitiveRestart : bool { + kNo = false, + kYes = true +}; + +/** + * Should a created surface be texturable? + */ +enum class GrTexturable : bool { + kNo = false, + kYes = true +}; + +// A DDL recorder has its own proxy provider and proxy cache. This enum indicates if +// a given proxy provider is one of these special ones. +enum class GrDDLProvider : bool { + kNo = false, + kYes = true +}; + +/** Ownership rules for external GPU resources imported into Skia. */ +enum GrWrapOwnership { + /** Skia will assume the client will keep the resource alive and Skia will not free it. */ + kBorrow_GrWrapOwnership, + + /** Skia will assume ownership of the resource and free it. */ + kAdopt_GrWrapOwnership, +}; + +enum class GrWrapCacheable : bool { + /** + * The wrapped resource will be removed from the cache as soon as it becomes purgeable. It may + * still be assigned and found by a unique key, but the presence of the key will not be used to + * keep the resource alive when it has no references. + */ + kNo = false, + /** + * The wrapped resource is allowed to remain in the GrResourceCache when it has no references + * but has a unique key. Such resources should only be given unique keys when it is known that + * the key will eventually be removed from the resource or invalidated via the message bus. + */ + kYes = true +}; + +enum class GrBudgetedType : uint8_t { + /** The resource is budgeted and is subject to purging under budget pressure. */ + kBudgeted, + /** + * The resource is unbudgeted and is purged as soon as it has no refs regardless of whether + * it has a unique or scratch key. + */ + kUnbudgetedUncacheable, + /** + * The resource is unbudgeted and is allowed to remain in the cache with no refs if it + * has a unique key. Scratch keys are ignored. + */ + kUnbudgetedCacheable, +}; + +enum class GrScissorTest : bool { + kDisabled = false, + kEnabled = true +}; + +/* + * Used to say whether texture is backed by memory. + */ +enum class GrMemoryless : bool { + /** + * The texture will be allocated normally and will affect memory budgets. + */ + kNo = false, + /** + * The texture will be not use GPU memory and will not affect memory budgets. + */ + kYes = true +}; + +struct GrMipLevel { + const void* fPixels = nullptr; + size_t fRowBytes = 0; + // This may be used to keep fPixels from being freed while a GrMipLevel exists. + sk_sp fOptionalStorage; + + static_assert(::sk_is_trivially_relocatable::value); + static_assert(::sk_is_trivially_relocatable::value); + + using sk_is_trivially_relocatable = std::true_type; +}; + +enum class GrSemaphoreWrapType { + kWillSignal, + kWillWait, +}; + +/** + * This enum is used to specify the load operation to be used when an OpsTask/GrOpsRenderPass + * begins execution. + */ +enum class GrLoadOp { + kLoad, + kClear, + kDiscard, +}; + +/** + * This enum is used to specify the store operation to be used when an OpsTask/GrOpsRenderPass + * ends execution. + */ +enum class GrStoreOp { + kStore, + kDiscard, +}; + +/** + * Used to control antialiasing in draw calls. + */ +enum class GrAA : bool { + kNo = false, + kYes = true +}; + +enum class GrFillRule : bool { + kNonzero, + kEvenOdd +}; + +inline GrFillRule GrFillRuleForPathFillType(SkPathFillType fillType) { + switch (fillType) { + case SkPathFillType::kWinding: + case SkPathFillType::kInverseWinding: + return GrFillRule::kNonzero; + case SkPathFillType::kEvenOdd: + case SkPathFillType::kInverseEvenOdd: + return GrFillRule::kEvenOdd; + } + SkUNREACHABLE; +} + +inline GrFillRule GrFillRuleForSkPath(const SkPath& path) { + return GrFillRuleForPathFillType(path.getFillType()); +} + +/** This enum indicates the type of antialiasing to be performed. */ +enum class GrAAType : unsigned { + /** No antialiasing */ + kNone, + /** Use fragment shader code to blend with a fractional pixel coverage. */ + kCoverage, + /** Use normal MSAA. */ + kMSAA, + + kLast = kMSAA +}; +static const int kGrAATypeCount = static_cast(GrAAType::kLast) + 1; + +static constexpr bool GrAATypeIsHW(GrAAType type) { + switch (type) { + case GrAAType::kNone: + return false; + case GrAAType::kCoverage: + return false; + case GrAAType::kMSAA: + return true; + } + SkUNREACHABLE; +} + +/** + * Some pixel configs are inherently clamped to [0,1], some are allowed to go outside that range, + * and some are FP but manually clamped in the XP. + */ +enum class GrClampType { + kAuto, // Normalized, fixed-point configs + kManual, // Clamped FP configs + kNone, // Normal (unclamped) FP configs +}; + +/** + * A number of rectangle/quadrilateral drawing APIs can control anti-aliasing on a per edge basis. + * These masks specify which edges are AA'ed. The intent for this is to support tiling with seamless + * boundaries, where the inner edges are non-AA and the outer edges are AA. Regular rectangle draws + * simply use kAll or kNone depending on if they want anti-aliasing or not. + * + * In APIs that support per-edge AA, GrQuadAAFlags is the only AA-control parameter that is + * provided (compared to the typical GrAA parameter). kNone is equivalent to GrAA::kNo, and any + * other set of edge flags would require GrAA::kYes (with rendering output dependent on how that + * maps to GrAAType for a given SurfaceDrawContext). + * + * These values are identical to SkCanvas::QuadAAFlags. + */ +enum class GrQuadAAFlags { + kLeft = 0b0001, + kTop = 0b0010, + kRight = 0b0100, + kBottom = 0b1000, + + kNone = 0b0000, + kAll = 0b1111, +}; + +GR_MAKE_BITFIELD_CLASS_OPS(GrQuadAAFlags) + +static inline GrQuadAAFlags SkToGrQuadAAFlags(unsigned flags) { + return static_cast(flags); +} + +/** + * The type of texture. Backends other than GL currently only use the 2D value but the type must + * still be known at the API-neutral layer as it used to determine whether MIP maps, renderability, + * and sampling parameters are legal for proxies that will be instantiated with wrapped textures. + */ +enum class GrTextureType { + kNone, + k2D, + /* Rectangle uses unnormalized texture coordinates. */ + kRectangle, + kExternal +}; + +enum GrShaderType { + kVertex_GrShaderType, + kFragment_GrShaderType, + + kLast_GrShaderType = kFragment_GrShaderType +}; +static const int kGrShaderTypeCount = kLast_GrShaderType + 1; + +enum GrShaderFlags { + kNone_GrShaderFlags = 0, + kVertex_GrShaderFlag = 1 << 0, + kFragment_GrShaderFlag = 1 << 1 +}; +SK_MAKE_BITFIELD_OPS(GrShaderFlags) + +/** Rectangle and external textures only support the clamp wrap mode and do not support + * MIP maps. + */ +static inline bool GrTextureTypeHasRestrictedSampling(GrTextureType type) { + switch (type) { + case GrTextureType::k2D: + return false; + case GrTextureType::kRectangle: + return true; + case GrTextureType::kExternal: + return true; + default: + SK_ABORT("Unexpected texture type"); + } +} + +////////////////////////////////////////////////////////////////////////////// + +/** + * Types used to describe format of vertices in arrays. + */ +enum GrVertexAttribType { + kFloat_GrVertexAttribType = 0, + kFloat2_GrVertexAttribType, + kFloat3_GrVertexAttribType, + kFloat4_GrVertexAttribType, + kHalf_GrVertexAttribType, + kHalf2_GrVertexAttribType, + kHalf4_GrVertexAttribType, + + kInt2_GrVertexAttribType, // vector of 2 32-bit ints + kInt3_GrVertexAttribType, // vector of 3 32-bit ints + kInt4_GrVertexAttribType, // vector of 4 32-bit ints + + + kByte_GrVertexAttribType, // signed byte + kByte2_GrVertexAttribType, // vector of 2 8-bit signed bytes + kByte4_GrVertexAttribType, // vector of 4 8-bit signed bytes + kUByte_GrVertexAttribType, // unsigned byte + kUByte2_GrVertexAttribType, // vector of 2 8-bit unsigned bytes + kUByte4_GrVertexAttribType, // vector of 4 8-bit unsigned bytes + + kUByte_norm_GrVertexAttribType, // unsigned byte, e.g. coverage, 0 -> 0.0f, 255 -> 1.0f. + kUByte4_norm_GrVertexAttribType, // vector of 4 unsigned bytes, e.g. colors, 0 -> 0.0f, + // 255 -> 1.0f. + + kShort2_GrVertexAttribType, // vector of 2 16-bit shorts. + kShort4_GrVertexAttribType, // vector of 4 16-bit shorts. + + kUShort2_GrVertexAttribType, // vector of 2 unsigned shorts. 0 -> 0, 65535 -> 65535. + kUShort2_norm_GrVertexAttribType, // vector of 2 unsigned shorts. 0 -> 0.0f, 65535 -> 1.0f. + + kInt_GrVertexAttribType, + kUInt_GrVertexAttribType, + + kUShort_norm_GrVertexAttribType, + + kUShort4_norm_GrVertexAttribType, // vector of 4 unsigned shorts. 0 -> 0.0f, 65535 -> 1.0f. + + kLast_GrVertexAttribType = kUShort4_norm_GrVertexAttribType +}; +static const int kGrVertexAttribTypeCount = kLast_GrVertexAttribType + 1; + +////////////////////////////////////////////////////////////////////////////// + +/** + * We have coverage effects that clip rendering to the edge of some geometric primitive. + * This enum specifies how that clipping is performed. Not all factories that take a + * GrClipEdgeType will succeed with all values and it is up to the caller to verify success. + */ +enum class GrClipEdgeType { + kFillBW, + kFillAA, + kInverseFillBW, + kInverseFillAA, + + kLast = kInverseFillAA +}; +static const int kGrClipEdgeTypeCnt = (int) GrClipEdgeType::kLast + 1; + +static constexpr bool GrClipEdgeTypeIsFill(const GrClipEdgeType edgeType) { + return (GrClipEdgeType::kFillAA == edgeType || GrClipEdgeType::kFillBW == edgeType); +} + +static constexpr bool GrClipEdgeTypeIsInverseFill(const GrClipEdgeType edgeType) { + return (GrClipEdgeType::kInverseFillAA == edgeType || + GrClipEdgeType::kInverseFillBW == edgeType); +} + +static constexpr bool GrClipEdgeTypeIsAA(const GrClipEdgeType edgeType) { + return (GrClipEdgeType::kFillBW != edgeType && + GrClipEdgeType::kInverseFillBW != edgeType); +} + +static inline GrClipEdgeType GrInvertClipEdgeType(const GrClipEdgeType edgeType) { + switch (edgeType) { + case GrClipEdgeType::kFillBW: + return GrClipEdgeType::kInverseFillBW; + case GrClipEdgeType::kFillAA: + return GrClipEdgeType::kInverseFillAA; + case GrClipEdgeType::kInverseFillBW: + return GrClipEdgeType::kFillBW; + case GrClipEdgeType::kInverseFillAA: + return GrClipEdgeType::kFillAA; + } + SkUNREACHABLE; +} + +/** + * Indicates the type of pending IO operations that can be recorded for gpu resources. + */ +enum GrIOType { + kRead_GrIOType, + kWrite_GrIOType, + kRW_GrIOType +}; + +/** + * Indicates the type of data that a GPU buffer will be used for. + */ +enum class GrGpuBufferType { + kVertex, + kIndex, + kDrawIndirect, + kXferCpuToGpu, + kXferGpuToCpu, + kUniform, +}; +static const constexpr int kGrGpuBufferTypeCount = static_cast(GrGpuBufferType::kUniform) + 1; + +/** + * Provides a performance hint regarding the frequency at which a data store will be accessed. + */ +enum GrAccessPattern { + /** Data store will be respecified repeatedly and used many times. */ + kDynamic_GrAccessPattern, + /** Data store will be specified once and used many times. (Thus disqualified from caching.) */ + kStatic_GrAccessPattern, + /** Data store will be specified once and used at most a few times. (Also can't be cached.) */ + kStream_GrAccessPattern, + + kLast_GrAccessPattern = kStream_GrAccessPattern +}; + +// Flags shared between the GrSurface & GrSurfaceProxy class hierarchies +enum class GrInternalSurfaceFlags { + kNone = 0, + + // Texture-level + + // Means the pixels in the texture are read-only. Cannot also be a GrRenderTarget[Proxy]. + kReadOnly = 1 << 0, + + // RT-level + + // This flag is for use with GL only. It tells us that the internal render target wraps FBO 0. + kGLRTFBOIDIs0 = 1 << 1, + + // This means the render target is multisampled, and internally holds a non-msaa texture for + // resolving into. The render target resolves itself by blitting into this internal texture. + // (asTexture() might or might not return the internal texture, but if it does, we always + // resolve the render target before accessing this texture's data.) + kRequiresManualMSAAResolve = 1 << 2, + + // This means the pixels in the render target are write-only. This is used for Dawn and Metal + // swap chain targets which can be rendered to, but not read or copied. + kFramebufferOnly = 1 << 3, + + // This is a Vulkan only flag. If set the surface can be used as an input attachment in a + // shader. This is used for doing in shader blending where we want to sample from the same + // image we are drawing to. + kVkRTSupportsInputAttachment = 1 << 4, +}; + +GR_MAKE_BITFIELD_CLASS_OPS(GrInternalSurfaceFlags) + +// 'GR_MAKE_BITFIELD_CLASS_OPS' defines the & operator on GrInternalSurfaceFlags to return bool. +// We want to find the bitwise & with these masks, so we declare them as ints. +constexpr static int kGrInternalTextureFlagsMask = static_cast( + GrInternalSurfaceFlags::kReadOnly); + +// We don't include kVkRTSupportsInputAttachment in this mask since we check it manually. We don't +// require that both the surface and proxy have matching values for this flag. Instead we require +// if the proxy has it set then the surface must also have it set. All other flags listed here must +// match on the proxy and surface. +// TODO: Add back kFramebufferOnly flag here once we update GrSurfaceCharacterization to take it +// as a flag. skbug.com/10672 +constexpr static int kGrInternalRenderTargetFlagsMask = static_cast( + GrInternalSurfaceFlags::kGLRTFBOIDIs0 | + GrInternalSurfaceFlags::kRequiresManualMSAAResolve/* | + GrInternalSurfaceFlags::kFramebufferOnly*/); + +constexpr static int kGrInternalTextureRenderTargetFlagsMask = + kGrInternalTextureFlagsMask | kGrInternalRenderTargetFlagsMask; + +#ifdef SK_DEBUG +// Takes a pointer to a GrCaps, and will suppress prints if required +#define GrCapsDebugf(caps, ...) if (!(caps)->suppressPrints()) SkDebugf(__VA_ARGS__) +#else +#define GrCapsDebugf(caps, ...) do {} while (0) +#endif + +/** + * Specifies if the holder owns the backend, OpenGL or Vulkan, object. + */ +enum class GrBackendObjectOwnership : bool { + /** Holder does not destroy the backend object. */ + kBorrowed = false, + /** Holder destroys the backend object. */ + kOwned = true +}; + +/** + * Used to include or exclude specific GPU path renderers for testing purposes. + */ +enum class GpuPathRenderers { + kNone = 0, // Always use software masks and/or DefaultPathRenderer. + kDashLine = 1 << 0, + kAtlas = 1 << 1, + kTessellation = 1 << 2, + kCoverageCounting = 1 << 3, + kAAHairline = 1 << 4, + kAAConvex = 1 << 5, + kAALinearizing = 1 << 6, + kSmall = 1 << 7, + kTriangulating = 1 << 8, + kDefault = ((1 << 9) - 1) // All path renderers. +}; + +/** + * Used to describe the current state of Mips on a GrTexture + */ +enum class GrMipmapStatus { + kNotAllocated, // Mips have not been allocated + kDirty, // Mips are allocated but the full mip tree does not have valid data + kValid, // All levels fully allocated and have valid data in them +}; + +GR_MAKE_BITFIELD_CLASS_OPS(GpuPathRenderers) + +/** + * Like SkColorType this describes a layout of pixel data in CPU memory. It specifies the channels, + * their type, and width. This exists so that the GPU backend can have private types that have no + * analog in the public facing SkColorType enum and omit types not implemented in the GPU backend. + * It does not refer to a texture format and the mapping to texture formats may be many-to-many. + * It does not specify the sRGB encoding of the stored values. The components are listed in order of + * where they appear in memory. In other words the first component listed is in the low bits and + * the last component in the high bits. + */ +enum class GrColorType { + kUnknown, + kAlpha_8, + kBGR_565, + kRGB_565, + kABGR_4444, // This name differs from SkColorType. kARGB_4444_SkColorType is misnamed. + kRGBA_8888, + kRGBA_8888_SRGB, + kRGB_888x, + kRG_88, + kBGRA_8888, + kRGBA_1010102, + kBGRA_1010102, + kRGBA_10x6, + kGray_8, + kGrayAlpha_88, + kAlpha_F16, + kRGBA_F16, + kRGBA_F16_Clamped, + kRGBA_F32, + + kAlpha_16, + kRG_1616, + kRG_F16, + kRGBA_16161616, + + // Unusual types that come up after reading back in cases where we are reassigning the meaning + // of a texture format's channels to use for a particular color format but have to read back the + // data to a full RGBA quadruple. (e.g. using a R8 texture format as A8 color type but the API + // only supports reading to RGBA8.) None of these have SkColorType equivalents. + kAlpha_8xxx, + kAlpha_F32xxx, + kGray_8xxx, + kR_8xxx, + + // Types used to initialize backend textures. + kRGB_888, + kR_8, + kR_16, + kR_F16, + kGray_F16, + kBGRA_4444, + kARGB_4444, + + kLast = kARGB_4444 +}; + +static const int kGrColorTypeCnt = static_cast(GrColorType::kLast) + 1; + +static constexpr SkColorType GrColorTypeToSkColorType(GrColorType ct) { + switch (ct) { + case GrColorType::kUnknown: return kUnknown_SkColorType; + case GrColorType::kAlpha_8: return kAlpha_8_SkColorType; + case GrColorType::kBGR_565: return kRGB_565_SkColorType; + case GrColorType::kRGB_565: return kUnknown_SkColorType; + case GrColorType::kABGR_4444: return kARGB_4444_SkColorType; + case GrColorType::kRGBA_8888: return kRGBA_8888_SkColorType; + case GrColorType::kRGBA_8888_SRGB: return kSRGBA_8888_SkColorType; + case GrColorType::kRGB_888x: return kRGB_888x_SkColorType; + case GrColorType::kRG_88: return kR8G8_unorm_SkColorType; + case GrColorType::kBGRA_8888: return kBGRA_8888_SkColorType; + case GrColorType::kRGBA_1010102: return kRGBA_1010102_SkColorType; + case GrColorType::kBGRA_1010102: return kBGRA_1010102_SkColorType; + case GrColorType::kRGBA_10x6: return kRGBA_10x6_SkColorType; + case GrColorType::kGray_8: return kGray_8_SkColorType; + case GrColorType::kGrayAlpha_88: return kUnknown_SkColorType; + case GrColorType::kAlpha_F16: return kA16_float_SkColorType; + case GrColorType::kRGBA_F16: return kRGBA_F16_SkColorType; + case GrColorType::kRGBA_F16_Clamped: return kRGBA_F16Norm_SkColorType; + case GrColorType::kRGBA_F32: return kRGBA_F32_SkColorType; + case GrColorType::kAlpha_8xxx: return kUnknown_SkColorType; + case GrColorType::kAlpha_F32xxx: return kUnknown_SkColorType; + case GrColorType::kGray_8xxx: return kUnknown_SkColorType; + case GrColorType::kR_8xxx: return kUnknown_SkColorType; + case GrColorType::kAlpha_16: return kA16_unorm_SkColorType; + case GrColorType::kRG_1616: return kR16G16_unorm_SkColorType; + case GrColorType::kRGBA_16161616: return kR16G16B16A16_unorm_SkColorType; + case GrColorType::kRG_F16: return kR16G16_float_SkColorType; + case GrColorType::kRGB_888: return kUnknown_SkColorType; + case GrColorType::kR_8: return kR8_unorm_SkColorType; + case GrColorType::kR_16: return kUnknown_SkColorType; + case GrColorType::kR_F16: return kUnknown_SkColorType; + case GrColorType::kGray_F16: return kUnknown_SkColorType; + case GrColorType::kARGB_4444: return kUnknown_SkColorType; + case GrColorType::kBGRA_4444: return kUnknown_SkColorType; + } + SkUNREACHABLE; +} + +static constexpr GrColorType SkColorTypeToGrColorType(SkColorType ct) { + switch (ct) { + case kUnknown_SkColorType: return GrColorType::kUnknown; + case kAlpha_8_SkColorType: return GrColorType::kAlpha_8; + case kRGB_565_SkColorType: return GrColorType::kBGR_565; + case kARGB_4444_SkColorType: return GrColorType::kABGR_4444; + case kRGBA_8888_SkColorType: return GrColorType::kRGBA_8888; + case kSRGBA_8888_SkColorType: return GrColorType::kRGBA_8888_SRGB; + case kRGB_888x_SkColorType: return GrColorType::kRGB_888x; + case kBGRA_8888_SkColorType: return GrColorType::kBGRA_8888; + case kGray_8_SkColorType: return GrColorType::kGray_8; + case kRGBA_F16Norm_SkColorType: return GrColorType::kRGBA_F16_Clamped; + case kRGBA_F16_SkColorType: return GrColorType::kRGBA_F16; + case kRGBA_1010102_SkColorType: return GrColorType::kRGBA_1010102; + case kRGB_101010x_SkColorType: return GrColorType::kUnknown; + case kBGRA_1010102_SkColorType: return GrColorType::kBGRA_1010102; + case kBGR_101010x_SkColorType: return GrColorType::kUnknown; + case kBGR_101010x_XR_SkColorType: return GrColorType::kUnknown; + case kBGRA_10101010_XR_SkColorType: return GrColorType::kUnknown; + case kRGBA_10x6_SkColorType: return GrColorType::kRGBA_10x6; + case kRGBA_F32_SkColorType: return GrColorType::kRGBA_F32; + case kR8G8_unorm_SkColorType: return GrColorType::kRG_88; + case kA16_unorm_SkColorType: return GrColorType::kAlpha_16; + case kR16G16_unorm_SkColorType: return GrColorType::kRG_1616; + case kA16_float_SkColorType: return GrColorType::kAlpha_F16; + case kR16G16_float_SkColorType: return GrColorType::kRG_F16; + case kR16G16B16A16_unorm_SkColorType: return GrColorType::kRGBA_16161616; + case kR8_unorm_SkColorType: return GrColorType::kR_8; + } + SkUNREACHABLE; +} + +static constexpr uint32_t GrColorTypeChannelFlags(GrColorType ct) { + switch (ct) { + case GrColorType::kUnknown: return 0; + case GrColorType::kAlpha_8: return kAlpha_SkColorChannelFlag; + case GrColorType::kBGR_565: return kRGB_SkColorChannelFlags; + case GrColorType::kRGB_565: return kRGB_SkColorChannelFlags; + case GrColorType::kABGR_4444: return kRGBA_SkColorChannelFlags; + case GrColorType::kRGBA_8888: return kRGBA_SkColorChannelFlags; + case GrColorType::kRGBA_8888_SRGB: return kRGBA_SkColorChannelFlags; + case GrColorType::kRGB_888x: return kRGB_SkColorChannelFlags; + case GrColorType::kRG_88: return kRG_SkColorChannelFlags; + case GrColorType::kBGRA_8888: return kRGBA_SkColorChannelFlags; + case GrColorType::kRGBA_1010102: return kRGBA_SkColorChannelFlags; + case GrColorType::kBGRA_1010102: return kRGBA_SkColorChannelFlags; + case GrColorType::kRGBA_10x6: return kRGBA_SkColorChannelFlags; + case GrColorType::kGray_8: return kGray_SkColorChannelFlag; + case GrColorType::kGrayAlpha_88: return kGrayAlpha_SkColorChannelFlags; + case GrColorType::kAlpha_F16: return kAlpha_SkColorChannelFlag; + case GrColorType::kRGBA_F16: return kRGBA_SkColorChannelFlags; + case GrColorType::kRGBA_F16_Clamped: return kRGBA_SkColorChannelFlags; + case GrColorType::kRGBA_F32: return kRGBA_SkColorChannelFlags; + case GrColorType::kAlpha_8xxx: return kAlpha_SkColorChannelFlag; + case GrColorType::kAlpha_F32xxx: return kAlpha_SkColorChannelFlag; + case GrColorType::kGray_8xxx: return kGray_SkColorChannelFlag; + case GrColorType::kR_8xxx: return kRed_SkColorChannelFlag; + case GrColorType::kAlpha_16: return kAlpha_SkColorChannelFlag; + case GrColorType::kRG_1616: return kRG_SkColorChannelFlags; + case GrColorType::kRGBA_16161616: return kRGBA_SkColorChannelFlags; + case GrColorType::kRG_F16: return kRG_SkColorChannelFlags; + case GrColorType::kRGB_888: return kRGB_SkColorChannelFlags; + case GrColorType::kR_8: return kRed_SkColorChannelFlag; + case GrColorType::kR_16: return kRed_SkColorChannelFlag; + case GrColorType::kR_F16: return kRed_SkColorChannelFlag; + case GrColorType::kGray_F16: return kGray_SkColorChannelFlag; + case GrColorType::kARGB_4444: return kRGBA_SkColorChannelFlags; + case GrColorType::kBGRA_4444: return kRGBA_SkColorChannelFlags; + } + SkUNREACHABLE; +} + +/** + * Describes the encoding of channel data in a GrColorType. + */ +enum class GrColorTypeEncoding { + kUnorm, + kSRGBUnorm, + // kSnorm, + kFloat, + // kSint + // kUint +}; + +/** + * Describes a GrColorType by how many bits are used for each color component and how they are + * encoded. Currently all the non-zero channels share a single GrColorTypeEncoding. This could be + * expanded to store separate encodings and to indicate which bits belong to which components. + */ +class GrColorFormatDesc { +public: + static constexpr GrColorFormatDesc MakeRGBA(int rgba, GrColorTypeEncoding e) { + return {rgba, rgba, rgba, rgba, 0, e}; + } + + static constexpr GrColorFormatDesc MakeRGBA(int rgb, int a, GrColorTypeEncoding e) { + return {rgb, rgb, rgb, a, 0, e}; + } + + static constexpr GrColorFormatDesc MakeRGB(int rgb, GrColorTypeEncoding e) { + return {rgb, rgb, rgb, 0, 0, e}; + } + + static constexpr GrColorFormatDesc MakeRGB(int r, int g, int b, GrColorTypeEncoding e) { + return {r, g, b, 0, 0, e}; + } + + static constexpr GrColorFormatDesc MakeAlpha(int a, GrColorTypeEncoding e) { + return {0, 0, 0, a, 0, e}; + } + + static constexpr GrColorFormatDesc MakeR(int r, GrColorTypeEncoding e) { + return {r, 0, 0, 0, 0, e}; + } + + static constexpr GrColorFormatDesc MakeRG(int rg, GrColorTypeEncoding e) { + return {rg, rg, 0, 0, 0, e}; + } + + static constexpr GrColorFormatDesc MakeGray(int grayBits, GrColorTypeEncoding e) { + return {0, 0, 0, 0, grayBits, e}; + } + + static constexpr GrColorFormatDesc MakeGrayAlpha(int grayAlpha, GrColorTypeEncoding e) { + return {0, 0, 0, 0, grayAlpha, e}; + } + + static constexpr GrColorFormatDesc MakeInvalid() { return {}; } + + constexpr int r() const { return fRBits; } + constexpr int g() const { return fGBits; } + constexpr int b() const { return fBBits; } + constexpr int a() const { return fABits; } + constexpr int operator[](int c) const { + switch (c) { + case 0: return this->r(); + case 1: return this->g(); + case 2: return this->b(); + case 3: return this->a(); + } + SkUNREACHABLE; + } + + constexpr int gray() const { return fGrayBits; } + + constexpr GrColorTypeEncoding encoding() const { return fEncoding; } + +private: + int fRBits = 0; + int fGBits = 0; + int fBBits = 0; + int fABits = 0; + int fGrayBits = 0; + GrColorTypeEncoding fEncoding = GrColorTypeEncoding::kUnorm; + + constexpr GrColorFormatDesc() = default; + + constexpr GrColorFormatDesc(int r, int g, int b, int a, int gray, GrColorTypeEncoding encoding) + : fRBits(r), fGBits(g), fBBits(b), fABits(a), fGrayBits(gray), fEncoding(encoding) { + SkASSERT(r >= 0 && g >= 0 && b >= 0 && a >= 0 && gray >= 0); + SkASSERT(!gray || (!r && !g && !b)); + SkASSERT(r || g || b || a || gray); + } +}; + +static constexpr GrColorFormatDesc GrGetColorTypeDesc(GrColorType ct) { + switch (ct) { + case GrColorType::kUnknown: + return GrColorFormatDesc::MakeInvalid(); + case GrColorType::kAlpha_8: + return GrColorFormatDesc::MakeAlpha(8, GrColorTypeEncoding::kUnorm); + case GrColorType::kBGR_565: + return GrColorFormatDesc::MakeRGB(5, 6, 5, GrColorTypeEncoding::kUnorm); + case GrColorType::kRGB_565: + return GrColorFormatDesc::MakeRGB(5, 6, 5, GrColorTypeEncoding::kUnorm); + case GrColorType::kABGR_4444: + return GrColorFormatDesc::MakeRGBA(4, GrColorTypeEncoding::kUnorm); + case GrColorType::kRGBA_8888: + return GrColorFormatDesc::MakeRGBA(8, GrColorTypeEncoding::kUnorm); + case GrColorType::kRGBA_8888_SRGB: + return GrColorFormatDesc::MakeRGBA(8, GrColorTypeEncoding::kSRGBUnorm); + case GrColorType::kRGB_888x: + return GrColorFormatDesc::MakeRGB(8, GrColorTypeEncoding::kUnorm); + case GrColorType::kRG_88: + return GrColorFormatDesc::MakeRG(8, GrColorTypeEncoding::kUnorm); + case GrColorType::kBGRA_8888: + return GrColorFormatDesc::MakeRGBA(8, GrColorTypeEncoding::kUnorm); + case GrColorType::kRGBA_1010102: + return GrColorFormatDesc::MakeRGBA(10, 2, GrColorTypeEncoding::kUnorm); + case GrColorType::kBGRA_1010102: + return GrColorFormatDesc::MakeRGBA(10, 2, GrColorTypeEncoding::kUnorm); + case GrColorType::kRGBA_10x6: + return GrColorFormatDesc::MakeRGBA(10, GrColorTypeEncoding::kUnorm); + case GrColorType::kGray_8: + return GrColorFormatDesc::MakeGray(8, GrColorTypeEncoding::kUnorm); + case GrColorType::kGrayAlpha_88: + return GrColorFormatDesc::MakeGrayAlpha(8, GrColorTypeEncoding::kUnorm); + case GrColorType::kAlpha_F16: + return GrColorFormatDesc::MakeAlpha(16, GrColorTypeEncoding::kFloat); + case GrColorType::kRGBA_F16: + return GrColorFormatDesc::MakeRGBA(16, GrColorTypeEncoding::kFloat); + case GrColorType::kRGBA_F16_Clamped: + return GrColorFormatDesc::MakeRGBA(16, GrColorTypeEncoding::kFloat); + case GrColorType::kRGBA_F32: + return GrColorFormatDesc::MakeRGBA(32, GrColorTypeEncoding::kFloat); + case GrColorType::kAlpha_8xxx: + return GrColorFormatDesc::MakeAlpha(8, GrColorTypeEncoding::kUnorm); + case GrColorType::kAlpha_F32xxx: + return GrColorFormatDesc::MakeAlpha(32, GrColorTypeEncoding::kFloat); + case GrColorType::kGray_8xxx: + return GrColorFormatDesc::MakeGray(8, GrColorTypeEncoding::kUnorm); + case GrColorType::kR_8xxx: + return GrColorFormatDesc::MakeR(8, GrColorTypeEncoding::kUnorm); + case GrColorType::kAlpha_16: + return GrColorFormatDesc::MakeAlpha(16, GrColorTypeEncoding::kUnorm); + case GrColorType::kRG_1616: + return GrColorFormatDesc::MakeRG(16, GrColorTypeEncoding::kUnorm); + case GrColorType::kRGBA_16161616: + return GrColorFormatDesc::MakeRGBA(16, GrColorTypeEncoding::kUnorm); + case GrColorType::kRG_F16: + return GrColorFormatDesc::MakeRG(16, GrColorTypeEncoding::kFloat); + case GrColorType::kRGB_888: + return GrColorFormatDesc::MakeRGB(8, GrColorTypeEncoding::kUnorm); + case GrColorType::kR_8: + return GrColorFormatDesc::MakeR(8, GrColorTypeEncoding::kUnorm); + case GrColorType::kR_16: + return GrColorFormatDesc::MakeR(16, GrColorTypeEncoding::kUnorm); + case GrColorType::kR_F16: + return GrColorFormatDesc::MakeR(16, GrColorTypeEncoding::kFloat); + case GrColorType::kGray_F16: + return GrColorFormatDesc::MakeGray(16, GrColorTypeEncoding::kFloat); + case GrColorType::kARGB_4444: + return GrColorFormatDesc::MakeRGBA(4, GrColorTypeEncoding::kUnorm); + case GrColorType::kBGRA_4444: + return GrColorFormatDesc::MakeRGBA(4, GrColorTypeEncoding::kUnorm); + } + SkUNREACHABLE; +} + +static constexpr GrClampType GrColorTypeClampType(GrColorType colorType) { + if (GrGetColorTypeDesc(colorType).encoding() == GrColorTypeEncoding::kUnorm || + GrGetColorTypeDesc(colorType).encoding() == GrColorTypeEncoding::kSRGBUnorm) { + return GrClampType::kAuto; + } + return GrColorType::kRGBA_F16_Clamped == colorType ? GrClampType::kManual : GrClampType::kNone; +} + +// Consider a color type "wider" than n if it has more than n bits for any its representable +// channels. +static constexpr bool GrColorTypeIsWiderThan(GrColorType colorType, int n) { + SkASSERT(n > 0); + auto desc = GrGetColorTypeDesc(colorType); + return (desc.r() && desc.r() > n )|| + (desc.g() && desc.g() > n) || + (desc.b() && desc.b() > n) || + (desc.a() && desc.a() > n) || + (desc.gray() && desc.gray() > n); +} + +static constexpr bool GrColorTypeIsAlphaOnly(GrColorType ct) { + return GrColorTypeChannelFlags(ct) == kAlpha_SkColorChannelFlag; +} + +static constexpr bool GrColorTypeHasAlpha(GrColorType ct) { + return GrColorTypeChannelFlags(ct) & kAlpha_SkColorChannelFlag; +} + +static constexpr size_t GrColorTypeBytesPerPixel(GrColorType ct) { + switch (ct) { + case GrColorType::kUnknown: return 0; + case GrColorType::kAlpha_8: return 1; + case GrColorType::kBGR_565: return 2; + case GrColorType::kRGB_565: return 2; + case GrColorType::kABGR_4444: return 2; + case GrColorType::kRGBA_8888: return 4; + case GrColorType::kRGBA_8888_SRGB: return 4; + case GrColorType::kRGB_888x: return 4; + case GrColorType::kRG_88: return 2; + case GrColorType::kBGRA_8888: return 4; + case GrColorType::kRGBA_1010102: return 4; + case GrColorType::kBGRA_1010102: return 4; + case GrColorType::kRGBA_10x6: return 8; + case GrColorType::kGray_8: return 1; + case GrColorType::kGrayAlpha_88: return 2; + case GrColorType::kAlpha_F16: return 2; + case GrColorType::kRGBA_F16: return 8; + case GrColorType::kRGBA_F16_Clamped: return 8; + case GrColorType::kRGBA_F32: return 16; + case GrColorType::kAlpha_8xxx: return 4; + case GrColorType::kAlpha_F32xxx: return 16; + case GrColorType::kGray_8xxx: return 4; + case GrColorType::kR_8xxx: return 4; + case GrColorType::kAlpha_16: return 2; + case GrColorType::kRG_1616: return 4; + case GrColorType::kRGBA_16161616: return 8; + case GrColorType::kRG_F16: return 4; + case GrColorType::kRGB_888: return 3; + case GrColorType::kR_8: return 1; + case GrColorType::kR_16: return 2; + case GrColorType::kR_F16: return 2; + case GrColorType::kGray_F16: return 2; + case GrColorType::kARGB_4444: return 2; + case GrColorType::kBGRA_4444: return 2; + } + SkUNREACHABLE; +} + +enum class GrDstSampleFlags { + kNone = 0, + kRequiresTextureBarrier = 1 << 0, + kAsInputAttachment = 1 << 1, +}; +GR_MAKE_BITFIELD_CLASS_OPS(GrDstSampleFlags) + +using GrVisitProxyFunc = std::function; + +#if defined(SK_DEBUG) || defined(GR_TEST_UTILS) || defined(SK_ENABLE_DUMP_GPU) +static constexpr const char* GrBackendApiToStr(GrBackendApi api) { + switch (api) { + case GrBackendApi::kOpenGL: return "OpenGL"; + case GrBackendApi::kVulkan: return "Vulkan"; + case GrBackendApi::kMetal: return "Metal"; + case GrBackendApi::kDirect3D: return "Direct3D"; + case GrBackendApi::kMock: return "Mock"; + case GrBackendApi::kUnsupported: return "Unsupported"; + } + SkUNREACHABLE; +} + +static constexpr const char* GrColorTypeToStr(GrColorType ct) { + switch (ct) { + case GrColorType::kUnknown: return "kUnknown"; + case GrColorType::kAlpha_8: return "kAlpha_8"; + case GrColorType::kBGR_565: return "kBGR_565"; + case GrColorType::kRGB_565: return "kRGB_565"; + case GrColorType::kABGR_4444: return "kABGR_4444"; + case GrColorType::kRGBA_8888: return "kRGBA_8888"; + case GrColorType::kRGBA_8888_SRGB: return "kRGBA_8888_SRGB"; + case GrColorType::kRGB_888x: return "kRGB_888x"; + case GrColorType::kRG_88: return "kRG_88"; + case GrColorType::kBGRA_8888: return "kBGRA_8888"; + case GrColorType::kRGBA_1010102: return "kRGBA_1010102"; + case GrColorType::kBGRA_1010102: return "kBGRA_1010102"; + case GrColorType::kRGBA_10x6: return "kBGRA_10x6"; + case GrColorType::kGray_8: return "kGray_8"; + case GrColorType::kGrayAlpha_88: return "kGrayAlpha_88"; + case GrColorType::kAlpha_F16: return "kAlpha_F16"; + case GrColorType::kRGBA_F16: return "kRGBA_F16"; + case GrColorType::kRGBA_F16_Clamped: return "kRGBA_F16_Clamped"; + case GrColorType::kRGBA_F32: return "kRGBA_F32"; + case GrColorType::kAlpha_8xxx: return "kAlpha_8xxx"; + case GrColorType::kAlpha_F32xxx: return "kAlpha_F32xxx"; + case GrColorType::kGray_8xxx: return "kGray_8xxx"; + case GrColorType::kR_8xxx: return "kR_8xxx"; + case GrColorType::kAlpha_16: return "kAlpha_16"; + case GrColorType::kRG_1616: return "kRG_1616"; + case GrColorType::kRGBA_16161616: return "kRGBA_16161616"; + case GrColorType::kRG_F16: return "kRG_F16"; + case GrColorType::kRGB_888: return "kRGB_888"; + case GrColorType::kR_8: return "kR_8"; + case GrColorType::kR_16: return "kR_16"; + case GrColorType::kR_F16: return "kR_F16"; + case GrColorType::kGray_F16: return "kGray_F16"; + case GrColorType::kARGB_4444: return "kARGB_4444"; + case GrColorType::kBGRA_4444: return "kBGRA_4444"; + } + SkUNREACHABLE; +} + +static constexpr const char* GrSurfaceOriginToStr(GrSurfaceOrigin origin) { + switch (origin) { + case kTopLeft_GrSurfaceOrigin: return "kTopLeft"; + case kBottomLeft_GrSurfaceOrigin: return "kBottomLeft"; + } + SkUNREACHABLE; +} +#endif + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/graphite/ContextOptionsPriv.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/graphite/ContextOptionsPriv.h new file mode 100644 index 0000000000..769af79497 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/graphite/ContextOptionsPriv.h @@ -0,0 +1,70 @@ +/* + * Copyright 2023 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_ContextOptionsPriv_DEFINED +#define skgpu_graphite_ContextOptionsPriv_DEFINED + +namespace skgpu::graphite { + +/** + * Used to include or exclude a specific path rendering technique for testing purposes. + */ +enum class PathRendererStrategy { + /** + * Graphite selects the best path rendering technique for each shape. This is the default + * behavior. + */ + kDefault, + + /** + * All paths are rasterized into coverage masks using a GPU compute approach. This method + * always uses analytic anti-aliasing. + */ + kComputeAnalyticAA, + + /** + * All paths are rasterized into coverage masks using a GPU compute approach. This method + * supports 16 and 8 sample multi-sampled anti-aliasing. + */ + kComputeMSAA16, + kComputeMSAA8, + + /** + * All paths are rasterized into coverage masks using the CPU raster backend. + */ + kRasterAA, + + /** + * Render paths using tessellation and stencil-and-cover. + */ + kTessellation, +}; + +/** + * Private options that are only meant for testing within Skia's tools. + */ +struct ContextOptionsPriv { + + int fMaxTextureSizeOverride = SK_MaxS32; + + /** + * Maximum width and height of internal texture atlases. + */ + int fMaxTextureAtlasSize = 2048; + + /** + * If true, will store a pointer in Recorder that points back to the Context + * that created it. Used by readPixels() and other methods that normally require a Context. + */ + bool fStoreContextRefInRecorder = false; + + PathRendererStrategy fPathRendererStrategy = PathRendererStrategy::kDefault; +}; + +} // namespace skgpu::graphite + +#endif // skgpu_graphite_ContextOptionsPriv_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/graphite/DawnTypesPriv.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/graphite/DawnTypesPriv.h new file mode 100644 index 0000000000..c3c4aa0bbb --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/graphite/DawnTypesPriv.h @@ -0,0 +1,61 @@ +/* + * 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_DawnTypesPriv_DEFINED +#define skgpu_graphite_DawnTypesPriv_DEFINED + +#include "include/core/SkString.h" +#include "include/gpu/graphite/dawn/DawnTypes.h" + +namespace skgpu::graphite { + +struct DawnTextureSpec { + DawnTextureSpec() = default; + DawnTextureSpec(const DawnTextureInfo& info) + : fFormat(info.fFormat) + , fViewFormat(info.fViewFormat) + , fUsage(info.fUsage) + , fAspect(info.fAspect) + , fSlice(info.fSlice) {} + + bool operator==(const DawnTextureSpec& that) const { + return fUsage == that.fUsage && fFormat == that.fFormat && + fViewFormat == that.fViewFormat && fAspect == that.fAspect && + fSlice == that.fSlice; + } + + bool isCompatible(const DawnTextureSpec& that) const { + // The usages may match or the usage passed in may be a superset of the usage stored within. + // The aspect should either match the plane aspect or should be All. + return getViewFormat() == that.getViewFormat() && (fUsage & that.fUsage) == fUsage && + (fAspect == that.fAspect || fAspect == wgpu::TextureAspect::All); + } + + wgpu::TextureFormat getViewFormat() const { + return fViewFormat != wgpu::TextureFormat::Undefined ? fViewFormat : fFormat; + } + + SkString toString() const; + + wgpu::TextureFormat fFormat = wgpu::TextureFormat::Undefined; + // `fViewFormat` is always single plane format or plane view format for a multiplanar + // wgpu::Texture. + wgpu::TextureFormat fViewFormat = wgpu::TextureFormat::Undefined; + wgpu::TextureUsage fUsage = wgpu::TextureUsage::None; + wgpu::TextureAspect fAspect = wgpu::TextureAspect::All; + uint32_t fSlice = 0; +}; + +DawnTextureInfo DawnTextureSpecToTextureInfo(const DawnTextureSpec& dawnSpec, + uint32_t sampleCount, + Mipmapped mipmapped); + +DawnTextureInfo DawnTextureInfoFromWGPUTexture(WGPUTexture texture); + +} // namespace skgpu::graphite + +#endif // skgpu_graphite_DawnTypesPriv_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/graphite/MtlGraphiteTypesPriv.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/graphite/MtlGraphiteTypesPriv.h new file mode 100644 index 0000000000..56059b6a52 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/graphite/MtlGraphiteTypesPriv.h @@ -0,0 +1,95 @@ +/* + * Copyright 2021 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef skgpu_graphite_MtlGraphiteTypesPriv_DEFINED +#define skgpu_graphite_MtlGraphiteTypesPriv_DEFINED + +#include "include/core/SkString.h" +#include "include/gpu/graphite/GraphiteTypes.h" +#include "include/gpu/graphite/mtl/MtlGraphiteTypes.h" + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef __APPLE__ + +#include + +// We're using the MSL version as shorthand for the Metal SDK version here +#if defined(SK_BUILD_FOR_MAC) +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 130000 +#define SKGPU_GRAPHITE_METAL_SDK_VERSION 300 +#elif __MAC_OS_X_VERSION_MAX_ALLOWED >= 120000 +#define SKGPU_GRAPHITE_METAL_SDK_VERSION 240 +#elif __MAC_OS_X_VERSION_MAX_ALLOWED >= 110000 +#define SKGPU_GRAPHITE_METAL_SDK_VERSION 230 +#else +#error Must use at least 11.00 SDK to build Metal backend for MacOS +#endif +#else +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 160000 || __TV_OS_VERSION_MAX_ALLOWED >= 160000 +#define SKGPU_GRAPHITE_METAL_SDK_VERSION 300 +#elif __IPHONE_OS_VERSION_MAX_ALLOWED >= 150000 || __TV_OS_VERSION_MAX_ALLOWED >= 150000 +#define SKGPU_GRAPHITE_METAL_SDK_VERSION 240 +#elif __IPHONE_OS_VERSION_MAX_ALLOWED >= 140000 || __TV_OS_VERSION_MAX_ALLOWED >= 140000 +#define SKGPU_GRAPHITE_METAL_SDK_VERSION 230 +#else +#error Must use at least 14.00 SDK to build Metal backend for iOS +#endif +#endif + +#endif // __APPLE__ + +namespace skgpu::graphite { + +struct MtlTextureSpec { + MtlTextureSpec() + : fFormat(0) + , fUsage(0) + , fStorageMode(0) + , fFramebufferOnly(false) {} + MtlTextureSpec(const MtlTextureInfo& info) + : fFormat(info.fFormat) + , fUsage(info.fUsage) + , fStorageMode(info.fStorageMode) + , fFramebufferOnly(info.fFramebufferOnly) {} + + bool operator==(const MtlTextureSpec& that) const { + return fFormat == that.fFormat && + fUsage == that.fUsage && + fStorageMode == that.fStorageMode && + fFramebufferOnly == that.fFramebufferOnly; + } + + bool isCompatible(const MtlTextureSpec& that) const { + // The usages may match or the usage passed in may be a superset of the usage stored within. + return fFormat == that.fFormat && + fStorageMode == that.fStorageMode && + fFramebufferOnly == that.fFramebufferOnly && + (fUsage & that.fUsage) == fUsage; + } + + SkString toString() const { + return SkStringPrintf("format=%u,usage=0x%04X,storageMode=%u,framebufferOnly=%d", + fFormat, + fUsage, + fStorageMode, + fFramebufferOnly); + } + + MtlPixelFormat fFormat; + MtlTextureUsage fUsage; + MtlStorageMode fStorageMode; + bool fFramebufferOnly; +}; + +MtlTextureInfo MtlTextureSpecToTextureInfo(const MtlTextureSpec& mtlSpec, + uint32_t sampleCount, + Mipmapped mipmapped); + +} // namespace skgpu::graphite + +#endif // skgpu_graphite_MtlGraphiteTypesPriv_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/graphite/VulkanGraphiteTypesPriv.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/graphite/VulkanGraphiteTypesPriv.h new file mode 100644 index 0000000000..57019163b8 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/graphite/VulkanGraphiteTypesPriv.h @@ -0,0 +1,83 @@ +/* + * 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_VulkanGraphiteTypesPriv_DEFINED +#define skgpu_graphite_VulkanGraphiteTypesPriv_DEFINED + +#include "include/core/SkString.h" +#include "include/gpu/graphite/vk/VulkanGraphiteTypes.h" +#include "include/private/gpu/vk/SkiaVulkan.h" + +namespace skgpu::graphite { + +struct VulkanTextureSpec { + VulkanTextureSpec() + : fFlags(0) + , fFormat(VK_FORMAT_UNDEFINED) + , fImageTiling(VK_IMAGE_TILING_OPTIMAL) + , fImageUsageFlags(0) + , fSharingMode(VK_SHARING_MODE_EXCLUSIVE) + , fAspectMask(VK_IMAGE_ASPECT_COLOR_BIT) + , fYcbcrConversionInfo({}) {} + VulkanTextureSpec(const VulkanTextureInfo& info) + : fFlags(info.fFlags) + , fFormat(info.fFormat) + , fImageTiling(info.fImageTiling) + , fImageUsageFlags(info.fImageUsageFlags) + , fSharingMode(info.fSharingMode) + , fAspectMask(info.fAspectMask) + , fYcbcrConversionInfo(info.fYcbcrConversionInfo) {} + + bool operator==(const VulkanTextureSpec& that) const { + return fFlags == that.fFlags && + fFormat == that.fFormat && + fImageTiling == that.fImageTiling && + fImageUsageFlags == that.fImageUsageFlags && + fSharingMode == that.fSharingMode && + fAspectMask == that.fAspectMask && + fYcbcrConversionInfo == that.fYcbcrConversionInfo; + } + + bool isCompatible(const VulkanTextureSpec& that) const { + // The usages may match or the usage passed in may be a superset of the usage stored within. + return fFlags == that.fFlags && + fFormat == that.fFormat && + fImageTiling == that.fImageTiling && + fSharingMode == that.fSharingMode && + fAspectMask == that.fAspectMask && + (fImageUsageFlags & that.fImageUsageFlags) == fImageUsageFlags && + fYcbcrConversionInfo == that.fYcbcrConversionInfo; + } + + SkString toString() const { + return SkStringPrintf( + "flags=0x%08X,format=%d,imageTiling=%d,imageUsageFlags=0x%08X,sharingMode=%d," + "aspectMask=%u", + fFlags, + fFormat, + fImageTiling, + fImageUsageFlags, + fSharingMode, + fAspectMask); + } + + VkImageCreateFlags fFlags; + VkFormat fFormat; + VkImageTiling fImageTiling; + VkImageUsageFlags fImageUsageFlags; + VkSharingMode fSharingMode; + VkImageAspectFlags fAspectMask; + VulkanYcbcrConversionInfo fYcbcrConversionInfo; +}; + +VulkanTextureInfo VulkanTextureSpecToTextureInfo(const VulkanTextureSpec& vkSpec, + uint32_t sampleCount, + Mipmapped mipmapped); + +} // namespace skgpu::graphite + +#endif // skgpu_graphite_VulkanGraphiteTypesPriv_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/vk/SkiaVulkan.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/vk/SkiaVulkan.h new file mode 100644 index 0000000000..412dbf535f --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/private/gpu/vk/SkiaVulkan.h @@ -0,0 +1,36 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkiaVulkan_DEFINED +#define SkiaVulkan_DEFINED + +#include "include/core/SkTypes.h" + +// IWYU pragma: begin_exports + +#if (SKIA_IMPLEMENTATION || !defined(SK_VULKAN)) && !defined(SK_USE_EXTERNAL_VULKAN_HEADERS) +#include "include/third_party/vulkan/vulkan/vulkan_core.h" +#else +// For google3 builds we don't set SKIA_IMPLEMENTATION so we need to make sure that the vulkan +// headers stay up to date for our needs +#include +#endif + +#ifdef SK_BUILD_FOR_ANDROID +// This is needed to get android extensions for external memory +#if (SKIA_IMPLEMENTATION || !defined(SK_VULKAN)) && !defined(SK_USE_EXTERNAL_VULKAN_HEADERS) +#include "include/third_party/vulkan/vulkan/vulkan_android.h" +#else +// For google3 builds we don't set SKIA_IMPLEMENTATION so we need to make sure that the vulkan +// headers stay up to date for our needs +#include +#endif +#endif + +// IWYU pragma: end_exports + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/sksl/OWNERS b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/sksl/OWNERS new file mode 100644 index 0000000000..9e9d9bb906 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/sksl/OWNERS @@ -0,0 +1,3 @@ +# In addition to include/ owners, the following reviewers can approve changes to SkSL public API: +brianosman@google.com +johnstiles@google.com diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/sksl/SkSLDebugTrace.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/sksl/SkSLDebugTrace.h new file mode 100644 index 0000000000..9c5eafbc94 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/sksl/SkSLDebugTrace.h @@ -0,0 +1,28 @@ +/* + * Copyright 2021 Google LLC. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SKSL_DEBUG_TRACE +#define SKSL_DEBUG_TRACE + +#include "include/core/SkRefCnt.h" + +class SkWStream; + +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; +}; + +} // namespace SkSL + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/sksl/SkSLVersion.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/sksl/SkSLVersion.h new file mode 100644 index 0000000000..ad059d580e --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/sksl/SkSLVersion.h @@ -0,0 +1,27 @@ +/* + * 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 SkSLVersion_DEFINED +#define SkSLVersion_DEFINED + +namespace SkSL { + +enum class Version { + /** + * Desktop GLSL 1.10, GLSL ES 1.00, WebGL 1.0 + */ + k100, + + /** + * Desktop GLSL 3.30, GLSL ES 3.00, WebGL 2.0 + */ + k300, +}; + +} // namespace SkSL + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/svg/SkSVGCanvas.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/svg/SkSVGCanvas.h new file mode 100644 index 0000000000..d4c38ea017 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/svg/SkSVGCanvas.h @@ -0,0 +1,42 @@ +/* + * 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 SkSVGCanvas_DEFINED +#define SkSVGCanvas_DEFINED + +#include "include/core/SkTypes.h" + +#include +#include + +class SkCanvas; +class SkWStream; +struct SkRect; + +class SK_API SkSVGCanvas { +public: + enum { + kConvertTextToPaths_Flag = 0x01, // emit text as s + kNoPrettyXML_Flag = 0x02, // suppress newlines and tabs in output + kRelativePathEncoding_Flag = 0x04, // use relative commands for path encoding + }; + + /** + * Returns a new canvas that will generate SVG commands from its draw calls, and send + * them to the provided stream. Ownership of the stream is not transfered, and it must + * remain valid for the lifetime of the returned canvas. + * + * The canvas may buffer some drawing calls, so the output is not guaranteed to be valid + * or complete until the canvas instance is deleted. + * + * The 'bounds' parameter defines an initial SVG viewport (viewBox attribute on the root + * SVG element). + */ + static std::unique_ptr Make(const SkRect& bounds, SkWStream*, uint32_t flags = 0); +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkCamera.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkCamera.h new file mode 100644 index 0000000000..536691875e --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkCamera.h @@ -0,0 +1,109 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +// Inspired by Rob Johnson's most excellent QuickDraw GX sample code + +#ifndef SkCamera_DEFINED +#define SkCamera_DEFINED + +#include "include/core/SkM44.h" +#include "include/core/SkMatrix.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" +#include "include/private/base/SkNoncopyable.h" + +// NOTE -- This entire header / impl is deprecated, and will be removed from Skia soon. +// +// Skia now has support for a 4x matrix (SkM44) in SkCanvas. +// + +class SkCanvas; + +// DEPRECATED +class SkPatch3D { +public: + SkPatch3D(); + + void reset(); + void transform(const SkM44&, SkPatch3D* dst = nullptr) const; + + // dot a unit vector with the patch's normal + SkScalar dotWith(SkScalar dx, SkScalar dy, SkScalar dz) const; + SkScalar dotWith(const SkV3& v) const { + return this->dotWith(v.x, v.y, v.z); + } + + // deprecated, but still here for animator (for now) + void rotate(SkScalar /*x*/, SkScalar /*y*/, SkScalar /*z*/) {} + void rotateDegrees(SkScalar /*x*/, SkScalar /*y*/, SkScalar /*z*/) {} + +private: +public: // make public for SkDraw3D for now + SkV3 fU, fV; + SkV3 fOrigin; + + friend class SkCamera3D; +}; + +// DEPRECATED +class SkCamera3D { +public: + SkCamera3D(); + + void reset(); + void update(); + void patchToMatrix(const SkPatch3D&, SkMatrix* matrix) const; + + SkV3 fLocation; // origin of the camera's space + SkV3 fAxis; // view direction + SkV3 fZenith; // up direction + SkV3 fObserver; // eye position (may not be the same as the origin) + +private: + mutable SkMatrix fOrientation; + mutable bool fNeedToUpdate; + + void doUpdate() const; +}; + +// DEPRECATED +class SK_API Sk3DView : SkNoncopyable { +public: + Sk3DView(); + ~Sk3DView(); + + void save(); + void restore(); + + void translate(SkScalar x, SkScalar y, SkScalar z); + void rotateX(SkScalar deg); + void rotateY(SkScalar deg); + void rotateZ(SkScalar deg); + +#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK + void setCameraLocation(SkScalar x, SkScalar y, SkScalar z); + SkScalar getCameraLocationX() const; + SkScalar getCameraLocationY() const; + SkScalar getCameraLocationZ() const; +#endif + + void getMatrix(SkMatrix*) const; + void applyToCanvas(SkCanvas*) const; + + SkScalar dotWithNormal(SkScalar dx, SkScalar dy, SkScalar dz) const; + +private: + struct Rec { + Rec* fNext; + SkM44 fMatrix; + }; + Rec* fRec; + Rec fInitialRec; + SkCamera3D fCamera; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkCanvasStateUtils.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkCanvasStateUtils.h new file mode 100644 index 0000000000..0172e37931 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkCanvasStateUtils.h @@ -0,0 +1,81 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkCanvasStateUtils_DEFINED +#define SkCanvasStateUtils_DEFINED + +#include "include/core/SkTypes.h" + +#include + +class SkCanvas; +class SkCanvasState; + +/** + * A set of functions that are useful for copying the state of an SkCanvas + * across a library boundary where the Skia library on the other side of the + * boundary may be newer. The expected usage is outline below... + * + * Lib Boundary + * CaptureCanvasState(...) ||| + * SkCanvas --> SkCanvasState ||| + * ||| CreateFromCanvasState(...) + * ||| SkCanvasState --> SkCanvas` + * ||| Draw into SkCanvas` + * ||| Unref SkCanvas` + * ReleaseCanvasState(...) ||| + * + */ +class SK_API SkCanvasStateUtils { +public: + /** + * Captures the current state of the canvas into an opaque ptr that is safe + * to pass to a different instance of Skia (which may be the same version, + * or may be newer). The function will return NULL in the event that one of the + * following conditions are true. + * 1) the canvas device type is not supported (currently only raster is supported) + * 2) the canvas clip type is not supported (currently only non-AA clips are supported) + * + * It is recommended that the original canvas also not be used until all + * canvases that have been created using its captured state have been dereferenced. + * + * Finally, it is important to note that any draw filters attached to the + * canvas are NOT currently captured. + * + * @param canvas The canvas you wish to capture the current state of. + * @return NULL or an opaque ptr that can be passed to CreateFromCanvasState + * to reconstruct the canvas. The caller is responsible for calling + * ReleaseCanvasState to free the memory associated with this state. + */ + static SkCanvasState* CaptureCanvasState(SkCanvas* canvas); + + /** + * Create a new SkCanvas from the captured state of another SkCanvas. The + * function will return NULL in the event that one of the + * following conditions are true. + * 1) the captured state is in an unrecognized format + * 2) the captured canvas device type is not supported + * + * @param state Opaque object created by CaptureCanvasState. + * @return NULL or an SkCanvas* whose devices and matrix/clip state are + * identical to the captured canvas. The caller is responsible for + * calling unref on the SkCanvas. + */ + static std::unique_ptr MakeFromCanvasState(const SkCanvasState* state); + + /** + * Free the memory associated with the captured canvas state. The state + * should not be released until all SkCanvas objects created using that + * state have been dereferenced. Must be called from the same library + * instance that created the state via CaptureCanvasState. + * + * @param state The captured state you wish to dispose of. + */ + static void ReleaseCanvasState(SkCanvasState* state); +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkCustomTypeface.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkCustomTypeface.h new file mode 100644 index 0000000000..d387fb24ca --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkCustomTypeface.h @@ -0,0 +1,69 @@ +/* + * Copyright 2020 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkCustomTypeface_DEFINED +#define SkCustomTypeface_DEFINED + +#include "include/core/SkDrawable.h" +#include "include/core/SkFontMetrics.h" +#include "include/core/SkFontStyle.h" +#include "include/core/SkPath.h" +#include "include/core/SkRect.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkTypeface.h" +#include "include/core/SkTypes.h" + +#include +#include + +class SkStream; +class SkStreamAsset; +struct SkFontArguments; + +class SK_API SkCustomTypefaceBuilder { +public: + SkCustomTypefaceBuilder(); + + void setGlyph(SkGlyphID, float advance, const SkPath&); + void setGlyph(SkGlyphID, float advance, sk_sp, const SkRect& bounds); + + void setMetrics(const SkFontMetrics& fm, float scale = 1); + void setFontStyle(SkFontStyle); + + sk_sp detach(); + + static constexpr SkTypeface::FactoryId FactoryId = SkSetFourByteTag('u','s','e','r'); + static sk_sp MakeFromStream(std::unique_ptr, const SkFontArguments&); + +private: + struct GlyphRec { + // logical union + SkPath fPath; + sk_sp fDrawable; + + SkRect fBounds = {0,0,0,0}; // only used for drawable glyphs atm + float fAdvance = 0; + + bool isDrawable() const { + SkASSERT(!fDrawable || fPath.isEmpty()); + return fDrawable != nullptr; + } + }; + + std::vector fGlyphRecs; + SkFontMetrics fMetrics; + SkFontStyle fStyle; + + GlyphRec& ensureStorage(SkGlyphID); + + static sk_sp Deserialize(SkStream*); + + friend class SkTypeface; + friend class SkUserTypeface; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkEventTracer.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkEventTracer.h new file mode 100644 index 0000000000..2ec0a3b355 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkEventTracer.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2014 Google Inc. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkEventTracer_DEFINED +#define SkEventTracer_DEFINED + +// The class in this header defines the interface between Skia's internal +// tracing macros and an external entity (e.g., Chrome) that will consume them. +// Such an entity should subclass SkEventTracer and provide an instance of +// that event to SkEventTracer::SetInstance. + +// If you're looking for the tracing macros to instrument Skia itself, those +// live in src/core/SkTraceEvent.h + +#include "include/core/SkTypes.h" + +#include + +class SK_API SkEventTracer { +public: + + typedef uint64_t Handle; + + /** + * If this is the first call to SetInstance or GetInstance then the passed instance is + * installed and true is returned. Otherwise, false is returned. In either case ownership of the + * tracer is transferred and it will be deleted when no longer needed. + * + * Not deleting the tracer on process exit should not cause problems as + * the whole heap is about to go away with the process. This can also + * improve performance by reducing the amount of work needed. + * + * @param leakTracer Do not delete tracer on process exit. + */ + static bool SetInstance(SkEventTracer*, bool leakTracer = false); + + /** + * Gets the event tracer. If this is the first call to SetInstance or GetIntance then a default + * event tracer is installed and returned. + */ + static SkEventTracer* GetInstance(); + + virtual ~SkEventTracer() = default; + + // The pointer returned from GetCategoryGroupEnabled() points to a + // value with zero or more of the following bits. Used in this class only. + // The TRACE_EVENT macros should only use the value as a bool. + // These values must be in sync with macro values in trace_event.h in chromium. + enum CategoryGroupEnabledFlags { + // Category group enabled for the recording mode. + kEnabledForRecording_CategoryGroupEnabledFlags = 1 << 0, + // Category group enabled for the monitoring mode. + kEnabledForMonitoring_CategoryGroupEnabledFlags = 1 << 1, + // Category group enabled by SetEventCallbackEnabled(). + kEnabledForEventCallback_CategoryGroupEnabledFlags = 1 << 2, + }; + + virtual const uint8_t* getCategoryGroupEnabled(const char* name) = 0; + virtual const char* getCategoryGroupName(const uint8_t* categoryEnabledFlag) = 0; + + virtual SkEventTracer::Handle + addTraceEvent(char phase, + const uint8_t* categoryEnabledFlag, + const char* name, + uint64_t id, + int32_t numArgs, + const char** argNames, + const uint8_t* argTypes, + const uint64_t* argValues, + uint8_t flags) = 0; + + virtual void + updateTraceEventDuration(const uint8_t* categoryEnabledFlag, + const char* name, + SkEventTracer::Handle handle) = 0; + + // Optional method that can be implemented to allow splitting up traces into different sections. + virtual void newTracingSection(const char*) {} + +protected: + SkEventTracer() = default; + SkEventTracer(const SkEventTracer&) = delete; + SkEventTracer& operator=(const SkEventTracer&) = delete; +}; + +#endif // SkEventTracer_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkNWayCanvas.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkNWayCanvas.h new file mode 100644 index 0000000000..0332a1432b --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkNWayCanvas.h @@ -0,0 +1,123 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkNWayCanvas_DEFINED +#define SkNWayCanvas_DEFINED + +#include "include/core/SkCanvasVirtualEnforcer.h" +#include "include/core/SkColor.h" +#include "include/core/SkM44.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkSamplingOptions.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" +#include "include/private/base/SkTDArray.h" +#include "include/utils/SkNoDrawCanvas.h" + +#include + +namespace sktext { +class GlyphRunList; +} + +class SkCanvas; +class SkData; +class SkDrawable; +class SkImage; +class SkMatrix; +class SkPaint; +class SkPath; +class SkPicture; +class SkRRect; +class SkRegion; +class SkShader; +class SkTextBlob; +class SkVertices; +enum class SkBlendMode; +enum class SkClipOp; +struct SkDrawShadowRec; +struct SkPoint; +struct SkRSXform; +struct SkRect; + +namespace sktext::gpu { class Slug; } + +class SK_API SkNWayCanvas : public SkCanvasVirtualEnforcer { +public: + SkNWayCanvas(int width, int height); + ~SkNWayCanvas() override; + + virtual void addCanvas(SkCanvas*); + virtual void removeCanvas(SkCanvas*); + virtual void removeAll(); + +protected: + SkTDArray fList; + + void willSave() override; + SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override; + bool onDoSaveBehind(const SkRect*) override; + void willRestore() override; + + void didConcat44(const SkM44&) override; + void didSetM44(const SkM44&) override; + void didScale(SkScalar, SkScalar) override; + void didTranslate(SkScalar, SkScalar) override; + + void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override; + void onDrawGlyphRunList(const sktext::GlyphRunList&, const SkPaint&) override; + void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, + const SkPaint& paint) override; + void onDrawSlug(const sktext::gpu::Slug* slug, const SkPaint& paint) override; + void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], + const SkPoint texCoords[4], SkBlendMode, const SkPaint& paint) override; + + void onDrawPaint(const SkPaint&) override; + void onDrawBehind(const SkPaint&) override; + void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) override; + void onDrawRect(const SkRect&, const SkPaint&) override; + void onDrawRegion(const SkRegion&, const SkPaint&) override; + void onDrawOval(const SkRect&, const SkPaint&) override; + void onDrawArc(const SkRect&, SkScalar, SkScalar, bool, const SkPaint&) override; + void onDrawRRect(const SkRRect&, const SkPaint&) override; + void onDrawPath(const SkPath&, const SkPaint&) override; + + void onDrawImage2(const SkImage*, SkScalar, SkScalar, const SkSamplingOptions&, + const SkPaint*) override; + void onDrawImageRect2(const SkImage*, const SkRect&, const SkRect&, const SkSamplingOptions&, + const SkPaint*, SrcRectConstraint) override; + void onDrawImageLattice2(const SkImage*, const Lattice&, const SkRect&, SkFilterMode, + const SkPaint*) override; + void onDrawAtlas2(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int, + SkBlendMode, const SkSamplingOptions&, const SkRect*, const SkPaint*) override; + + void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) override; + void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override; + + void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override; + void onClipRRect(const SkRRect&, SkClipOp, ClipEdgeStyle) override; + void onClipPath(const SkPath&, SkClipOp, ClipEdgeStyle) override; + void onClipShader(sk_sp, SkClipOp) override; + void onClipRegion(const SkRegion&, SkClipOp) override; + void onResetClip() override; + + void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override; + void onDrawDrawable(SkDrawable*, const SkMatrix*) override; + void onDrawAnnotation(const SkRect&, const char[], SkData*) override; + + void onDrawEdgeAAQuad(const SkRect&, const SkPoint[4], QuadAAFlags, const SkColor4f&, + SkBlendMode) override; + void onDrawEdgeAAImageSet2(const ImageSetEntry[], int count, const SkPoint[], const SkMatrix[], + const SkSamplingOptions&,const SkPaint*, SrcRectConstraint) override; + class Iter; +private: + using INHERITED = SkCanvasVirtualEnforcer; +}; + + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkNoDrawCanvas.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkNoDrawCanvas.h new file mode 100644 index 0000000000..bcf43bec3c --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkNoDrawCanvas.h @@ -0,0 +1,78 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkNoDrawCanvas_DEFINED +#define SkNoDrawCanvas_DEFINED + +#include "include/core/SkCanvas.h" +#include "include/core/SkCanvasVirtualEnforcer.h" + +struct SkIRect; + +// SkNoDrawCanvas is a helper for SkCanvas subclasses which do not need to +// actually rasterize (e.g., analysis of the draw calls). +// +// It provides the following simplifications: +// +// * not backed by any device/pixels +// * conservative clipping (clipping calls only use rectangles) +// +class SK_API SkNoDrawCanvas : public SkCanvasVirtualEnforcer { +public: + SkNoDrawCanvas(int width, int height); + SkNoDrawCanvas(const SkIRect&); + + // Optimization to reset state to be the same as after construction. + void resetCanvas(int w, int h) { this->resetForNextPicture(SkIRect::MakeWH(w, h)); } + void resetCanvas(const SkIRect& rect) { this->resetForNextPicture(rect); } + +protected: + SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override; + bool onDoSaveBehind(const SkRect*) override; + + // No-op overrides for aborting rasterization earlier than SkNullBlitter. + void onDrawAnnotation(const SkRect&, const char[], SkData*) override {} + void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override {} + void onDrawDrawable(SkDrawable*, const SkMatrix*) override {} + void onDrawTextBlob(const SkTextBlob*, SkScalar, SkScalar, const SkPaint&) override {} + void onDrawPatch(const SkPoint[12], const SkColor[4], const SkPoint[4], SkBlendMode, + const SkPaint&) override {} + + void onDrawPaint(const SkPaint&) override {} + void onDrawBehind(const SkPaint&) override {} + void onDrawPoints(PointMode, size_t, const SkPoint[], const SkPaint&) override {} + void onDrawRect(const SkRect&, const SkPaint&) override {} + void onDrawRegion(const SkRegion&, const SkPaint&) override {} + void onDrawOval(const SkRect&, const SkPaint&) override {} + void onDrawArc(const SkRect&, SkScalar, SkScalar, bool, const SkPaint&) override {} + void onDrawRRect(const SkRRect&, const SkPaint&) override {} + void onDrawPath(const SkPath&, const SkPaint&) override {} + + void onDrawImage2(const SkImage*, SkScalar, SkScalar, const SkSamplingOptions&, + const SkPaint*) override {} + void onDrawImageRect2(const SkImage*, const SkRect&, const SkRect&, const SkSamplingOptions&, + const SkPaint*, SrcRectConstraint) override {} + void onDrawImageLattice2(const SkImage*, const Lattice&, const SkRect&, SkFilterMode, + const SkPaint*) override {} + void onDrawAtlas2(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int, + SkBlendMode, const SkSamplingOptions&, const SkRect*, const SkPaint*) override {} + + void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) override {} + void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override {} + void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override {} + + void onDrawEdgeAAQuad(const SkRect&, const SkPoint[4], QuadAAFlags, const SkColor4f&, + SkBlendMode) override {} + void onDrawEdgeAAImageSet2(const ImageSetEntry[], int, const SkPoint[], const SkMatrix[], + const SkSamplingOptions&, const SkPaint*, + SrcRectConstraint) override {} + +private: + using INHERITED = SkCanvasVirtualEnforcer; +}; + +#endif // SkNoDrawCanvas_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkNullCanvas.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkNullCanvas.h new file mode 100644 index 0000000000..a77e3e3de9 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkNullCanvas.h @@ -0,0 +1,22 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkNullCanvas_DEFINED +#define SkNullCanvas_DEFINED + +#include "include/core/SkTypes.h" + +#include + +class SkCanvas; + +/** + * Creates a canvas that draws nothing. This is useful for performance testing. + */ +SK_API std::unique_ptr SkMakeNullCanvas(); + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkOrderedFontMgr.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkOrderedFontMgr.h new file mode 100644 index 0000000000..0b686e5edc --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkOrderedFontMgr.h @@ -0,0 +1,66 @@ +/* + * Copyright 2021 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkOrderedFontMgr_DEFINED +#define SkOrderedFontMgr_DEFINED + +#include "include/core/SkFontMgr.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkTypes.h" + +#include +#include + +class SkData; +class SkFontStyle; +class SkStreamAsset; +class SkString; +class SkTypeface; +struct SkFontArguments; + +/** + * Collects an order list of other font managers, and visits them in order + * when a request to find or match is issued. + * + * Note: this explicitly fails on any attempt to Make a typeface: all of + * those requests will return null. + */ +class SK_API SkOrderedFontMgr : public SkFontMgr { +public: + SkOrderedFontMgr(); + ~SkOrderedFontMgr() override; + + void append(sk_sp); + +protected: + int onCountFamilies() const override; + void onGetFamilyName(int index, SkString* familyName) const override; + sk_sp onCreateStyleSet(int index)const override; + + sk_sp onMatchFamily(const char familyName[]) const override; + + sk_sp onMatchFamilyStyle(const char familyName[], + const SkFontStyle&) const override; + sk_sp onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle&, + const char* bcp47[], int bcp47Count, + SkUnichar character) const override; + + // Note: all of these always return null + sk_sp onMakeFromData(sk_sp, int ttcIndex) const override; + sk_sp onMakeFromStreamIndex(std::unique_ptr, + int ttcIndex) const override; + sk_sp onMakeFromStreamArgs(std::unique_ptr, + const SkFontArguments&) const override; + sk_sp onMakeFromFile(const char path[], int ttcIndex) const override; + + sk_sp onLegacyMakeTypeface(const char familyName[], SkFontStyle) const override; + +private: + std::vector> fList; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkPaintFilterCanvas.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkPaintFilterCanvas.h new file mode 100644 index 0000000000..ce86d2ea3a --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkPaintFilterCanvas.h @@ -0,0 +1,140 @@ +/* + * 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 SkPaintFilterCanvas_DEFINED +#define SkPaintFilterCanvas_DEFINED + +#include "include/core/SkCanvas.h" +#include "include/core/SkCanvasVirtualEnforcer.h" +#include "include/core/SkColor.h" +#include "include/core/SkImageInfo.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkSamplingOptions.h" +#include "include/core/SkScalar.h" +#include "include/core/SkSize.h" +#include "include/core/SkTypes.h" +#include "include/private/base/SkTDArray.h" +#include "include/utils/SkNWayCanvas.h" + +#include + +namespace sktext { +class GlyphRunList; +} + +class GrRecordingContext; +class SkData; +class SkDrawable; +class SkImage; +class SkMatrix; +class SkPaint; +class SkPath; +class SkPicture; +class SkPixmap; +class SkRRect; +class SkRegion; +class SkSurface; +class SkSurfaceProps; +class SkTextBlob; +class SkVertices; +enum class SkBlendMode; +struct SkDrawShadowRec; +struct SkPoint; +struct SkRSXform; +struct SkRect; + +/** \class SkPaintFilterCanvas + + A utility proxy base class for implementing draw/paint filters. +*/ +class SK_API SkPaintFilterCanvas : public SkCanvasVirtualEnforcer { +public: + /** + * The new SkPaintFilterCanvas is configured for forwarding to the + * specified canvas. Also copies the target canvas matrix and clip bounds. + */ + SkPaintFilterCanvas(SkCanvas* canvas); + + enum Type { + kPicture_Type, + }; + + // Forwarded to the wrapped canvas. + SkISize getBaseLayerSize() const override { return proxy()->getBaseLayerSize(); } + GrRecordingContext* recordingContext() const override { return proxy()->recordingContext(); } +protected: + /** + * Called with the paint that will be used to draw the specified type. + * The implementation may modify the paint as they wish. + * + * The result bool is used to determine whether the draw op is to be + * executed (true) or skipped (false). + * + * Note: The base implementation calls onFilter() for top-level/explicit paints only. + * To also filter encapsulated paints (e.g. SkPicture, SkTextBlob), clients may need to + * override the relevant methods (i.e. drawPicture, drawTextBlob). + */ + virtual bool onFilter(SkPaint& paint) const = 0; + + void onDrawPaint(const SkPaint&) override; + void onDrawBehind(const SkPaint&) override; + void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) override; + void onDrawRect(const SkRect&, const SkPaint&) override; + void onDrawRRect(const SkRRect&, const SkPaint&) override; + void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override; + void onDrawRegion(const SkRegion&, const SkPaint&) override; + void onDrawOval(const SkRect&, const SkPaint&) override; + void onDrawArc(const SkRect&, SkScalar, SkScalar, bool, const SkPaint&) override; + void onDrawPath(const SkPath&, const SkPaint&) override; + + void onDrawImage2(const SkImage*, SkScalar, SkScalar, const SkSamplingOptions&, + const SkPaint*) override; + void onDrawImageRect2(const SkImage*, const SkRect&, const SkRect&, const SkSamplingOptions&, + const SkPaint*, SrcRectConstraint) override; + void onDrawImageLattice2(const SkImage*, const Lattice&, const SkRect&, SkFilterMode, + const SkPaint*) override; + void onDrawAtlas2(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int, + SkBlendMode, const SkSamplingOptions&, const SkRect*, const SkPaint*) override; + + void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) override; + void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], + const SkPoint texCoords[4], SkBlendMode, + const SkPaint& paint) override; + void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override; + void onDrawDrawable(SkDrawable*, const SkMatrix*) override; + + void onDrawGlyphRunList(const sktext::GlyphRunList&, const SkPaint&) override; + void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, + const SkPaint& paint) override; + void onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) override; + void onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) override; + + void onDrawEdgeAAQuad(const SkRect&, const SkPoint[4], QuadAAFlags, const SkColor4f&, + SkBlendMode) override; + void onDrawEdgeAAImageSet2(const ImageSetEntry[], int count, const SkPoint[], const SkMatrix[], + const SkSamplingOptions&,const SkPaint*, SrcRectConstraint) override; + + // Forwarded to the wrapped canvas. + sk_sp onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override; + bool onPeekPixels(SkPixmap* pixmap) override; + bool onAccessTopLayerPixels(SkPixmap* pixmap) override; + SkImageInfo onImageInfo() const override; + bool onGetProps(SkSurfaceProps* props, bool top) const override; + +private: + class AutoPaintFilter; + + SkCanvas* proxy() const { SkASSERT(fList.size() == 1); return fList[0]; } + + SkPaintFilterCanvas* internal_private_asPaintFilterCanvas() const override { + return const_cast(this); + } + + friend class SkAndroidFrameworkUtils; +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkParse.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkParse.h new file mode 100644 index 0000000000..bcabc3c793 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkParse.h @@ -0,0 +1,37 @@ + +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#ifndef SkParse_DEFINED +#define SkParse_DEFINED + +#include "include/core/SkColor.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" + +#include +#include + +class SK_API SkParse { +public: + static int Count(const char str[]); // number of scalars or int values + static int Count(const char str[], char separator); + static const char* FindColor(const char str[], SkColor* value); + static const char* FindHex(const char str[], uint32_t* value); + static const char* FindMSec(const char str[], SkMSec* value); + static const char* FindNamedColor(const char str[], size_t len, SkColor* color); + static const char* FindS32(const char str[], int32_t* value); + static const char* FindScalar(const char str[], SkScalar* value); + static const char* FindScalars(const char str[], SkScalar value[], int count); + + static bool FindBool(const char str[], bool* value); + // return the index of str in list[], or -1 if not found + static int FindList(const char str[], const char list[]); +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkParsePath.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkParsePath.h new file mode 100644 index 0000000000..acd0ef2305 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkParsePath.h @@ -0,0 +1,25 @@ + +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#ifndef SkParsePath_DEFINED +#define SkParsePath_DEFINED + +#include "include/core/SkPath.h" + +class SkString; + +class SK_API SkParsePath { +public: + static bool FromSVGString(const char str[], SkPath*); + + enum class PathEncoding { Absolute, Relative }; + static SkString ToSVGString(const SkPath&, PathEncoding = PathEncoding::Absolute); +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkShadowUtils.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkShadowUtils.h new file mode 100644 index 0000000000..0f77b11096 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkShadowUtils.h @@ -0,0 +1,102 @@ + +/* + * 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 SkShadowUtils_DEFINED +#define SkShadowUtils_DEFINED + +#include "include/core/SkColor.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" + +#include + +class SkCanvas; +class SkMatrix; +class SkPath; +struct SkPoint3; +struct SkRect; + +enum SkShadowFlags { + kNone_ShadowFlag = 0x00, + /** The occluding object is not opaque. Knowing that the occluder is opaque allows + * us to cull shadow geometry behind it and improve performance. */ + kTransparentOccluder_ShadowFlag = 0x01, + /** Don't try to use analytic shadows. */ + kGeometricOnly_ShadowFlag = 0x02, + /** Light position represents a direction, light radius is blur radius at elevation 1 */ + kDirectionalLight_ShadowFlag = 0x04, + /** Concave paths will only use blur to generate the shadow */ + kConcaveBlurOnly_ShadowFlag = 0x08, + /** mask for all shadow flags */ + kAll_ShadowFlag = 0x0F +}; + +class SK_API SkShadowUtils { +public: + /** + * Draw an offset spot shadow and outlining ambient shadow for the given path using a disc + * light. The shadow may be cached, depending on the path type and canvas matrix. If the + * matrix is perspective or the path is volatile, it will not be cached. + * + * @param canvas The canvas on which to draw the shadows. + * @param path The occluder used to generate the shadows. + * @param zPlaneParams Values for the plane function which returns the Z offset of the + * occluder from the canvas based on local x and y values (the current matrix is not applied). + * @param lightPos Generally, the 3D position of the light relative to the canvas plane. + * If kDirectionalLight_ShadowFlag is set, this specifies a vector pointing + * towards the light. + * @param lightRadius Generally, the radius of the disc light. + * If DirectionalLight_ShadowFlag is set, this specifies the amount of + * blur when the occluder is at Z offset == 1. The blur will grow linearly + * as the Z value increases. + * @param ambientColor The color of the ambient shadow. + * @param spotColor The color of the spot shadow. + * @param flags Options controlling opaque occluder optimizations, shadow appearance, + * and light position. See SkShadowFlags. + */ + static void DrawShadow(SkCanvas* canvas, const SkPath& path, const SkPoint3& zPlaneParams, + const SkPoint3& lightPos, SkScalar lightRadius, + SkColor ambientColor, SkColor spotColor, + uint32_t flags = SkShadowFlags::kNone_ShadowFlag); + + /** + * Generate bounding box for shadows relative to path. Includes both the ambient and spot + * shadow bounds. + * + * @param ctm Current transformation matrix to device space. + * @param path The occluder used to generate the shadows. + * @param zPlaneParams Values for the plane function which returns the Z offset of the + * occluder from the canvas based on local x and y values (the current matrix is not applied). + * @param lightPos Generally, the 3D position of the light relative to the canvas plane. + * If kDirectionalLight_ShadowFlag is set, this specifies a vector pointing + * towards the light. + * @param lightRadius Generally, the radius of the disc light. + * If DirectionalLight_ShadowFlag is set, this specifies the amount of + * blur when the occluder is at Z offset == 1. The blur will grow linearly + * as the Z value increases. + * @param flags Options controlling opaque occluder optimizations, shadow appearance, + * and light position. See SkShadowFlags. + * @param bounds Return value for shadow bounding box. + * @return Returns true if successful, false otherwise. + */ + static bool GetLocalBounds(const SkMatrix& ctm, const SkPath& path, + const SkPoint3& zPlaneParams, const SkPoint3& lightPos, + SkScalar lightRadius, uint32_t flags, SkRect* bounds); + + /** + * Helper routine to compute color values for one-pass tonal alpha. + * + * @param inAmbientColor Original ambient color + * @param inSpotColor Original spot color + * @param outAmbientColor Modified ambient color + * @param outSpotColor Modified spot color + */ + static void ComputeTonalColors(SkColor inAmbientColor, SkColor inSpotColor, + SkColor* outAmbientColor, SkColor* outSpotColor); +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkTextUtils.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkTextUtils.h new file mode 100644 index 0000000000..06f83b934f --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkTextUtils.h @@ -0,0 +1,43 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkTextUtils_DEFINED +#define SkTextUtils_DEFINED + +#include "include/core/SkFontTypes.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" + +#include +#include + +class SkCanvas; +class SkFont; +class SkPaint; +class SkPath; + +class SK_API SkTextUtils { +public: + enum Align { + kLeft_Align, + kCenter_Align, + kRight_Align, + }; + + static void Draw(SkCanvas*, const void* text, size_t size, SkTextEncoding, + SkScalar x, SkScalar y, const SkFont&, const SkPaint&, Align = kLeft_Align); + + static void DrawString(SkCanvas* canvas, const char text[], SkScalar x, SkScalar y, + const SkFont& font, const SkPaint& paint, Align align = kLeft_Align) { + Draw(canvas, text, strlen(text), SkTextEncoding::kUTF8, x, y, font, paint, align); + } + + static void GetPath(const void* text, size_t length, SkTextEncoding, SkScalar x, SkScalar y, + const SkFont&, SkPath*); +}; + +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkTraceEventPhase.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkTraceEventPhase.h new file mode 100644 index 0000000000..38457be24b --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/SkTraceEventPhase.h @@ -0,0 +1,19 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#ifndef SkTraceEventPhase_DEFINED +#define SkTraceEventPhase_DEFINED + +// Phase indicates the nature of an event entry. E.g. part of a begin/end pair. +#define TRACE_EVENT_PHASE_BEGIN ('B') +#define TRACE_EVENT_PHASE_END ('E') +#define TRACE_EVENT_PHASE_COMPLETE ('X') +#define TRACE_EVENT_PHASE_INSTANT ('I') +#define TRACE_EVENT_PHASE_ASYNC_BEGIN ('S') +#define TRACE_EVENT_PHASE_ASYNC_END ('F') +#define TRACE_EVENT_PHASE_COUNTER ('C') +#define TRACE_EVENT_PHASE_CREATE_OBJECT ('N') +#define TRACE_EVENT_PHASE_SNAPSHOT_OBJECT ('O') +#define TRACE_EVENT_PHASE_DELETE_OBJECT ('D') + +#endif // SkTraceEventPhase_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/mac/SkCGUtils.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/mac/SkCGUtils.h new file mode 100644 index 0000000000..73d89c174a --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/include/utils/mac/SkCGUtils.h @@ -0,0 +1,90 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SkCGUtils_DEFINED +#define SkCGUtils_DEFINED + +#include "include/core/SkImage.h" +#include "include/core/SkImageInfo.h" +#include "include/core/SkPixmap.h" +#include "include/core/SkSize.h" + +#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) + +#ifdef SK_BUILD_FOR_MAC +#include +#endif + +#ifdef SK_BUILD_FOR_IOS +#include +#endif + +class SkBitmap; +class SkColorSpace; +class SkData; +class SkPixmap; +class SkStreamRewindable; + +SK_API CGContextRef SkCreateCGContext(const SkPixmap&); + +/** + * Given a CGImage, allocate an SkBitmap and copy the image's pixels into it. If scaleToFit is not + * null, use it to determine the size of the bitmap, and scale the image to fill the bitmap. + * Otherwise use the image's width/height. + * + * On failure, return false, and leave bitmap unchanged. + */ +SK_API bool SkCreateBitmapFromCGImage(SkBitmap* dst, CGImageRef src); + +SK_API sk_sp SkMakeImageFromCGImage(CGImageRef); + +/** + * Given a CGColorSpace, return the closest matching SkColorSpace. If no conversion is possible + * or if the input CGColorSpace is nullptr then return nullptr. + */ +SK_API sk_sp SkMakeColorSpaceFromCGColorSpace(CGColorSpaceRef); + +/** + * Copy the pixels from src into the memory specified by info/rowBytes/dstPixels. On failure, + * return false (e.g. ImageInfo incompatible with src). + */ +SK_API bool SkCopyPixelsFromCGImage(const SkImageInfo& info, size_t rowBytes, void* dstPixels, + CGImageRef src); +static inline bool SkCopyPixelsFromCGImage(const SkPixmap& dst, CGImageRef src) { + return SkCopyPixelsFromCGImage(dst.info(), dst.rowBytes(), dst.writable_addr(), src); +} + +/** + * Create an imageref from the specified bitmap. The color space parameter is ignored. + */ +SK_API CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm, + CGColorSpaceRef space); + +/** + * Create an imageref from the specified bitmap. + */ +SK_API CGImageRef SkCreateCGImageRef(const SkBitmap& bm); + +/** + * Given an SkColorSpace, create a CGColorSpace. This will return sRGB if the specified + * SkColorSpace is nullptr or on failure. This will not retain the specified SkColorSpace. + */ +SK_API CGColorSpaceRef SkCreateCGColorSpace(const SkColorSpace*); + +/** + * Given an SkData, create a CGDataProviderRef that refers to the and retains the specified data. + */ +SK_API CGDataProviderRef SkCreateCGDataProvider(sk_sp); + +/** + * Draw the bitmap into the specified CG context. (x,y) specifies the position of the top-left + * corner of the bitmap. + */ +void SkCGDrawBitmap(CGContextRef, const SkBitmap&, float x, float y); + +#endif // defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) +#endif // SkCGUtils_DEFINED diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/BUILD.gn b/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/BUILD.gn new file mode 100644 index 0000000000..5d037a94c9 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/BUILD.gn @@ -0,0 +1,100 @@ +# Copyright 2022 Google LLC +# +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("skcms.gni") + +# Use for CPU-specific skcms transform code that needs particular compiler flags. +# (This is patterned after `opts` in Skia's BUILD.gn.) +template("arch") { + if (invoker.enabled) { + source_set(target_name) { + visibility = [ ":*" ] + check_includes = false + forward_variables_from(invoker, "*") + } + } else { + # If not enabled, a phony empty target that swallows all otherwise unused variables. + source_set(target_name) { + visibility = [ ":*" ] + check_includes = false + forward_variables_from(invoker, + "*", + [ + "sources", + "cflags", + "defines", + ]) + } + } +} + +arch("skcms_TransformHsw") { + enabled = current_cpu == "x64" && target_os != "android" + sources = skcms_TransformHsw + if (is_win) { + if (is_clang) { + cflags = [ + "/clang:-mavx2", + "/clang:-mf16c", + "/clang:-ffp-contract=off", + ] + } else { + cflags = [ "/arch:AVX2" ] + } + } else { + cflags = [ + "-mavx2", + "-mf16c", + "-std=c11", + ] + } +} + +arch("skcms_TransformSkx") { + enabled = current_cpu == "x64" && target_os != "android" + sources = skcms_TransformSkx + if (is_win) { + if (is_clang) { + cflags = [ + "/clang:-mavx512f", + "/clang:-mavx512dq", + "/clang:-mavx512cd", + "/clang:-mavx512bw", + "/clang:-mavx512vl", + "/clang:-ffp-contract=off", + ] + } else { + cflags = [ "/arch:AVX512" ] + } + } else { + cflags = [ + "-mavx512f", + "-mavx512dq", + "-mavx512cd", + "-mavx512bw", + "-mavx512vl", + "-std=c11", + ] + } +} + +static_library("skcms") { + cflags = [] + if (!is_win || is_clang) { + cflags += [ "-std=c11" ] + } + if (target_cpu != "x64" || target_os == "android") { + defines = [ + "SKCMS_DISABLE_HSW", + "SKCMS_DISABLE_SKX", + ] + } + public = skcms_public_headers + sources = skcms_public + skcms_TransformBaseline + deps = [ + ":skcms_TransformHsw", + ":skcms_TransformSkx", + ] +} diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/OWNERS b/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/OWNERS new file mode 100644 index 0000000000..cc36d27e3d --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/OWNERS @@ -0,0 +1,2 @@ +# The auto-roller directly checks in skcms, so give it ownership as well: +skia-autoroll@skia-public.iam.gserviceaccount.com diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/README.chromium b/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/README.chromium new file mode 100644 index 0000000000..15543c64fd --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/README.chromium @@ -0,0 +1,6 @@ +Name: skcms +URL: https://skia.org/ +Version: unknown +Security Critical: yes +Shipped: yes +License: BSD diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/skcms.cc b/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/skcms.cc new file mode 100644 index 0000000000..047f21bed1 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/skcms.cc @@ -0,0 +1,2889 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "src/skcms_public.h" // NO_G3_REWRITE +#include "src/skcms_internals.h" // NO_G3_REWRITE +#include "src/skcms_Transform.h" // NO_G3_REWRITE +#include +#include +#include +#include +#include + +#if defined(__ARM_NEON) + #include +#elif defined(__SSE__) + #include + + #if defined(__clang__) + // That #include is usually enough, but Clang's headers + // "helpfully" skip including the whole kitchen sink when _MSC_VER is + // defined, because lots of programs on Windows would include that and + // it'd be a lot slower. But we want all those headers included so we + // can use their features after runtime checks later. + #include + #include + #include + #include + #include + #endif +#endif + +using namespace skcms_private; + +static bool sAllowRuntimeCPUDetection = true; + +void skcms_DisableRuntimeCPUDetection() { + sAllowRuntimeCPUDetection = false; +} + +static float log2f_(float x) { + // The first approximation of log2(x) is its exponent 'e', minus 127. + int32_t bits; + memcpy(&bits, &x, sizeof(bits)); + + float e = (float)bits * (1.0f / (1<<23)); + + // If we use the mantissa too we can refine the error signficantly. + int32_t m_bits = (bits & 0x007fffff) | 0x3f000000; + float m; + memcpy(&m, &m_bits, sizeof(m)); + + return (e - 124.225514990f + - 1.498030302f*m + - 1.725879990f/(0.3520887068f + m)); +} +static float logf_(float x) { + const float ln2 = 0.69314718f; + return ln2*log2f_(x); +} + +static float exp2f_(float x) { + if (x > 128.0f) { + return INFINITY_; + } else if (x < -127.0f) { + return 0.0f; + } + float fract = x - floorf_(x); + + float fbits = (1.0f * (1<<23)) * (x + 121.274057500f + - 1.490129070f*fract + + 27.728023300f/(4.84252568f - fract)); + + // Before we cast fbits to int32_t, check for out of range values to pacify UBSAN. + // INT_MAX is not exactly representable as a float, so exclude it as effectively infinite. + // Negative values are effectively underflow - we'll end up returning a (different) negative + // value, which makes no sense. So clamp to zero. + if (fbits >= (float)INT_MAX) { + return INFINITY_; + } else if (fbits < 0) { + return 0; + } + + int32_t bits = (int32_t)fbits; + memcpy(&x, &bits, sizeof(x)); + return x; +} + +// Not static, as it's used by some test tools. +float powf_(float x, float y) { + if (x <= 0.f) { + return 0.f; + } + if (x == 1.f) { + return 1.f; + } + return exp2f_(log2f_(x) * y); +} + +static float expf_(float x) { + const float log2_e = 1.4426950408889634074f; + return exp2f_(log2_e * x); +} + +static float fmaxf_(float x, float y) { return x > y ? x : y; } +static float fminf_(float x, float y) { return x < y ? x : y; } + +static bool isfinitef_(float x) { return 0 == x*0; } + +static float minus_1_ulp(float x) { + int32_t bits; + memcpy(&bits, &x, sizeof(bits)); + bits = bits - 1; + memcpy(&x, &bits, sizeof(bits)); + return x; +} + +// Most transfer functions we work with are sRGBish. +// For exotic HDR transfer functions, we encode them using a tf.g that makes no sense, +// and repurpose the other fields to hold the parameters of the HDR functions. +struct TF_PQish { float A,B,C,D,E,F; }; +struct TF_HLGish { float R,G,a,b,c,K_minus_1; }; +// We didn't originally support a scale factor K for HLG, and instead just stored 0 in +// the unused `f` field of skcms_TransferFunction for HLGish and HLGInvish transfer functions. +// By storing f=K-1, those old unusued f=0 values now mean K=1, a noop scale factor. + +static float TFKind_marker(skcms_TFType kind) { + // We'd use different NaNs, but those aren't guaranteed to be preserved by WASM. + return -(float)kind; +} + +static skcms_TFType classify(const skcms_TransferFunction& tf, TF_PQish* pq = nullptr + , TF_HLGish* hlg = nullptr) { + if (tf.g < 0) { + // Negative "g" is mapped to enum values; large negative are for sure invalid. + if (tf.g < -128) { + return skcms_TFType_Invalid; + } + int enum_g = -static_cast(tf.g); + // Non-whole "g" values are invalid as well. + if (static_cast(-enum_g) != tf.g) { + return skcms_TFType_Invalid; + } + // TODO: soundness checks for PQ/HLG like we do for sRGBish? + switch (enum_g) { + case skcms_TFType_PQish: + if (pq) { + memcpy(pq , &tf.a, sizeof(*pq )); + } + return skcms_TFType_PQish; + case skcms_TFType_HLGish: + if (hlg) { + memcpy(hlg, &tf.a, sizeof(*hlg)); + } + return skcms_TFType_HLGish; + case skcms_TFType_HLGinvish: + if (hlg) { + memcpy(hlg, &tf.a, sizeof(*hlg)); + } + return skcms_TFType_HLGinvish; + } + return skcms_TFType_Invalid; + } + + // Basic soundness checks for sRGBish transfer functions. + if (isfinitef_(tf.a + tf.b + tf.c + tf.d + tf.e + tf.f + tf.g) + // a,c,d,g should be non-negative to make any sense. + && tf.a >= 0 + && tf.c >= 0 + && tf.d >= 0 + && tf.g >= 0 + // Raising a negative value to a fractional tf->g produces complex numbers. + && tf.a * tf.d + tf.b >= 0) { + return skcms_TFType_sRGBish; + } + + return skcms_TFType_Invalid; +} + +skcms_TFType skcms_TransferFunction_getType(const skcms_TransferFunction* tf) { + return classify(*tf); +} +bool skcms_TransferFunction_isSRGBish(const skcms_TransferFunction* tf) { + return classify(*tf) == skcms_TFType_sRGBish; +} +bool skcms_TransferFunction_isPQish(const skcms_TransferFunction* tf) { + return classify(*tf) == skcms_TFType_PQish; +} +bool skcms_TransferFunction_isHLGish(const skcms_TransferFunction* tf) { + return classify(*tf) == skcms_TFType_HLGish; +} + +bool skcms_TransferFunction_makePQish(skcms_TransferFunction* tf, + float A, float B, float C, + float D, float E, float F) { + *tf = { TFKind_marker(skcms_TFType_PQish), A,B,C,D,E,F }; + assert(skcms_TransferFunction_isPQish(tf)); + return true; +} + +bool skcms_TransferFunction_makeScaledHLGish(skcms_TransferFunction* tf, + float K, float R, float G, + float a, float b, float c) { + *tf = { TFKind_marker(skcms_TFType_HLGish), R,G, a,b,c, K-1.0f }; + assert(skcms_TransferFunction_isHLGish(tf)); + return true; +} + +float skcms_TransferFunction_eval(const skcms_TransferFunction* tf, float x) { + float sign = x < 0 ? -1.0f : 1.0f; + x *= sign; + + TF_PQish pq; + TF_HLGish hlg; + switch (classify(*tf, &pq, &hlg)) { + case skcms_TFType_Invalid: break; + + case skcms_TFType_HLGish: { + const float K = hlg.K_minus_1 + 1.0f; + return K * sign * (x*hlg.R <= 1 ? powf_(x*hlg.R, hlg.G) + : expf_((x-hlg.c)*hlg.a) + hlg.b); + } + + // skcms_TransferFunction_invert() inverts R, G, and a for HLGinvish so this math is fast. + case skcms_TFType_HLGinvish: { + const float K = hlg.K_minus_1 + 1.0f; + x /= K; + return sign * (x <= 1 ? hlg.R * powf_(x, hlg.G) + : hlg.a * logf_(x - hlg.b) + hlg.c); + } + + case skcms_TFType_sRGBish: + return sign * (x < tf->d ? tf->c * x + tf->f + : powf_(tf->a * x + tf->b, tf->g) + tf->e); + + case skcms_TFType_PQish: + return sign * + powf_((pq.A + pq.B * powf_(x, pq.C)) / (pq.D + pq.E * powf_(x, pq.C)), pq.F); + } + return 0; +} + + +static float eval_curve(const skcms_Curve* curve, float x) { + if (curve->table_entries == 0) { + return skcms_TransferFunction_eval(&curve->parametric, x); + } + + float ix = fmaxf_(0, fminf_(x, 1)) * static_cast(curve->table_entries - 1); + int lo = (int) ix , + hi = (int)(float)minus_1_ulp(ix + 1.0f); + float t = ix - (float)lo; + + float l, h; + if (curve->table_8) { + l = curve->table_8[lo] * (1/255.0f); + h = curve->table_8[hi] * (1/255.0f); + } else { + uint16_t be_l, be_h; + memcpy(&be_l, curve->table_16 + 2*lo, 2); + memcpy(&be_h, curve->table_16 + 2*hi, 2); + uint16_t le_l = ((be_l << 8) | (be_l >> 8)) & 0xffff; + uint16_t le_h = ((be_h << 8) | (be_h >> 8)) & 0xffff; + l = le_l * (1/65535.0f); + h = le_h * (1/65535.0f); + } + return l + (h-l)*t; +} + +float skcms_MaxRoundtripError(const skcms_Curve* curve, const skcms_TransferFunction* inv_tf) { + uint32_t N = curve->table_entries > 256 ? curve->table_entries : 256; + const float dx = 1.0f / static_cast(N - 1); + float err = 0; + for (uint32_t i = 0; i < N; i++) { + float x = static_cast(i) * dx, + y = eval_curve(curve, x); + err = fmaxf_(err, fabsf_(x - skcms_TransferFunction_eval(inv_tf, y))); + } + return err; +} + +bool skcms_AreApproximateInverses(const skcms_Curve* curve, const skcms_TransferFunction* inv_tf) { + return skcms_MaxRoundtripError(curve, inv_tf) < (1/512.0f); +} + +// Additional ICC signature values that are only used internally +enum { + // File signature + skcms_Signature_acsp = 0x61637370, + + // Tag signatures + skcms_Signature_rTRC = 0x72545243, + skcms_Signature_gTRC = 0x67545243, + skcms_Signature_bTRC = 0x62545243, + skcms_Signature_kTRC = 0x6B545243, + + skcms_Signature_rXYZ = 0x7258595A, + skcms_Signature_gXYZ = 0x6758595A, + skcms_Signature_bXYZ = 0x6258595A, + + skcms_Signature_A2B0 = 0x41324230, + skcms_Signature_B2A0 = 0x42324130, + + skcms_Signature_CHAD = 0x63686164, + skcms_Signature_WTPT = 0x77747074, + + skcms_Signature_CICP = 0x63696370, + + // Type signatures + skcms_Signature_curv = 0x63757276, + skcms_Signature_mft1 = 0x6D667431, + skcms_Signature_mft2 = 0x6D667432, + skcms_Signature_mAB = 0x6D414220, + skcms_Signature_mBA = 0x6D424120, + skcms_Signature_para = 0x70617261, + skcms_Signature_sf32 = 0x73663332, + // XYZ is also a PCS signature, so it's defined in skcms.h + // skcms_Signature_XYZ = 0x58595A20, +}; + +static uint16_t read_big_u16(const uint8_t* ptr) { + uint16_t be; + memcpy(&be, ptr, sizeof(be)); +#if defined(_MSC_VER) + return _byteswap_ushort(be); +#else + return __builtin_bswap16(be); +#endif +} + +static uint32_t read_big_u32(const uint8_t* ptr) { + uint32_t be; + memcpy(&be, ptr, sizeof(be)); +#if defined(_MSC_VER) + return _byteswap_ulong(be); +#else + return __builtin_bswap32(be); +#endif +} + +static int32_t read_big_i32(const uint8_t* ptr) { + return (int32_t)read_big_u32(ptr); +} + +static float read_big_fixed(const uint8_t* ptr) { + return static_cast(read_big_i32(ptr)) * (1.0f / 65536.0f); +} + +// Maps to an in-memory profile so that fields line up to the locations specified +// in ICC.1:2010, section 7.2 +typedef struct { + uint8_t size [ 4]; + uint8_t cmm_type [ 4]; + uint8_t version [ 4]; + uint8_t profile_class [ 4]; + uint8_t data_color_space [ 4]; + uint8_t pcs [ 4]; + uint8_t creation_date_time [12]; + uint8_t signature [ 4]; + uint8_t platform [ 4]; + uint8_t flags [ 4]; + uint8_t device_manufacturer [ 4]; + uint8_t device_model [ 4]; + uint8_t device_attributes [ 8]; + uint8_t rendering_intent [ 4]; + uint8_t illuminant_X [ 4]; + uint8_t illuminant_Y [ 4]; + uint8_t illuminant_Z [ 4]; + uint8_t creator [ 4]; + uint8_t profile_id [16]; + uint8_t reserved [28]; + uint8_t tag_count [ 4]; // Technically not part of header, but required +} header_Layout; + +typedef struct { + uint8_t signature [4]; + uint8_t offset [4]; + uint8_t size [4]; +} tag_Layout; + +static const tag_Layout* get_tag_table(const skcms_ICCProfile* profile) { + return (const tag_Layout*)(profile->buffer + SAFE_SIZEOF(header_Layout)); +} + +// s15Fixed16ArrayType is technically variable sized, holding N values. However, the only valid +// use of the type is for the CHAD tag that stores exactly nine values. +typedef struct { + uint8_t type [ 4]; + uint8_t reserved [ 4]; + uint8_t values [36]; +} sf32_Layout; + +bool skcms_GetCHAD(const skcms_ICCProfile* profile, skcms_Matrix3x3* m) { + skcms_ICCTag tag; + if (!skcms_GetTagBySignature(profile, skcms_Signature_CHAD, &tag)) { + return false; + } + + if (tag.type != skcms_Signature_sf32 || tag.size < SAFE_SIZEOF(sf32_Layout)) { + return false; + } + + const sf32_Layout* sf32Tag = (const sf32_Layout*)tag.buf; + const uint8_t* values = sf32Tag->values; + for (int r = 0; r < 3; ++r) + for (int c = 0; c < 3; ++c, values += 4) { + m->vals[r][c] = read_big_fixed(values); + } + return true; +} + +// XYZType is technically variable sized, holding N XYZ triples. However, the only valid uses of +// the type are for tags/data that store exactly one triple. +typedef struct { + uint8_t type [4]; + uint8_t reserved [4]; + uint8_t X [4]; + uint8_t Y [4]; + uint8_t Z [4]; +} XYZ_Layout; + +static bool read_tag_xyz(const skcms_ICCTag* tag, float* x, float* y, float* z) { + if (tag->type != skcms_Signature_XYZ || tag->size < SAFE_SIZEOF(XYZ_Layout)) { + return false; + } + + const XYZ_Layout* xyzTag = (const XYZ_Layout*)tag->buf; + + *x = read_big_fixed(xyzTag->X); + *y = read_big_fixed(xyzTag->Y); + *z = read_big_fixed(xyzTag->Z); + return true; +} + +bool skcms_GetWTPT(const skcms_ICCProfile* profile, float xyz[3]) { + skcms_ICCTag tag; + return skcms_GetTagBySignature(profile, skcms_Signature_WTPT, &tag) && + read_tag_xyz(&tag, &xyz[0], &xyz[1], &xyz[2]); +} + +static bool read_to_XYZD50(const skcms_ICCTag* rXYZ, const skcms_ICCTag* gXYZ, + const skcms_ICCTag* bXYZ, skcms_Matrix3x3* toXYZ) { + return read_tag_xyz(rXYZ, &toXYZ->vals[0][0], &toXYZ->vals[1][0], &toXYZ->vals[2][0]) && + read_tag_xyz(gXYZ, &toXYZ->vals[0][1], &toXYZ->vals[1][1], &toXYZ->vals[2][1]) && + read_tag_xyz(bXYZ, &toXYZ->vals[0][2], &toXYZ->vals[1][2], &toXYZ->vals[2][2]); +} + +typedef struct { + uint8_t type [4]; + uint8_t reserved_a [4]; + uint8_t function_type [2]; + uint8_t reserved_b [2]; + uint8_t variable [1/*variable*/]; // 1, 3, 4, 5, or 7 s15.16, depending on function_type +} para_Layout; + +static bool read_curve_para(const uint8_t* buf, uint32_t size, + skcms_Curve* curve, uint32_t* curve_size) { + if (size < SAFE_FIXED_SIZE(para_Layout)) { + return false; + } + + const para_Layout* paraTag = (const para_Layout*)buf; + + enum { kG = 0, kGAB = 1, kGABC = 2, kGABCD = 3, kGABCDEF = 4 }; + uint16_t function_type = read_big_u16(paraTag->function_type); + if (function_type > kGABCDEF) { + return false; + } + + static const uint32_t curve_bytes[] = { 4, 12, 16, 20, 28 }; + if (size < SAFE_FIXED_SIZE(para_Layout) + curve_bytes[function_type]) { + return false; + } + + if (curve_size) { + *curve_size = SAFE_FIXED_SIZE(para_Layout) + curve_bytes[function_type]; + } + + curve->table_entries = 0; + curve->parametric.a = 1.0f; + curve->parametric.b = 0.0f; + curve->parametric.c = 0.0f; + curve->parametric.d = 0.0f; + curve->parametric.e = 0.0f; + curve->parametric.f = 0.0f; + curve->parametric.g = read_big_fixed(paraTag->variable); + + switch (function_type) { + case kGAB: + curve->parametric.a = read_big_fixed(paraTag->variable + 4); + curve->parametric.b = read_big_fixed(paraTag->variable + 8); + if (curve->parametric.a == 0) { + return false; + } + curve->parametric.d = -curve->parametric.b / curve->parametric.a; + break; + case kGABC: + curve->parametric.a = read_big_fixed(paraTag->variable + 4); + curve->parametric.b = read_big_fixed(paraTag->variable + 8); + curve->parametric.e = read_big_fixed(paraTag->variable + 12); + if (curve->parametric.a == 0) { + return false; + } + curve->parametric.d = -curve->parametric.b / curve->parametric.a; + curve->parametric.f = curve->parametric.e; + break; + case kGABCD: + curve->parametric.a = read_big_fixed(paraTag->variable + 4); + curve->parametric.b = read_big_fixed(paraTag->variable + 8); + curve->parametric.c = read_big_fixed(paraTag->variable + 12); + curve->parametric.d = read_big_fixed(paraTag->variable + 16); + break; + case kGABCDEF: + curve->parametric.a = read_big_fixed(paraTag->variable + 4); + curve->parametric.b = read_big_fixed(paraTag->variable + 8); + curve->parametric.c = read_big_fixed(paraTag->variable + 12); + curve->parametric.d = read_big_fixed(paraTag->variable + 16); + curve->parametric.e = read_big_fixed(paraTag->variable + 20); + curve->parametric.f = read_big_fixed(paraTag->variable + 24); + break; + } + return skcms_TransferFunction_isSRGBish(&curve->parametric); +} + +typedef struct { + uint8_t type [4]; + uint8_t reserved [4]; + uint8_t value_count [4]; + uint8_t variable [1/*variable*/]; // value_count, 8.8 if 1, uint16 (n*65535) if > 1 +} curv_Layout; + +static bool read_curve_curv(const uint8_t* buf, uint32_t size, + skcms_Curve* curve, uint32_t* curve_size) { + if (size < SAFE_FIXED_SIZE(curv_Layout)) { + return false; + } + + const curv_Layout* curvTag = (const curv_Layout*)buf; + + uint32_t value_count = read_big_u32(curvTag->value_count); + if (size < SAFE_FIXED_SIZE(curv_Layout) + value_count * SAFE_SIZEOF(uint16_t)) { + return false; + } + + if (curve_size) { + *curve_size = SAFE_FIXED_SIZE(curv_Layout) + value_count * SAFE_SIZEOF(uint16_t); + } + + if (value_count < 2) { + curve->table_entries = 0; + curve->parametric.a = 1.0f; + curve->parametric.b = 0.0f; + curve->parametric.c = 0.0f; + curve->parametric.d = 0.0f; + curve->parametric.e = 0.0f; + curve->parametric.f = 0.0f; + if (value_count == 0) { + // Empty tables are a shorthand for an identity curve + curve->parametric.g = 1.0f; + } else { + // Single entry tables are a shorthand for simple gamma + curve->parametric.g = read_big_u16(curvTag->variable) * (1.0f / 256.0f); + } + } else { + curve->table_8 = nullptr; + curve->table_16 = curvTag->variable; + curve->table_entries = value_count; + } + + return true; +} + +// Parses both curveType and parametricCurveType data. Ensures that at most 'size' bytes are read. +// If curve_size is not nullptr, writes the number of bytes used by the curve in (*curve_size). +static bool read_curve(const uint8_t* buf, uint32_t size, + skcms_Curve* curve, uint32_t* curve_size) { + if (!buf || size < 4 || !curve) { + return false; + } + + uint32_t type = read_big_u32(buf); + if (type == skcms_Signature_para) { + return read_curve_para(buf, size, curve, curve_size); + } else if (type == skcms_Signature_curv) { + return read_curve_curv(buf, size, curve, curve_size); + } + + return false; +} + +// mft1 and mft2 share a large chunk of data +typedef struct { + uint8_t type [ 4]; + uint8_t reserved_a [ 4]; + uint8_t input_channels [ 1]; + uint8_t output_channels [ 1]; + uint8_t grid_points [ 1]; + uint8_t reserved_b [ 1]; + uint8_t matrix [36]; +} mft_CommonLayout; + +typedef struct { + mft_CommonLayout common [1]; + + uint8_t variable [1/*variable*/]; +} mft1_Layout; + +typedef struct { + mft_CommonLayout common [1]; + + uint8_t input_table_entries [2]; + uint8_t output_table_entries [2]; + uint8_t variable [1/*variable*/]; +} mft2_Layout; + +static bool read_mft_common(const mft_CommonLayout* mftTag, skcms_A2B* a2b) { + // MFT matrices are applied before the first set of curves, but must be identity unless the + // input is PCSXYZ. We don't support PCSXYZ profiles, so we ignore this matrix. Note that the + // matrix in skcms_A2B is applied later in the pipe, so supporting this would require another + // field/flag. + a2b->matrix_channels = 0; + a2b-> input_channels = mftTag-> input_channels[0]; + a2b->output_channels = mftTag->output_channels[0]; + + // We require exactly three (ie XYZ/Lab/RGB) output channels + if (a2b->output_channels != ARRAY_COUNT(a2b->output_curves)) { + return false; + } + // We require at least one, and no more than four (ie CMYK) input channels + if (a2b->input_channels < 1 || a2b->input_channels > ARRAY_COUNT(a2b->input_curves)) { + return false; + } + + for (uint32_t i = 0; i < a2b->input_channels; ++i) { + a2b->grid_points[i] = mftTag->grid_points[0]; + } + // The grid only makes sense with at least two points along each axis + if (a2b->grid_points[0] < 2) { + return false; + } + return true; +} + +// All as the A2B version above, except where noted. +static bool read_mft_common(const mft_CommonLayout* mftTag, skcms_B2A* b2a) { + // Same as A2B. + b2a->matrix_channels = 0; + b2a-> input_channels = mftTag-> input_channels[0]; + b2a->output_channels = mftTag->output_channels[0]; + + + // For B2A, exactly 3 input channels (XYZ) and 3 (RGB) or 4 (CMYK) output channels. + if (b2a->input_channels != ARRAY_COUNT(b2a->input_curves)) { + return false; + } + if (b2a->output_channels < 3 || b2a->output_channels > ARRAY_COUNT(b2a->output_curves)) { + return false; + } + + // Same as A2B. + for (uint32_t i = 0; i < b2a->input_channels; ++i) { + b2a->grid_points[i] = mftTag->grid_points[0]; + } + if (b2a->grid_points[0] < 2) { + return false; + } + return true; +} + +template +static bool init_tables(const uint8_t* table_base, uint64_t max_tables_len, uint32_t byte_width, + uint32_t input_table_entries, uint32_t output_table_entries, + A2B_or_B2A* out) { + // byte_width is 1 or 2, [input|output]_table_entries are in [2, 4096], so no overflow + uint32_t byte_len_per_input_table = input_table_entries * byte_width; + uint32_t byte_len_per_output_table = output_table_entries * byte_width; + + // [input|output]_channels are <= 4, so still no overflow + uint32_t byte_len_all_input_tables = out->input_channels * byte_len_per_input_table; + uint32_t byte_len_all_output_tables = out->output_channels * byte_len_per_output_table; + + uint64_t grid_size = out->output_channels * byte_width; + for (uint32_t axis = 0; axis < out->input_channels; ++axis) { + grid_size *= out->grid_points[axis]; + } + + if (max_tables_len < byte_len_all_input_tables + grid_size + byte_len_all_output_tables) { + return false; + } + + for (uint32_t i = 0; i < out->input_channels; ++i) { + out->input_curves[i].table_entries = input_table_entries; + if (byte_width == 1) { + out->input_curves[i].table_8 = table_base + i * byte_len_per_input_table; + out->input_curves[i].table_16 = nullptr; + } else { + out->input_curves[i].table_8 = nullptr; + out->input_curves[i].table_16 = table_base + i * byte_len_per_input_table; + } + } + + if (byte_width == 1) { + out->grid_8 = table_base + byte_len_all_input_tables; + out->grid_16 = nullptr; + } else { + out->grid_8 = nullptr; + out->grid_16 = table_base + byte_len_all_input_tables; + } + + const uint8_t* output_table_base = table_base + byte_len_all_input_tables + grid_size; + for (uint32_t i = 0; i < out->output_channels; ++i) { + out->output_curves[i].table_entries = output_table_entries; + if (byte_width == 1) { + out->output_curves[i].table_8 = output_table_base + i * byte_len_per_output_table; + out->output_curves[i].table_16 = nullptr; + } else { + out->output_curves[i].table_8 = nullptr; + out->output_curves[i].table_16 = output_table_base + i * byte_len_per_output_table; + } + } + + return true; +} + +template +static bool read_tag_mft1(const skcms_ICCTag* tag, A2B_or_B2A* out) { + if (tag->size < SAFE_FIXED_SIZE(mft1_Layout)) { + return false; + } + + const mft1_Layout* mftTag = (const mft1_Layout*)tag->buf; + if (!read_mft_common(mftTag->common, out)) { + return false; + } + + uint32_t input_table_entries = 256; + uint32_t output_table_entries = 256; + + return init_tables(mftTag->variable, tag->size - SAFE_FIXED_SIZE(mft1_Layout), 1, + input_table_entries, output_table_entries, out); +} + +template +static bool read_tag_mft2(const skcms_ICCTag* tag, A2B_or_B2A* out) { + if (tag->size < SAFE_FIXED_SIZE(mft2_Layout)) { + return false; + } + + const mft2_Layout* mftTag = (const mft2_Layout*)tag->buf; + if (!read_mft_common(mftTag->common, out)) { + return false; + } + + uint32_t input_table_entries = read_big_u16(mftTag->input_table_entries); + uint32_t output_table_entries = read_big_u16(mftTag->output_table_entries); + + // ICC spec mandates that 2 <= table_entries <= 4096 + if (input_table_entries < 2 || input_table_entries > 4096 || + output_table_entries < 2 || output_table_entries > 4096) { + return false; + } + + return init_tables(mftTag->variable, tag->size - SAFE_FIXED_SIZE(mft2_Layout), 2, + input_table_entries, output_table_entries, out); +} + +static bool read_curves(const uint8_t* buf, uint32_t size, uint32_t curve_offset, + uint32_t num_curves, skcms_Curve* curves) { + for (uint32_t i = 0; i < num_curves; ++i) { + if (curve_offset > size) { + return false; + } + + uint32_t curve_bytes; + if (!read_curve(buf + curve_offset, size - curve_offset, &curves[i], &curve_bytes)) { + return false; + } + + if (curve_bytes > UINT32_MAX - 3) { + return false; + } + curve_bytes = (curve_bytes + 3) & ~3U; + + uint64_t new_offset_64 = (uint64_t)curve_offset + curve_bytes; + curve_offset = (uint32_t)new_offset_64; + if (new_offset_64 != curve_offset) { + return false; + } + } + + return true; +} + +// mAB and mBA tags use the same encoding, including color lookup tables. +typedef struct { + uint8_t type [ 4]; + uint8_t reserved_a [ 4]; + uint8_t input_channels [ 1]; + uint8_t output_channels [ 1]; + uint8_t reserved_b [ 2]; + uint8_t b_curve_offset [ 4]; + uint8_t matrix_offset [ 4]; + uint8_t m_curve_offset [ 4]; + uint8_t clut_offset [ 4]; + uint8_t a_curve_offset [ 4]; +} mAB_or_mBA_Layout; + +typedef struct { + uint8_t grid_points [16]; + uint8_t grid_byte_width [ 1]; + uint8_t reserved [ 3]; + uint8_t variable [1/*variable*/]; +} CLUT_Layout; + +static bool read_tag_mab(const skcms_ICCTag* tag, skcms_A2B* a2b, bool pcs_is_xyz) { + if (tag->size < SAFE_SIZEOF(mAB_or_mBA_Layout)) { + return false; + } + + const mAB_or_mBA_Layout* mABTag = (const mAB_or_mBA_Layout*)tag->buf; + + a2b->input_channels = mABTag->input_channels[0]; + a2b->output_channels = mABTag->output_channels[0]; + + // We require exactly three (ie XYZ/Lab/RGB) output channels + if (a2b->output_channels != ARRAY_COUNT(a2b->output_curves)) { + return false; + } + // We require no more than four (ie CMYK) input channels + if (a2b->input_channels > ARRAY_COUNT(a2b->input_curves)) { + return false; + } + + uint32_t b_curve_offset = read_big_u32(mABTag->b_curve_offset); + uint32_t matrix_offset = read_big_u32(mABTag->matrix_offset); + uint32_t m_curve_offset = read_big_u32(mABTag->m_curve_offset); + uint32_t clut_offset = read_big_u32(mABTag->clut_offset); + uint32_t a_curve_offset = read_big_u32(mABTag->a_curve_offset); + + // "B" curves must be present + if (0 == b_curve_offset) { + return false; + } + + if (!read_curves(tag->buf, tag->size, b_curve_offset, a2b->output_channels, + a2b->output_curves)) { + return false; + } + + // "M" curves and Matrix must be used together + if (0 != m_curve_offset) { + if (0 == matrix_offset) { + return false; + } + a2b->matrix_channels = a2b->output_channels; + if (!read_curves(tag->buf, tag->size, m_curve_offset, a2b->matrix_channels, + a2b->matrix_curves)) { + return false; + } + + // Read matrix, which is stored as a row-major 3x3, followed by the fourth column + if (tag->size < matrix_offset + 12 * SAFE_SIZEOF(uint32_t)) { + return false; + } + float encoding_factor = pcs_is_xyz ? (65535 / 32768.0f) : 1.0f; + const uint8_t* mtx_buf = tag->buf + matrix_offset; + a2b->matrix.vals[0][0] = encoding_factor * read_big_fixed(mtx_buf + 0); + a2b->matrix.vals[0][1] = encoding_factor * read_big_fixed(mtx_buf + 4); + a2b->matrix.vals[0][2] = encoding_factor * read_big_fixed(mtx_buf + 8); + a2b->matrix.vals[1][0] = encoding_factor * read_big_fixed(mtx_buf + 12); + a2b->matrix.vals[1][1] = encoding_factor * read_big_fixed(mtx_buf + 16); + a2b->matrix.vals[1][2] = encoding_factor * read_big_fixed(mtx_buf + 20); + a2b->matrix.vals[2][0] = encoding_factor * read_big_fixed(mtx_buf + 24); + a2b->matrix.vals[2][1] = encoding_factor * read_big_fixed(mtx_buf + 28); + a2b->matrix.vals[2][2] = encoding_factor * read_big_fixed(mtx_buf + 32); + a2b->matrix.vals[0][3] = encoding_factor * read_big_fixed(mtx_buf + 36); + a2b->matrix.vals[1][3] = encoding_factor * read_big_fixed(mtx_buf + 40); + a2b->matrix.vals[2][3] = encoding_factor * read_big_fixed(mtx_buf + 44); + } else { + if (0 != matrix_offset) { + return false; + } + a2b->matrix_channels = 0; + } + + // "A" curves and CLUT must be used together + if (0 != a_curve_offset) { + if (0 == clut_offset) { + return false; + } + if (!read_curves(tag->buf, tag->size, a_curve_offset, a2b->input_channels, + a2b->input_curves)) { + return false; + } + + if (tag->size < clut_offset + SAFE_FIXED_SIZE(CLUT_Layout)) { + return false; + } + const CLUT_Layout* clut = (const CLUT_Layout*)(tag->buf + clut_offset); + + if (clut->grid_byte_width[0] == 1) { + a2b->grid_8 = clut->variable; + a2b->grid_16 = nullptr; + } else if (clut->grid_byte_width[0] == 2) { + a2b->grid_8 = nullptr; + a2b->grid_16 = clut->variable; + } else { + return false; + } + + uint64_t grid_size = a2b->output_channels * clut->grid_byte_width[0]; // the payload + for (uint32_t i = 0; i < a2b->input_channels; ++i) { + a2b->grid_points[i] = clut->grid_points[i]; + // The grid only makes sense with at least two points along each axis + if (a2b->grid_points[i] < 2) { + return false; + } + grid_size *= a2b->grid_points[i]; + } + if (tag->size < clut_offset + SAFE_FIXED_SIZE(CLUT_Layout) + grid_size) { + return false; + } + } else { + if (0 != clut_offset) { + return false; + } + + // If there is no CLUT, the number of input and output channels must match + if (a2b->input_channels != a2b->output_channels) { + return false; + } + + // Zero out the number of input channels to signal that we're skipping this stage + a2b->input_channels = 0; + } + + return true; +} + +// Exactly the same as read_tag_mab(), except where there are comments. +// TODO: refactor the two to eliminate common code? +static bool read_tag_mba(const skcms_ICCTag* tag, skcms_B2A* b2a, bool pcs_is_xyz) { + if (tag->size < SAFE_SIZEOF(mAB_or_mBA_Layout)) { + return false; + } + + const mAB_or_mBA_Layout* mBATag = (const mAB_or_mBA_Layout*)tag->buf; + + b2a->input_channels = mBATag->input_channels[0]; + b2a->output_channels = mBATag->output_channels[0]; + + // Require exactly 3 inputs (XYZ) and 3 (RGB) or 4 (CMYK) outputs. + if (b2a->input_channels != ARRAY_COUNT(b2a->input_curves)) { + return false; + } + if (b2a->output_channels < 3 || b2a->output_channels > ARRAY_COUNT(b2a->output_curves)) { + return false; + } + + uint32_t b_curve_offset = read_big_u32(mBATag->b_curve_offset); + uint32_t matrix_offset = read_big_u32(mBATag->matrix_offset); + uint32_t m_curve_offset = read_big_u32(mBATag->m_curve_offset); + uint32_t clut_offset = read_big_u32(mBATag->clut_offset); + uint32_t a_curve_offset = read_big_u32(mBATag->a_curve_offset); + + if (0 == b_curve_offset) { + return false; + } + + // "B" curves are our inputs, not outputs. + if (!read_curves(tag->buf, tag->size, b_curve_offset, b2a->input_channels, + b2a->input_curves)) { + return false; + } + + if (0 != m_curve_offset) { + if (0 == matrix_offset) { + return false; + } + // Matrix channels is tied to input_channels (3), not output_channels. + b2a->matrix_channels = b2a->input_channels; + + if (!read_curves(tag->buf, tag->size, m_curve_offset, b2a->matrix_channels, + b2a->matrix_curves)) { + return false; + } + + if (tag->size < matrix_offset + 12 * SAFE_SIZEOF(uint32_t)) { + return false; + } + float encoding_factor = pcs_is_xyz ? (32768 / 65535.0f) : 1.0f; // TODO: understand + const uint8_t* mtx_buf = tag->buf + matrix_offset; + b2a->matrix.vals[0][0] = encoding_factor * read_big_fixed(mtx_buf + 0); + b2a->matrix.vals[0][1] = encoding_factor * read_big_fixed(mtx_buf + 4); + b2a->matrix.vals[0][2] = encoding_factor * read_big_fixed(mtx_buf + 8); + b2a->matrix.vals[1][0] = encoding_factor * read_big_fixed(mtx_buf + 12); + b2a->matrix.vals[1][1] = encoding_factor * read_big_fixed(mtx_buf + 16); + b2a->matrix.vals[1][2] = encoding_factor * read_big_fixed(mtx_buf + 20); + b2a->matrix.vals[2][0] = encoding_factor * read_big_fixed(mtx_buf + 24); + b2a->matrix.vals[2][1] = encoding_factor * read_big_fixed(mtx_buf + 28); + b2a->matrix.vals[2][2] = encoding_factor * read_big_fixed(mtx_buf + 32); + b2a->matrix.vals[0][3] = encoding_factor * read_big_fixed(mtx_buf + 36); + b2a->matrix.vals[1][3] = encoding_factor * read_big_fixed(mtx_buf + 40); + b2a->matrix.vals[2][3] = encoding_factor * read_big_fixed(mtx_buf + 44); + } else { + if (0 != matrix_offset) { + return false; + } + b2a->matrix_channels = 0; + } + + if (0 != a_curve_offset) { + if (0 == clut_offset) { + return false; + } + + // "A" curves are our output, not input. + if (!read_curves(tag->buf, tag->size, a_curve_offset, b2a->output_channels, + b2a->output_curves)) { + return false; + } + + if (tag->size < clut_offset + SAFE_FIXED_SIZE(CLUT_Layout)) { + return false; + } + const CLUT_Layout* clut = (const CLUT_Layout*)(tag->buf + clut_offset); + + if (clut->grid_byte_width[0] == 1) { + b2a->grid_8 = clut->variable; + b2a->grid_16 = nullptr; + } else if (clut->grid_byte_width[0] == 2) { + b2a->grid_8 = nullptr; + b2a->grid_16 = clut->variable; + } else { + return false; + } + + uint64_t grid_size = b2a->output_channels * clut->grid_byte_width[0]; + for (uint32_t i = 0; i < b2a->input_channels; ++i) { + b2a->grid_points[i] = clut->grid_points[i]; + if (b2a->grid_points[i] < 2) { + return false; + } + grid_size *= b2a->grid_points[i]; + } + if (tag->size < clut_offset + SAFE_FIXED_SIZE(CLUT_Layout) + grid_size) { + return false; + } + } else { + if (0 != clut_offset) { + return false; + } + + if (b2a->input_channels != b2a->output_channels) { + return false; + } + + // Zero out *output* channels to skip this stage. + b2a->output_channels = 0; + } + return true; +} + +// If you pass f, we'll fit a possibly-non-zero value for *f. +// If you pass nullptr, we'll assume you want *f to be treated as zero. +static int fit_linear(const skcms_Curve* curve, int N, float tol, + float* c, float* d, float* f = nullptr) { + assert(N > 1); + // We iteratively fit the first points to the TF's linear piece. + // We want the cx + f line to pass through the first and last points we fit exactly. + // + // As we walk along the points we find the minimum and maximum slope of the line before the + // error would exceed our tolerance. We stop when the range [slope_min, slope_max] becomes + // emtpy, when we definitely can't add any more points. + // + // Some points' error intervals may intersect the running interval but not lie fully + // within it. So we keep track of the last point we saw that is a valid end point candidate, + // and once the search is done, back up to build the line through *that* point. + const float dx = 1.0f / static_cast(N - 1); + + int lin_points = 1; + + float f_zero = 0.0f; + if (f) { + *f = eval_curve(curve, 0); + } else { + f = &f_zero; + } + + + float slope_min = -INFINITY_; + float slope_max = +INFINITY_; + for (int i = 1; i < N; ++i) { + float x = static_cast(i) * dx; + float y = eval_curve(curve, x); + + float slope_max_i = (y + tol - *f) / x, + slope_min_i = (y - tol - *f) / x; + if (slope_max_i < slope_min || slope_max < slope_min_i) { + // Slope intervals would no longer overlap. + break; + } + slope_max = fminf_(slope_max, slope_max_i); + slope_min = fmaxf_(slope_min, slope_min_i); + + float cur_slope = (y - *f) / x; + if (slope_min <= cur_slope && cur_slope <= slope_max) { + lin_points = i + 1; + *c = cur_slope; + } + } + + // Set D to the last point that met our tolerance. + *d = static_cast(lin_points - 1) * dx; + return lin_points; +} + +// If this skcms_Curve holds an identity table, rewrite it as an identity skcms_TransferFunction. +static void canonicalize_identity(skcms_Curve* curve) { + if (curve->table_entries && curve->table_entries <= (uint32_t)INT_MAX) { + int N = (int)curve->table_entries; + + float c = 0.0f, d = 0.0f, f = 0.0f; + if (N == fit_linear(curve, N, 1.0f/static_cast(2*N), &c,&d,&f) + && c == 1.0f + && f == 0.0f) { + curve->table_entries = 0; + curve->table_8 = nullptr; + curve->table_16 = nullptr; + curve->parametric = skcms_TransferFunction{1,1,0,0,0,0,0}; + } + } +} + +static bool read_a2b(const skcms_ICCTag* tag, skcms_A2B* a2b, bool pcs_is_xyz) { + bool ok = false; + if (tag->type == skcms_Signature_mft1) { ok = read_tag_mft1(tag, a2b); } + if (tag->type == skcms_Signature_mft2) { ok = read_tag_mft2(tag, a2b); } + if (tag->type == skcms_Signature_mAB ) { ok = read_tag_mab(tag, a2b, pcs_is_xyz); } + if (!ok) { + return false; + } + + if (a2b->input_channels > 0) { canonicalize_identity(a2b->input_curves + 0); } + if (a2b->input_channels > 1) { canonicalize_identity(a2b->input_curves + 1); } + if (a2b->input_channels > 2) { canonicalize_identity(a2b->input_curves + 2); } + if (a2b->input_channels > 3) { canonicalize_identity(a2b->input_curves + 3); } + + if (a2b->matrix_channels > 0) { canonicalize_identity(a2b->matrix_curves + 0); } + if (a2b->matrix_channels > 1) { canonicalize_identity(a2b->matrix_curves + 1); } + if (a2b->matrix_channels > 2) { canonicalize_identity(a2b->matrix_curves + 2); } + + if (a2b->output_channels > 0) { canonicalize_identity(a2b->output_curves + 0); } + if (a2b->output_channels > 1) { canonicalize_identity(a2b->output_curves + 1); } + if (a2b->output_channels > 2) { canonicalize_identity(a2b->output_curves + 2); } + + return true; +} + +static bool read_b2a(const skcms_ICCTag* tag, skcms_B2A* b2a, bool pcs_is_xyz) { + bool ok = false; + if (tag->type == skcms_Signature_mft1) { ok = read_tag_mft1(tag, b2a); } + if (tag->type == skcms_Signature_mft2) { ok = read_tag_mft2(tag, b2a); } + if (tag->type == skcms_Signature_mBA ) { ok = read_tag_mba(tag, b2a, pcs_is_xyz); } + if (!ok) { + return false; + } + + if (b2a->input_channels > 0) { canonicalize_identity(b2a->input_curves + 0); } + if (b2a->input_channels > 1) { canonicalize_identity(b2a->input_curves + 1); } + if (b2a->input_channels > 2) { canonicalize_identity(b2a->input_curves + 2); } + + if (b2a->matrix_channels > 0) { canonicalize_identity(b2a->matrix_curves + 0); } + if (b2a->matrix_channels > 1) { canonicalize_identity(b2a->matrix_curves + 1); } + if (b2a->matrix_channels > 2) { canonicalize_identity(b2a->matrix_curves + 2); } + + if (b2a->output_channels > 0) { canonicalize_identity(b2a->output_curves + 0); } + if (b2a->output_channels > 1) { canonicalize_identity(b2a->output_curves + 1); } + if (b2a->output_channels > 2) { canonicalize_identity(b2a->output_curves + 2); } + if (b2a->output_channels > 3) { canonicalize_identity(b2a->output_curves + 3); } + + return true; +} + +typedef struct { + uint8_t type [4]; + uint8_t reserved [4]; + uint8_t color_primaries [1]; + uint8_t transfer_characteristics [1]; + uint8_t matrix_coefficients [1]; + uint8_t video_full_range_flag [1]; +} CICP_Layout; + +static bool read_cicp(const skcms_ICCTag* tag, skcms_CICP* cicp) { + if (tag->type != skcms_Signature_CICP || tag->size < SAFE_SIZEOF(CICP_Layout)) { + return false; + } + + const CICP_Layout* cicpTag = (const CICP_Layout*)tag->buf; + + cicp->color_primaries = cicpTag->color_primaries[0]; + cicp->transfer_characteristics = cicpTag->transfer_characteristics[0]; + cicp->matrix_coefficients = cicpTag->matrix_coefficients[0]; + cicp->video_full_range_flag = cicpTag->video_full_range_flag[0]; + return true; +} + +void skcms_GetTagByIndex(const skcms_ICCProfile* profile, uint32_t idx, skcms_ICCTag* tag) { + if (!profile || !profile->buffer || !tag) { return; } + if (idx > profile->tag_count) { return; } + const tag_Layout* tags = get_tag_table(profile); + tag->signature = read_big_u32(tags[idx].signature); + tag->size = read_big_u32(tags[idx].size); + tag->buf = read_big_u32(tags[idx].offset) + profile->buffer; + tag->type = read_big_u32(tag->buf); +} + +bool skcms_GetTagBySignature(const skcms_ICCProfile* profile, uint32_t sig, skcms_ICCTag* tag) { + if (!profile || !profile->buffer || !tag) { return false; } + const tag_Layout* tags = get_tag_table(profile); + for (uint32_t i = 0; i < profile->tag_count; ++i) { + if (read_big_u32(tags[i].signature) == sig) { + tag->signature = sig; + tag->size = read_big_u32(tags[i].size); + tag->buf = read_big_u32(tags[i].offset) + profile->buffer; + tag->type = read_big_u32(tag->buf); + return true; + } + } + return false; +} + +static bool usable_as_src(const skcms_ICCProfile* profile) { + return profile->has_A2B + || (profile->has_trc && profile->has_toXYZD50); +} + +bool skcms_ParseWithA2BPriority(const void* buf, size_t len, + const int priority[], const int priorities, + skcms_ICCProfile* profile) { + static_assert(SAFE_SIZEOF(header_Layout) == 132, "need to update header code"); + + if (!profile) { + return false; + } + memset(profile, 0, SAFE_SIZEOF(*profile)); + + if (len < SAFE_SIZEOF(header_Layout)) { + return false; + } + + // Byte-swap all header fields + const header_Layout* header = (const header_Layout*)buf; + profile->buffer = (const uint8_t*)buf; + profile->size = read_big_u32(header->size); + uint32_t version = read_big_u32(header->version); + profile->data_color_space = read_big_u32(header->data_color_space); + profile->pcs = read_big_u32(header->pcs); + uint32_t signature = read_big_u32(header->signature); + float illuminant_X = read_big_fixed(header->illuminant_X); + float illuminant_Y = read_big_fixed(header->illuminant_Y); + float illuminant_Z = read_big_fixed(header->illuminant_Z); + profile->tag_count = read_big_u32(header->tag_count); + + // Validate signature, size (smaller than buffer, large enough to hold tag table), + // and major version + uint64_t tag_table_size = profile->tag_count * SAFE_SIZEOF(tag_Layout); + if (signature != skcms_Signature_acsp || + profile->size > len || + profile->size < SAFE_SIZEOF(header_Layout) + tag_table_size || + (version >> 24) > 4) { + return false; + } + + // Validate that illuminant is D50 white + if (fabsf_(illuminant_X - 0.9642f) > 0.0100f || + fabsf_(illuminant_Y - 1.0000f) > 0.0100f || + fabsf_(illuminant_Z - 0.8249f) > 0.0100f) { + return false; + } + + // Validate that all tag entries have sane offset + size + const tag_Layout* tags = get_tag_table(profile); + for (uint32_t i = 0; i < profile->tag_count; ++i) { + uint32_t tag_offset = read_big_u32(tags[i].offset); + uint32_t tag_size = read_big_u32(tags[i].size); + uint64_t tag_end = (uint64_t)tag_offset + (uint64_t)tag_size; + if (tag_size < 4 || tag_end > profile->size) { + return false; + } + } + + if (profile->pcs != skcms_Signature_XYZ && profile->pcs != skcms_Signature_Lab) { + return false; + } + + bool pcs_is_xyz = profile->pcs == skcms_Signature_XYZ; + + // Pre-parse commonly used tags. + skcms_ICCTag kTRC; + if (profile->data_color_space == skcms_Signature_Gray && + skcms_GetTagBySignature(profile, skcms_Signature_kTRC, &kTRC)) { + if (!read_curve(kTRC.buf, kTRC.size, &profile->trc[0], nullptr)) { + // Malformed tag + return false; + } + profile->trc[1] = profile->trc[0]; + profile->trc[2] = profile->trc[0]; + profile->has_trc = true; + + if (pcs_is_xyz) { + profile->toXYZD50.vals[0][0] = illuminant_X; + profile->toXYZD50.vals[1][1] = illuminant_Y; + profile->toXYZD50.vals[2][2] = illuminant_Z; + profile->has_toXYZD50 = true; + } + } else { + skcms_ICCTag rTRC, gTRC, bTRC; + if (skcms_GetTagBySignature(profile, skcms_Signature_rTRC, &rTRC) && + skcms_GetTagBySignature(profile, skcms_Signature_gTRC, &gTRC) && + skcms_GetTagBySignature(profile, skcms_Signature_bTRC, &bTRC)) { + if (!read_curve(rTRC.buf, rTRC.size, &profile->trc[0], nullptr) || + !read_curve(gTRC.buf, gTRC.size, &profile->trc[1], nullptr) || + !read_curve(bTRC.buf, bTRC.size, &profile->trc[2], nullptr)) { + // Malformed TRC tags + return false; + } + profile->has_trc = true; + } + + skcms_ICCTag rXYZ, gXYZ, bXYZ; + if (skcms_GetTagBySignature(profile, skcms_Signature_rXYZ, &rXYZ) && + skcms_GetTagBySignature(profile, skcms_Signature_gXYZ, &gXYZ) && + skcms_GetTagBySignature(profile, skcms_Signature_bXYZ, &bXYZ)) { + if (!read_to_XYZD50(&rXYZ, &gXYZ, &bXYZ, &profile->toXYZD50)) { + // Malformed XYZ tags + return false; + } + profile->has_toXYZD50 = true; + } + } + + for (int i = 0; i < priorities; i++) { + // enum { perceptual, relative_colormetric, saturation } + if (priority[i] < 0 || priority[i] > 2) { + return false; + } + uint32_t sig = skcms_Signature_A2B0 + static_cast(priority[i]); + skcms_ICCTag tag; + if (skcms_GetTagBySignature(profile, sig, &tag)) { + if (!read_a2b(&tag, &profile->A2B, pcs_is_xyz)) { + // Malformed A2B tag + return false; + } + profile->has_A2B = true; + break; + } + } + + for (int i = 0; i < priorities; i++) { + // enum { perceptual, relative_colormetric, saturation } + if (priority[i] < 0 || priority[i] > 2) { + return false; + } + uint32_t sig = skcms_Signature_B2A0 + static_cast(priority[i]); + skcms_ICCTag tag; + if (skcms_GetTagBySignature(profile, sig, &tag)) { + if (!read_b2a(&tag, &profile->B2A, pcs_is_xyz)) { + // Malformed B2A tag + return false; + } + profile->has_B2A = true; + break; + } + } + + skcms_ICCTag cicp_tag; + if (skcms_GetTagBySignature(profile, skcms_Signature_CICP, &cicp_tag)) { + if (!read_cicp(&cicp_tag, &profile->CICP)) { + // Malformed CICP tag + return false; + } + profile->has_CICP = true; + } + + return usable_as_src(profile); +} + + +const skcms_ICCProfile* skcms_sRGB_profile() { + static const skcms_ICCProfile sRGB_profile = { + nullptr, // buffer, moot here + + 0, // size, moot here + skcms_Signature_RGB, // data_color_space + skcms_Signature_XYZ, // pcs + 0, // tag count, moot here + + // 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 + { + {{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 + {{ + { 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, + { + {{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,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_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,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 }, + }; + return &sRGB_profile; +} + +const skcms_ICCProfile* skcms_XYZD50_profile() { + // Just like sRGB above, but with identity transfer functions and toXYZD50 matrix. + static const skcms_ICCProfile XYZD50_profile = { + nullptr, // buffer, moot here + + 0, // size, moot here + skcms_Signature_RGB, // data_color_space + skcms_Signature_XYZ, // pcs + 0, // tag count, moot here + + true, // has_trc, followed by 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 + {{ + { 1,0,0 }, + { 0,1,0 }, + { 0,0,1 }, + }}, + + false, // has_A2B, followed by A2B itself, which we 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,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,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_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,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 }, + }; + + return &XYZD50_profile; +} + +const skcms_TransferFunction* skcms_sRGB_TransferFunction() { + return &skcms_sRGB_profile()->trc[0].parametric; +} + +const skcms_TransferFunction* skcms_sRGB_Inverse_TransferFunction() { + static const skcms_TransferFunction sRGB_inv = + {0.416666657f, 1.137283325f, -0.0f, 12.920000076f, 0.003130805f, -0.054969788f, -0.0f}; + return &sRGB_inv; +} + +const skcms_TransferFunction* skcms_Identity_TransferFunction() { + static const skcms_TransferFunction identity = {1,1,0,0,0,0,0}; + return &identity; +} + +const uint8_t skcms_252_random_bytes[] = { + 8, 179, 128, 204, 253, 38, 134, 184, 68, 102, 32, 138, 99, 39, 169, 215, + 119, 26, 3, 223, 95, 239, 52, 132, 114, 74, 81, 234, 97, 116, 244, 205, 30, + 154, 173, 12, 51, 159, 122, 153, 61, 226, 236, 178, 229, 55, 181, 220, 191, + 194, 160, 126, 168, 82, 131, 18, 180, 245, 163, 22, 246, 69, 235, 252, 57, + 108, 14, 6, 152, 240, 255, 171, 242, 20, 227, 177, 238, 96, 85, 16, 211, + 70, 200, 149, 155, 146, 127, 145, 100, 151, 109, 19, 165, 208, 195, 164, + 137, 254, 182, 248, 64, 201, 45, 209, 5, 147, 207, 210, 113, 162, 83, 225, + 9, 31, 15, 231, 115, 37, 58, 53, 24, 49, 197, 56, 120, 172, 48, 21, 214, + 129, 111, 11, 50, 187, 196, 34, 60, 103, 71, 144, 47, 203, 77, 80, 232, + 140, 222, 250, 206, 166, 247, 139, 249, 221, 72, 106, 27, 199, 117, 54, + 219, 135, 118, 40, 79, 41, 251, 46, 93, 212, 92, 233, 148, 28, 121, 63, + 123, 158, 105, 59, 29, 42, 143, 23, 0, 107, 176, 87, 104, 183, 156, 193, + 189, 90, 188, 65, 190, 17, 198, 7, 186, 161, 1, 124, 78, 125, 170, 133, + 174, 218, 67, 157, 75, 101, 89, 217, 62, 33, 141, 228, 25, 35, 91, 230, 4, + 2, 13, 73, 86, 167, 237, 84, 243, 44, 185, 66, 130, 110, 150, 142, 216, 88, + 112, 36, 224, 136, 202, 76, 94, 98, 175, 213 +}; + +bool skcms_ApproximatelyEqualProfiles(const skcms_ICCProfile* A, const skcms_ICCProfile* B) { + // Test for exactly equal profiles first. + if (A == B || 0 == memcmp(A,B, sizeof(skcms_ICCProfile))) { + return true; + } + + // For now this is the essentially the same strategy we use in test_only.c + // for our skcms_Transform() smoke tests: + // 1) transform A to XYZD50 + // 2) transform B to XYZD50 + // 3) return true if they're similar enough + // Our current criterion in 3) is maximum 1 bit error per XYZD50 byte. + + // skcms_252_random_bytes are 252 of a random shuffle of all possible bytes. + // 252 is evenly divisible by 3 and 4. Only 192, 10, 241, and 43 are missing. + + // We want to allow otherwise equivalent profiles tagged as grayscale and RGB + // to be treated as equal. But CMYK profiles are a totally different ballgame. + const auto CMYK = skcms_Signature_CMYK; + if ((A->data_color_space == CMYK) != (B->data_color_space == CMYK)) { + return false; + } + + // Interpret as RGB_888 if data color space is RGB or GRAY, RGBA_8888 if CMYK. + // TODO: working with RGBA_8888 either way is probably fastest. + skcms_PixelFormat fmt = skcms_PixelFormat_RGB_888; + size_t npixels = 84; + if (A->data_color_space == skcms_Signature_CMYK) { + fmt = skcms_PixelFormat_RGBA_8888; + npixels = 63; + } + + // TODO: if A or B is a known profile (skcms_sRGB_profile, skcms_XYZD50_profile), + // use pre-canned results and skip that skcms_Transform() call? + uint8_t dstA[252], + dstB[252]; + if (!skcms_Transform( + skcms_252_random_bytes, fmt, skcms_AlphaFormat_Unpremul, A, + dstA, skcms_PixelFormat_RGB_888, skcms_AlphaFormat_Unpremul, skcms_XYZD50_profile(), + npixels)) { + return false; + } + if (!skcms_Transform( + skcms_252_random_bytes, fmt, skcms_AlphaFormat_Unpremul, B, + dstB, skcms_PixelFormat_RGB_888, skcms_AlphaFormat_Unpremul, skcms_XYZD50_profile(), + npixels)) { + return false; + } + + // TODO: make sure this final check has reasonable codegen. + for (size_t i = 0; i < 252; i++) { + if (abs((int)dstA[i] - (int)dstB[i]) > 1) { + return false; + } + } + return true; +} + +bool skcms_TRCs_AreApproximateInverse(const skcms_ICCProfile* profile, + const skcms_TransferFunction* inv_tf) { + if (!profile || !profile->has_trc) { + return false; + } + + return skcms_AreApproximateInverses(&profile->trc[0], inv_tf) && + skcms_AreApproximateInverses(&profile->trc[1], inv_tf) && + skcms_AreApproximateInverses(&profile->trc[2], inv_tf); +} + +static bool is_zero_to_one(float x) { + return 0 <= x && x <= 1; +} + +typedef struct { float vals[3]; } skcms_Vector3; + +static skcms_Vector3 mv_mul(const skcms_Matrix3x3* m, const skcms_Vector3* v) { + skcms_Vector3 dst = {{0,0,0}}; + for (int row = 0; row < 3; ++row) { + dst.vals[row] = m->vals[row][0] * v->vals[0] + + m->vals[row][1] * v->vals[1] + + m->vals[row][2] * v->vals[2]; + } + return dst; +} + +bool skcms_AdaptToXYZD50(float wx, float wy, + skcms_Matrix3x3* toXYZD50) { + if (!is_zero_to_one(wx) || !is_zero_to_one(wy) || + !toXYZD50) { + return false; + } + + // Assumes that Y is 1.0f. + skcms_Vector3 wXYZ = { { wx / wy, 1, (1 - wx - wy) / wy } }; + + // Now convert toXYZ matrix to toXYZD50. + skcms_Vector3 wXYZD50 = { { 0.96422f, 1.0f, 0.82521f } }; + + // Calculate the chromatic adaptation matrix. We will use the Bradford method, thus + // the matrices below. The Bradford method is used by Adobe and is widely considered + // to be the best. + skcms_Matrix3x3 xyz_to_lms = {{ + { 0.8951f, 0.2664f, -0.1614f }, + { -0.7502f, 1.7135f, 0.0367f }, + { 0.0389f, -0.0685f, 1.0296f }, + }}; + skcms_Matrix3x3 lms_to_xyz = {{ + { 0.9869929f, -0.1470543f, 0.1599627f }, + { 0.4323053f, 0.5183603f, 0.0492912f }, + { -0.0085287f, 0.0400428f, 0.9684867f }, + }}; + + skcms_Vector3 srcCone = mv_mul(&xyz_to_lms, &wXYZ); + skcms_Vector3 dstCone = mv_mul(&xyz_to_lms, &wXYZD50); + + *toXYZD50 = {{ + { dstCone.vals[0] / srcCone.vals[0], 0, 0 }, + { 0, dstCone.vals[1] / srcCone.vals[1], 0 }, + { 0, 0, dstCone.vals[2] / srcCone.vals[2] }, + }}; + *toXYZD50 = skcms_Matrix3x3_concat(toXYZD50, &xyz_to_lms); + *toXYZD50 = skcms_Matrix3x3_concat(&lms_to_xyz, toXYZD50); + + return true; +} + +bool skcms_PrimariesToXYZD50(float rx, float ry, + float gx, float gy, + float bx, float by, + float wx, float wy, + skcms_Matrix3x3* toXYZD50) { + if (!is_zero_to_one(rx) || !is_zero_to_one(ry) || + !is_zero_to_one(gx) || !is_zero_to_one(gy) || + !is_zero_to_one(bx) || !is_zero_to_one(by) || + !is_zero_to_one(wx) || !is_zero_to_one(wy) || + !toXYZD50) { + return false; + } + + // First, we need to convert xy values (primaries) to XYZ. + skcms_Matrix3x3 primaries = {{ + { rx, gx, bx }, + { ry, gy, by }, + { 1 - rx - ry, 1 - gx - gy, 1 - bx - by }, + }}; + skcms_Matrix3x3 primaries_inv; + if (!skcms_Matrix3x3_invert(&primaries, &primaries_inv)) { + return false; + } + + // Assumes that Y is 1.0f. + skcms_Vector3 wXYZ = { { wx / wy, 1, (1 - wx - wy) / wy } }; + skcms_Vector3 XYZ = mv_mul(&primaries_inv, &wXYZ); + + skcms_Matrix3x3 toXYZ = {{ + { XYZ.vals[0], 0, 0 }, + { 0, XYZ.vals[1], 0 }, + { 0, 0, XYZ.vals[2] }, + }}; + toXYZ = skcms_Matrix3x3_concat(&primaries, &toXYZ); + + skcms_Matrix3x3 DXtoD50; + if (!skcms_AdaptToXYZD50(wx, wy, &DXtoD50)) { + return false; + } + + *toXYZD50 = skcms_Matrix3x3_concat(&DXtoD50, &toXYZ); + return true; +} + + +bool skcms_Matrix3x3_invert(const skcms_Matrix3x3* src, skcms_Matrix3x3* dst) { + double a00 = src->vals[0][0], + a01 = src->vals[1][0], + a02 = src->vals[2][0], + a10 = src->vals[0][1], + a11 = src->vals[1][1], + a12 = src->vals[2][1], + a20 = src->vals[0][2], + a21 = src->vals[1][2], + a22 = src->vals[2][2]; + + double b0 = a00*a11 - a01*a10, + b1 = a00*a12 - a02*a10, + b2 = a01*a12 - a02*a11, + b3 = a20, + b4 = a21, + b5 = a22; + + double determinant = b0*b5 + - b1*b4 + + b2*b3; + + if (determinant == 0) { + return false; + } + + double invdet = 1.0 / determinant; + if (invdet > +FLT_MAX || invdet < -FLT_MAX || !isfinitef_((float)invdet)) { + return false; + } + + b0 *= invdet; + b1 *= invdet; + b2 *= invdet; + b3 *= invdet; + b4 *= invdet; + b5 *= invdet; + + dst->vals[0][0] = (float)( a11*b5 - a12*b4 ); + dst->vals[1][0] = (float)( a02*b4 - a01*b5 ); + dst->vals[2][0] = (float)( + b2 ); + dst->vals[0][1] = (float)( a12*b3 - a10*b5 ); + dst->vals[1][1] = (float)( a00*b5 - a02*b3 ); + dst->vals[2][1] = (float)( - b1 ); + dst->vals[0][2] = (float)( a10*b4 - a11*b3 ); + dst->vals[1][2] = (float)( a01*b3 - a00*b4 ); + dst->vals[2][2] = (float)( + b0 ); + + for (int r = 0; r < 3; ++r) + for (int c = 0; c < 3; ++c) { + if (!isfinitef_(dst->vals[r][c])) { + return false; + } + } + return true; +} + +skcms_Matrix3x3 skcms_Matrix3x3_concat(const skcms_Matrix3x3* A, const skcms_Matrix3x3* B) { + skcms_Matrix3x3 m = { { { 0,0,0 },{ 0,0,0 },{ 0,0,0 } } }; + for (int r = 0; r < 3; r++) + for (int c = 0; c < 3; c++) { + m.vals[r][c] = A->vals[r][0] * B->vals[0][c] + + A->vals[r][1] * B->vals[1][c] + + A->vals[r][2] * B->vals[2][c]; + } + return m; +} + +#if defined(__clang__) + [[clang::no_sanitize("float-divide-by-zero")]] // Checked for by classify() on the way out. +#endif +bool skcms_TransferFunction_invert(const skcms_TransferFunction* src, skcms_TransferFunction* dst) { + TF_PQish pq; + TF_HLGish hlg; + switch (classify(*src, &pq, &hlg)) { + case skcms_TFType_Invalid: return false; + case skcms_TFType_sRGBish: break; // handled below + + case skcms_TFType_PQish: + *dst = { TFKind_marker(skcms_TFType_PQish), -pq.A, pq.D, 1.0f/pq.F + , pq.B, -pq.E, 1.0f/pq.C}; + return true; + + case skcms_TFType_HLGish: + *dst = { TFKind_marker(skcms_TFType_HLGinvish), 1.0f/hlg.R, 1.0f/hlg.G + , 1.0f/hlg.a, hlg.b, hlg.c + , hlg.K_minus_1 }; + return true; + + case skcms_TFType_HLGinvish: + *dst = { TFKind_marker(skcms_TFType_HLGish), 1.0f/hlg.R, 1.0f/hlg.G + , 1.0f/hlg.a, hlg.b, hlg.c + , hlg.K_minus_1 }; + return true; + } + + assert (classify(*src) == skcms_TFType_sRGBish); + + // We're inverting this function, solving for x in terms of y. + // y = (cx + f) x < d + // (ax + b)^g + e x ≥ d + // The inverse of this function can be expressed in the same piecewise form. + skcms_TransferFunction inv = {0,0,0,0,0,0,0}; + + // We'll start by finding the new threshold inv.d. + // In principle we should be able to find that by solving for y at x=d from either side. + // (If those two d values aren't the same, it's a discontinuous transfer function.) + float d_l = src->c * src->d + src->f, + d_r = powf_(src->a * src->d + src->b, src->g) + src->e; + if (fabsf_(d_l - d_r) > 1/512.0f) { + return false; + } + inv.d = d_l; // TODO(mtklein): better in practice to choose d_r? + + // When d=0, the linear section collapses to a point. We leave c,d,f all zero in that case. + if (inv.d > 0) { + // Inverting the linear section is pretty straightfoward: + // y = cx + f + // y - f = cx + // (1/c)y - f/c = x + inv.c = 1.0f/src->c; + inv.f = -src->f/src->c; + } + + // The interesting part is inverting the nonlinear section: + // y = (ax + b)^g + e. + // y - e = (ax + b)^g + // (y - e)^1/g = ax + b + // (y - e)^1/g - b = ax + // (1/a)(y - e)^1/g - b/a = x + // + // To make that fit our form, we need to move the (1/a) term inside the exponentiation: + // let k = (1/a)^g + // (1/a)( y - e)^1/g - b/a = x + // (ky - ke)^1/g - b/a = x + + float k = powf_(src->a, -src->g); // (1/a)^g == a^-g + inv.g = 1.0f / src->g; + inv.a = k; + inv.b = -k * src->e; + inv.e = -src->b / src->a; + + // We need to enforce the same constraints here that we do when fitting a curve, + // a >= 0 and ad+b >= 0. These constraints are checked by classify(), so they're true + // of the source function if we're here. + + // Just like when fitting the curve, there's really no way to rescue a < 0. + if (inv.a < 0) { + return false; + } + // On the other hand we can rescue an ad+b that's gone slightly negative here. + if (inv.a * inv.d + inv.b < 0) { + inv.b = -inv.a * inv.d; + } + + // That should usually make classify(inv) == sRGBish true, but there are a couple situations + // where we might still fail here, like non-finite parameter values. + if (classify(inv) != skcms_TFType_sRGBish) { + return false; + } + + assert (inv.a >= 0); + assert (inv.a * inv.d + inv.b >= 0); + + // Now in principle we're done. + // But to preserve the valuable invariant inv(src(1.0f)) == 1.0f, we'll tweak + // e or f of the inverse, depending on which segment contains src(1.0f). + float s = skcms_TransferFunction_eval(src, 1.0f); + if (!isfinitef_(s)) { + return false; + } + + float sign = s < 0 ? -1.0f : 1.0f; + s *= sign; + if (s < inv.d) { + inv.f = 1.0f - sign * inv.c * s; + } else { + inv.e = 1.0f - sign * powf_(inv.a * s + inv.b, inv.g); + } + + *dst = inv; + return classify(*dst) == skcms_TFType_sRGBish; +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + +// From here below we're approximating an skcms_Curve with an skcms_TransferFunction{g,a,b,c,d,e,f}: +// +// tf(x) = cx + f x < d +// tf(x) = (ax + b)^g + e x ≥ d +// +// When fitting, we add the additional constraint that both pieces meet at d: +// +// cd + f = (ad + b)^g + e +// +// Solving for e and folding it through gives an alternate formulation of the non-linear piece: +// +// tf(x) = cx + f x < d +// tf(x) = (ax + b)^g - (ad + b)^g + cd + f x ≥ d +// +// Our overall strategy is then: +// For a couple tolerances, +// - fit_linear(): fit c,d,f iteratively to as many points as our tolerance allows +// - invert c,d,f +// - fit_nonlinear(): fit g,a,b using Gauss-Newton given those inverted c,d,f +// (and by constraint, inverted e) to the inverse of the table. +// Return the parameters with least maximum error. +// +// To run Gauss-Newton to find g,a,b, we'll also need the gradient of the residuals +// of round-trip f_inv(x), the inverse of the non-linear piece of f(x). +// +// let y = Table(x) +// r(x) = x - f_inv(y) +// +// ∂r/∂g = ln(ay + b)*(ay + b)^g +// - ln(ad + b)*(ad + b)^g +// ∂r/∂a = yg(ay + b)^(g-1) +// - dg(ad + b)^(g-1) +// ∂r/∂b = g(ay + b)^(g-1) +// - g(ad + b)^(g-1) + +// Return the residual of roundtripping skcms_Curve(x) through f_inv(y) with parameters P, +// and fill out the gradient of the residual into dfdP. +static float rg_nonlinear(float x, + const skcms_Curve* curve, + const skcms_TransferFunction* tf, + float dfdP[3]) { + const float y = eval_curve(curve, x); + + const float g = tf->g, a = tf->a, b = tf->b, + c = tf->c, d = tf->d, f = tf->f; + + const float Y = fmaxf_(a*y + b, 0.0f), + D = a*d + b; + assert (D >= 0); + + // The gradient. + dfdP[0] = logf_(Y)*powf_(Y, g) + - logf_(D)*powf_(D, g); + dfdP[1] = y*g*powf_(Y, g-1) + - d*g*powf_(D, g-1); + dfdP[2] = g*powf_(Y, g-1) + - g*powf_(D, g-1); + + // The residual. + const float f_inv = powf_(Y, g) + - powf_(D, g) + + c*d + f; + return x - f_inv; +} + +static bool gauss_newton_step(const skcms_Curve* curve, + skcms_TransferFunction* tf, + float x0, float dx, int N) { + // We'll sample x from the range [x0,x1] (both inclusive) N times with even spacing. + // + // Let P = [ tf->g, tf->a, tf->b ] (the three terms that we're adjusting). + // + // We want to do P' = P + (Jf^T Jf)^-1 Jf^T r(P), + // where r(P) is the residual vector + // and Jf is the Jacobian matrix of f(), ∂r/∂P. + // + // Let's review the shape of each of these expressions: + // r(P) is [N x 1], a column vector with one entry per value of x tested + // Jf is [N x 3], a matrix with an entry for each (x,P) pair + // Jf^T is [3 x N], the transpose of Jf + // + // Jf^T Jf is [3 x N] * [N x 3] == [3 x 3], a 3x3 matrix, + // and so is its inverse (Jf^T Jf)^-1 + // Jf^T r(P) is [3 x N] * [N x 1] == [3 x 1], a column vector with the same shape as P + // + // Our implementation strategy to get to the final ∆P is + // 1) evaluate Jf^T Jf, call that lhs + // 2) evaluate Jf^T r(P), call that rhs + // 3) invert lhs + // 4) multiply inverse lhs by rhs + // + // This is a friendly implementation strategy because we don't have to have any + // buffers that scale with N, and equally nice don't have to perform any matrix + // operations that are variable size. + // + // Other implementation strategies could trade this off, e.g. evaluating the + // pseudoinverse of Jf ( (Jf^T Jf)^-1 Jf^T ) directly, then multiplying that by + // the residuals. That would probably require implementing singular value + // decomposition, and would create a [3 x N] matrix to be multiplied by the + // [N x 1] residual vector, but on the upside I think that'd eliminate the + // possibility of this gauss_newton_step() function ever failing. + + // 0) start off with lhs and rhs safely zeroed. + skcms_Matrix3x3 lhs = {{ {0,0,0}, {0,0,0}, {0,0,0} }}; + skcms_Vector3 rhs = { {0,0,0} }; + + // 1,2) evaluate lhs and evaluate rhs + // We want to evaluate Jf only once, but both lhs and rhs involve Jf^T, + // so we'll have to update lhs and rhs at the same time. + for (int i = 0; i < N; i++) { + float x = x0 + static_cast(i)*dx; + + float dfdP[3] = {0,0,0}; + float resid = rg_nonlinear(x,curve,tf, dfdP); + + for (int r = 0; r < 3; r++) { + for (int c = 0; c < 3; c++) { + lhs.vals[r][c] += dfdP[r] * dfdP[c]; + } + rhs.vals[r] += dfdP[r] * resid; + } + } + + // If any of the 3 P parameters are unused, this matrix will be singular. + // Detect those cases and fix them up to indentity instead, so we can invert. + for (int k = 0; k < 3; k++) { + if (lhs.vals[0][k]==0 && lhs.vals[1][k]==0 && lhs.vals[2][k]==0 && + lhs.vals[k][0]==0 && lhs.vals[k][1]==0 && lhs.vals[k][2]==0) { + lhs.vals[k][k] = 1; + } + } + + // 3) invert lhs + skcms_Matrix3x3 lhs_inv; + if (!skcms_Matrix3x3_invert(&lhs, &lhs_inv)) { + return false; + } + + // 4) multiply inverse lhs by rhs + skcms_Vector3 dP = mv_mul(&lhs_inv, &rhs); + tf->g += dP.vals[0]; + tf->a += dP.vals[1]; + tf->b += dP.vals[2]; + return isfinitef_(tf->g) && isfinitef_(tf->a) && isfinitef_(tf->b); +} + +static float max_roundtrip_error_checked(const skcms_Curve* curve, + const skcms_TransferFunction* tf_inv) { + skcms_TransferFunction tf; + if (!skcms_TransferFunction_invert(tf_inv, &tf) || skcms_TFType_sRGBish != classify(tf)) { + return INFINITY_; + } + + skcms_TransferFunction tf_inv_again; + if (!skcms_TransferFunction_invert(&tf, &tf_inv_again)) { + return INFINITY_; + } + + return skcms_MaxRoundtripError(curve, &tf_inv_again); +} + +// Fit the points in [L,N) to the non-linear piece of tf, or return false if we can't. +static bool fit_nonlinear(const skcms_Curve* curve, int L, int N, skcms_TransferFunction* tf) { + // This enforces a few constraints that are not modeled in gauss_newton_step()'s optimization. + auto fixup_tf = [tf]() { + // a must be non-negative. That ensures the function is monotonically increasing. + // We don't really know how to fix up a if it goes negative. + if (tf->a < 0) { + return false; + } + // ad+b must be non-negative. That ensures we don't end up with complex numbers in powf. + // We feel just barely not uneasy enough to tweak b so ad+b is zero in this case. + if (tf->a * tf->d + tf->b < 0) { + tf->b = -tf->a * tf->d; + } + assert (tf->a >= 0 && + tf->a * tf->d + tf->b >= 0); + + // cd+f must be ~= (ad+b)^g+e. That ensures the function is continuous. We keep e as a free + // parameter so we can guarantee this. + tf->e = tf->c*tf->d + tf->f + - powf_(tf->a*tf->d + tf->b, tf->g); + + return isfinitef_(tf->e); + }; + + if (!fixup_tf()) { + return false; + } + + // No matter where we start, dx should always represent N even steps from 0 to 1. + const float dx = 1.0f / static_cast(N-1); + + skcms_TransferFunction best_tf = *tf; + float best_max_error = INFINITY_; + + // Need this or several curves get worse... *sigh* + float init_error = max_roundtrip_error_checked(curve, tf); + if (init_error < best_max_error) { + best_max_error = init_error; + best_tf = *tf; + } + + // As far as we can tell, 1 Gauss-Newton step won't converge, and 3 steps is no better than 2. + for (int j = 0; j < 8; j++) { + if (!gauss_newton_step(curve, tf, static_cast(L)*dx, dx, N-L) || !fixup_tf()) { + *tf = best_tf; + return isfinitef_(best_max_error); + } + + float max_error = max_roundtrip_error_checked(curve, tf); + if (max_error < best_max_error) { + best_max_error = max_error; + best_tf = *tf; + } + } + + *tf = best_tf; + return isfinitef_(best_max_error); +} + +bool skcms_ApproximateCurve(const skcms_Curve* curve, + skcms_TransferFunction* approx, + float* max_error) { + if (!curve || !approx || !max_error) { + return false; + } + + if (curve->table_entries == 0) { + // No point approximating an skcms_TransferFunction with an skcms_TransferFunction! + return false; + } + + if (curve->table_entries == 1 || curve->table_entries > (uint32_t)INT_MAX) { + // We need at least two points, and must put some reasonable cap on the maximum number. + return false; + } + + int N = (int)curve->table_entries; + const float dx = 1.0f / static_cast(N - 1); + + *max_error = INFINITY_; + const float kTolerances[] = { 1.5f / 65535.0f, 1.0f / 512.0f }; + for (int t = 0; t < ARRAY_COUNT(kTolerances); t++) { + skcms_TransferFunction tf, + tf_inv; + + // It's problematic to fit curves with non-zero f, so always force it to zero explicitly. + tf.f = 0.0f; + int L = fit_linear(curve, N, kTolerances[t], &tf.c, &tf.d); + + if (L == N) { + // If the entire data set was linear, move the coefficients to the nonlinear portion + // with G == 1. This lets use a canonical representation with d == 0. + tf.g = 1; + tf.a = tf.c; + tf.b = tf.f; + tf.c = tf.d = tf.e = tf.f = 0; + } else if (L == N - 1) { + // Degenerate case with only two points in the nonlinear segment. Solve directly. + tf.g = 1; + tf.a = (eval_curve(curve, static_cast(N-1)*dx) - + eval_curve(curve, static_cast(N-2)*dx)) + / dx; + tf.b = eval_curve(curve, static_cast(N-2)*dx) + - tf.a * static_cast(N-2)*dx; + tf.e = 0; + } else { + // Start by guessing a gamma-only curve through the midpoint. + int mid = (L + N) / 2; + float mid_x = static_cast(mid) / static_cast(N - 1); + float mid_y = eval_curve(curve, mid_x); + tf.g = log2f_(mid_y) / log2f_(mid_x); + tf.a = 1; + tf.b = 0; + tf.e = tf.c*tf.d + tf.f + - powf_(tf.a*tf.d + tf.b, tf.g); + + + if (!skcms_TransferFunction_invert(&tf, &tf_inv) || + !fit_nonlinear(curve, L,N, &tf_inv)) { + continue; + } + + // We fit tf_inv, so calculate tf to keep in sync. + // fit_nonlinear() should guarantee invertibility. + if (!skcms_TransferFunction_invert(&tf_inv, &tf)) { + assert(false); + continue; + } + } + + // We'd better have a sane, sRGB-ish TF by now. + // Other non-Bad TFs would be fine, but we know we've only ever tried to fit sRGBish; + // anything else is just some accident of math and the way we pun tf.g as a type flag. + // fit_nonlinear() should guarantee this, but the special cases may fail this test. + if (skcms_TFType_sRGBish != classify(tf)) { + continue; + } + + // We find our error by roundtripping the table through tf_inv. + // + // (The most likely use case for this approximation is to be inverted and + // used as the transfer function for a destination color space.) + // + // We've kept tf and tf_inv in sync above, but we can't guarantee that tf is + // invertible, so re-verify that here (and use the new inverse for testing). + // fit_nonlinear() should guarantee this, but the special cases that don't use + // it may fail this test. + if (!skcms_TransferFunction_invert(&tf, &tf_inv)) { + continue; + } + + float err = skcms_MaxRoundtripError(curve, &tf_inv); + if (*max_error > err) { + *max_error = err; + *approx = tf; + } + } + return isfinitef_(*max_error); +} + +enum class CpuType { Baseline, HSW, SKX }; + +static CpuType cpu_type() { + #if defined(SKCMS_PORTABLE) || !defined(__x86_64__) || defined(SKCMS_FORCE_BASELINE) + return CpuType::Baseline; + #elif defined(SKCMS_FORCE_HSW) + return CpuType::HSW; + #elif defined(SKCMS_FORCE_SKX) + return CpuType::SKX; + #else + static const CpuType type = []{ + if (!sAllowRuntimeCPUDetection) { + return CpuType::Baseline; + } + // See http://www.sandpile.org/x86/cpuid.htm + + // First, a basic cpuid(1) lets us check prerequisites for HSW, SKX. + uint32_t eax, ebx, ecx, edx; + __asm__ __volatile__("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) + : "0"(1), "2"(0)); + if ((edx & (1u<<25)) && // SSE + (edx & (1u<<26)) && // SSE2 + (ecx & (1u<< 0)) && // SSE3 + (ecx & (1u<< 9)) && // SSSE3 + (ecx & (1u<<12)) && // FMA (N.B. not used, avoided even) + (ecx & (1u<<19)) && // SSE4.1 + (ecx & (1u<<20)) && // SSE4.2 + (ecx & (1u<<26)) && // XSAVE + (ecx & (1u<<27)) && // OSXSAVE + (ecx & (1u<<28)) && // AVX + (ecx & (1u<<29))) { // F16C + + // Call cpuid(7) to check for AVX2 and AVX-512 bits. + __asm__ __volatile__("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) + : "0"(7), "2"(0)); + // eax from xgetbv(0) will tell us whether XMM, YMM, and ZMM state is saved. + uint32_t xcr0, dont_need_edx; + __asm__ __volatile__("xgetbv" : "=a"(xcr0), "=d"(dont_need_edx) : "c"(0)); + + if ((xcr0 & (1u<<1)) && // XMM register state saved? + (xcr0 & (1u<<2)) && // YMM register state saved? + (ebx & (1u<<5))) { // AVX2 + // At this point we're at least HSW. Continue checking for SKX. + if ((xcr0 & (1u<< 5)) && // Opmasks state saved? + (xcr0 & (1u<< 6)) && // First 16 ZMM registers saved? + (xcr0 & (1u<< 7)) && // High 16 ZMM registers saved? + (ebx & (1u<<16)) && // AVX512F + (ebx & (1u<<17)) && // AVX512DQ + (ebx & (1u<<28)) && // AVX512CD + (ebx & (1u<<30)) && // AVX512BW + (ebx & (1u<<31))) { // AVX512VL + return CpuType::SKX; + } + return CpuType::HSW; + } + } + return CpuType::Baseline; + }(); + return type; + #endif +} + +static bool tf_is_gamma(const skcms_TransferFunction& tf) { + return tf.g > 0 && tf.a == 1 && + tf.b == 0 && tf.c == 0 && tf.d == 0 && tf.e == 0 && tf.f == 0; +} + +struct OpAndArg { + Op op; + const void* arg; +}; + +static OpAndArg select_curve_op(const skcms_Curve* curve, int channel) { + struct OpType { + Op sGamma, sRGBish, PQish, HLGish, HLGinvish, table; + }; + static constexpr OpType kOps[] = { + { Op::gamma_r, Op::tf_r, Op::pq_r, Op::hlg_r, Op::hlginv_r, Op::table_r }, + { Op::gamma_g, Op::tf_g, Op::pq_g, Op::hlg_g, Op::hlginv_g, Op::table_g }, + { Op::gamma_b, Op::tf_b, Op::pq_b, Op::hlg_b, Op::hlginv_b, Op::table_b }, + { Op::gamma_a, Op::tf_a, Op::pq_a, Op::hlg_a, Op::hlginv_a, Op::table_a }, + }; + const auto& op = kOps[channel]; + + if (curve->table_entries == 0) { + const OpAndArg noop = { Op::load_a8/*doesn't matter*/, nullptr }; + + const skcms_TransferFunction& tf = curve->parametric; + + if (tf_is_gamma(tf)) { + return tf.g != 1 ? OpAndArg{op.sGamma, &tf} + : noop; + } + + switch (classify(tf)) { + case skcms_TFType_Invalid: return noop; + case skcms_TFType_sRGBish: return OpAndArg{op.sRGBish, &tf}; + case skcms_TFType_PQish: return OpAndArg{op.PQish, &tf}; + case skcms_TFType_HLGish: return OpAndArg{op.HLGish, &tf}; + case skcms_TFType_HLGinvish: return OpAndArg{op.HLGinvish, &tf}; + } + } + return OpAndArg{op.table, curve}; +} + +static int select_curve_ops(const skcms_Curve* curves, int numChannels, OpAndArg* ops) { + // We process the channels in reverse order, yielding ops in ABGR order. + // (Working backwards allows us to fuse trailing B+G+R ops into a single RGB op.) + int cursor = 0; + for (int index = numChannels; index-- > 0; ) { + ops[cursor] = select_curve_op(&curves[index], index); + if (ops[cursor].arg) { + ++cursor; + } + } + + // Identify separate B+G+R ops and fuse them into a single RGB op. + if (cursor >= 3) { + struct FusableOps { + Op r, g, b, rgb; + }; + static constexpr FusableOps kFusableOps[] = { + {Op::gamma_r, Op::gamma_g, Op::gamma_b, Op::gamma_rgb}, + {Op::tf_r, Op::tf_g, Op::tf_b, Op::tf_rgb}, + {Op::pq_r, Op::pq_g, Op::pq_b, Op::pq_rgb}, + {Op::hlg_r, Op::hlg_g, Op::hlg_b, Op::hlg_rgb}, + {Op::hlginv_r, Op::hlginv_g, Op::hlginv_b, Op::hlginv_rgb}, + }; + + int posR = cursor - 1; + int posG = cursor - 2; + int posB = cursor - 3; + for (const FusableOps& fusableOp : kFusableOps) { + if (ops[posR].op == fusableOp.r && + ops[posG].op == fusableOp.g && + ops[posB].op == fusableOp.b && + (0 == memcmp(ops[posR].arg, ops[posG].arg, sizeof(skcms_TransferFunction))) && + (0 == memcmp(ops[posR].arg, ops[posB].arg, sizeof(skcms_TransferFunction)))) { + // Fuse the three matching ops into one. + ops[posB].op = fusableOp.rgb; + cursor -= 2; + break; + } + } + } + + return cursor; +} + +static size_t bytes_per_pixel(skcms_PixelFormat fmt) { + switch (fmt >> 1) { // ignore rgb/bgr + case skcms_PixelFormat_A_8 >> 1: return 1; + case skcms_PixelFormat_G_8 >> 1: return 1; + case skcms_PixelFormat_ABGR_4444 >> 1: return 2; + case skcms_PixelFormat_RGB_565 >> 1: return 2; + case skcms_PixelFormat_RGB_888 >> 1: return 3; + case skcms_PixelFormat_RGBA_8888 >> 1: return 4; + case skcms_PixelFormat_RGBA_8888_sRGB >> 1: return 4; + case skcms_PixelFormat_RGBA_1010102 >> 1: return 4; + case skcms_PixelFormat_RGB_101010x_XR >> 1: return 4; + case skcms_PixelFormat_RGB_161616LE >> 1: return 6; + case skcms_PixelFormat_RGBA_10101010_XR >> 1: return 8; + case skcms_PixelFormat_RGBA_16161616LE >> 1: return 8; + case skcms_PixelFormat_RGB_161616BE >> 1: return 6; + case skcms_PixelFormat_RGBA_16161616BE >> 1: return 8; + case skcms_PixelFormat_RGB_hhh_Norm >> 1: return 6; + case skcms_PixelFormat_RGBA_hhhh_Norm >> 1: return 8; + case skcms_PixelFormat_RGB_hhh >> 1: return 6; + case skcms_PixelFormat_RGBA_hhhh >> 1: return 8; + case skcms_PixelFormat_RGB_fff >> 1: return 12; + case skcms_PixelFormat_RGBA_ffff >> 1: return 16; + } + assert(false); + return 0; +} + +static bool prep_for_destination(const skcms_ICCProfile* profile, + skcms_Matrix3x3* fromXYZD50, + skcms_TransferFunction* invR, + skcms_TransferFunction* invG, + skcms_TransferFunction* invB) { + // skcms_Transform() supports B2A destinations... + if (profile->has_B2A) { return true; } + // ...and destinations with parametric transfer functions and an XYZD50 gamut matrix. + return profile->has_trc + && profile->has_toXYZD50 + && profile->trc[0].table_entries == 0 + && profile->trc[1].table_entries == 0 + && profile->trc[2].table_entries == 0 + && skcms_TransferFunction_invert(&profile->trc[0].parametric, invR) + && skcms_TransferFunction_invert(&profile->trc[1].parametric, invG) + && skcms_TransferFunction_invert(&profile->trc[2].parametric, invB) + && skcms_Matrix3x3_invert(&profile->toXYZD50, fromXYZD50); +} + +bool skcms_Transform(const void* src, + skcms_PixelFormat srcFmt, + skcms_AlphaFormat srcAlpha, + const skcms_ICCProfile* srcProfile, + void* dst, + skcms_PixelFormat dstFmt, + skcms_AlphaFormat dstAlpha, + const skcms_ICCProfile* dstProfile, + size_t nz) { + const size_t dst_bpp = bytes_per_pixel(dstFmt), + src_bpp = bytes_per_pixel(srcFmt); + // Let's just refuse if the request is absurdly big. + if (nz * dst_bpp > INT_MAX || nz * src_bpp > INT_MAX) { + return false; + } + int n = (int)nz; + + // Null profiles default to sRGB. Passing null for both is handy when doing format conversion. + if (!srcProfile) { + srcProfile = skcms_sRGB_profile(); + } + if (!dstProfile) { + dstProfile = skcms_sRGB_profile(); + } + + // We can't transform in place unless the PixelFormats are the same size. + if (dst == src && dst_bpp != src_bpp) { + return false; + } + // TODO: more careful alias rejection (like, dst == src + 1)? + + Op program[32]; + const void* context[32]; + + Op* ops = program; + const void** contexts = context; + + auto add_op = [&](Op o) { + *ops++ = o; + *contexts++ = nullptr; + }; + + auto add_op_ctx = [&](Op o, const void* c) { + *ops++ = o; + *contexts++ = c; + }; + + auto add_curve_ops = [&](const skcms_Curve* curves, int numChannels) { + OpAndArg oa[4]; + assert(numChannels <= ARRAY_COUNT(oa)); + + int numOps = select_curve_ops(curves, numChannels, oa); + + for (int i = 0; i < numOps; ++i) { + add_op_ctx(oa[i].op, oa[i].arg); + } + }; + + // These are always parametric curves of some sort. + skcms_Curve dst_curves[3]; + dst_curves[0].table_entries = + dst_curves[1].table_entries = + dst_curves[2].table_entries = 0; + + skcms_Matrix3x3 from_xyz; + + switch (srcFmt >> 1) { + default: return false; + case skcms_PixelFormat_A_8 >> 1: add_op(Op::load_a8); break; + case skcms_PixelFormat_G_8 >> 1: add_op(Op::load_g8); break; + case skcms_PixelFormat_ABGR_4444 >> 1: add_op(Op::load_4444); break; + case skcms_PixelFormat_RGB_565 >> 1: add_op(Op::load_565); break; + case skcms_PixelFormat_RGB_888 >> 1: add_op(Op::load_888); break; + case skcms_PixelFormat_RGBA_8888 >> 1: add_op(Op::load_8888); break; + case skcms_PixelFormat_RGBA_1010102 >> 1: add_op(Op::load_1010102); break; + case skcms_PixelFormat_RGB_101010x_XR >> 1: add_op(Op::load_101010x_XR); break; + case skcms_PixelFormat_RGBA_10101010_XR >> 1: add_op(Op::load_10101010_XR); break; + case skcms_PixelFormat_RGB_161616LE >> 1: add_op(Op::load_161616LE); break; + case skcms_PixelFormat_RGBA_16161616LE >> 1: add_op(Op::load_16161616LE); break; + case skcms_PixelFormat_RGB_161616BE >> 1: add_op(Op::load_161616BE); break; + case skcms_PixelFormat_RGBA_16161616BE >> 1: add_op(Op::load_16161616BE); break; + case skcms_PixelFormat_RGB_hhh_Norm >> 1: add_op(Op::load_hhh); break; + case skcms_PixelFormat_RGBA_hhhh_Norm >> 1: add_op(Op::load_hhhh); break; + case skcms_PixelFormat_RGB_hhh >> 1: add_op(Op::load_hhh); break; + case skcms_PixelFormat_RGBA_hhhh >> 1: add_op(Op::load_hhhh); break; + case skcms_PixelFormat_RGB_fff >> 1: add_op(Op::load_fff); break; + case skcms_PixelFormat_RGBA_ffff >> 1: add_op(Op::load_ffff); break; + + case skcms_PixelFormat_RGBA_8888_sRGB >> 1: + add_op(Op::load_8888); + add_op_ctx(Op::tf_rgb, skcms_sRGB_TransferFunction()); + break; + } + if (srcFmt == skcms_PixelFormat_RGB_hhh_Norm || + srcFmt == skcms_PixelFormat_RGBA_hhhh_Norm) { + add_op(Op::clamp); + } + if (srcFmt & 1) { + add_op(Op::swap_rb); + } + skcms_ICCProfile gray_dst_profile; + if ((dstFmt >> 1) == (skcms_PixelFormat_G_8 >> 1)) { + // When transforming to gray, stop at XYZ (by setting toXYZ to identity), then transform + // luminance (Y) by the destination transfer function. + gray_dst_profile = *dstProfile; + skcms_SetXYZD50(&gray_dst_profile, &skcms_XYZD50_profile()->toXYZD50); + dstProfile = &gray_dst_profile; + } + + if (srcProfile->data_color_space == skcms_Signature_CMYK) { + // Photoshop creates CMYK images as inverse CMYK. + // These happen to be the only ones we've _ever_ seen. + add_op(Op::invert); + // With CMYK, ignore the alpha type, to avoid changing K or conflating CMY with K. + srcAlpha = skcms_AlphaFormat_Unpremul; + } + + if (srcAlpha == skcms_AlphaFormat_Opaque) { + add_op(Op::force_opaque); + } else if (srcAlpha == skcms_AlphaFormat_PremulAsEncoded) { + add_op(Op::unpremul); + } + + if (dstProfile != srcProfile) { + + if (!prep_for_destination(dstProfile, + &from_xyz, + &dst_curves[0].parametric, + &dst_curves[1].parametric, + &dst_curves[2].parametric)) { + return false; + } + + if (srcProfile->has_A2B) { + if (srcProfile->A2B.input_channels) { + add_curve_ops(srcProfile->A2B.input_curves, + (int)srcProfile->A2B.input_channels); + add_op(Op::clamp); + add_op_ctx(Op::clut_A2B, &srcProfile->A2B); + } + + if (srcProfile->A2B.matrix_channels == 3) { + add_curve_ops(srcProfile->A2B.matrix_curves, /*numChannels=*/3); + + static const skcms_Matrix3x4 I = {{ + {1,0,0,0}, + {0,1,0,0}, + {0,0,1,0}, + }}; + if (0 != memcmp(&I, &srcProfile->A2B.matrix, sizeof(I))) { + add_op_ctx(Op::matrix_3x4, &srcProfile->A2B.matrix); + } + } + + if (srcProfile->A2B.output_channels == 3) { + add_curve_ops(srcProfile->A2B.output_curves, /*numChannels=*/3); + } + + if (srcProfile->pcs == skcms_Signature_Lab) { + add_op(Op::lab_to_xyz); + } + + } else if (srcProfile->has_trc && srcProfile->has_toXYZD50) { + add_curve_ops(srcProfile->trc, /*numChannels=*/3); + } else { + return false; + } + + // A2B sources are in XYZD50 by now, but TRC sources are still in their original gamut. + assert (srcProfile->has_A2B || srcProfile->has_toXYZD50); + + if (dstProfile->has_B2A) { + // B2A needs its input in XYZD50, so transform TRC sources now. + if (!srcProfile->has_A2B) { + add_op_ctx(Op::matrix_3x3, &srcProfile->toXYZD50); + } + + if (dstProfile->pcs == skcms_Signature_Lab) { + add_op(Op::xyz_to_lab); + } + + if (dstProfile->B2A.input_channels == 3) { + add_curve_ops(dstProfile->B2A.input_curves, /*numChannels=*/3); + } + + if (dstProfile->B2A.matrix_channels == 3) { + static const skcms_Matrix3x4 I = {{ + {1,0,0,0}, + {0,1,0,0}, + {0,0,1,0}, + }}; + if (0 != memcmp(&I, &dstProfile->B2A.matrix, sizeof(I))) { + add_op_ctx(Op::matrix_3x4, &dstProfile->B2A.matrix); + } + + add_curve_ops(dstProfile->B2A.matrix_curves, /*numChannels=*/3); + } + + if (dstProfile->B2A.output_channels) { + add_op(Op::clamp); + add_op_ctx(Op::clut_B2A, &dstProfile->B2A); + + add_curve_ops(dstProfile->B2A.output_curves, + (int)dstProfile->B2A.output_channels); + } + } else { + // This is a TRC destination. + // We'll concat any src->xyz matrix with our xyz->dst matrix into one src->dst matrix. + // (A2B sources are already in XYZD50, making that src->xyz matrix I.) + static const skcms_Matrix3x3 I = {{ + { 1.0f, 0.0f, 0.0f }, + { 0.0f, 1.0f, 0.0f }, + { 0.0f, 0.0f, 1.0f }, + }}; + const skcms_Matrix3x3* to_xyz = srcProfile->has_A2B ? &I : &srcProfile->toXYZD50; + + // There's a chance the source and destination gamuts are identical, + // in which case we can skip the gamut transform. + if (0 != memcmp(&dstProfile->toXYZD50, to_xyz, sizeof(skcms_Matrix3x3))) { + // Concat the entire gamut transform into from_xyz, + // now slightly misnamed but it's a handy spot to stash the result. + from_xyz = skcms_Matrix3x3_concat(&from_xyz, to_xyz); + add_op_ctx(Op::matrix_3x3, &from_xyz); + } + + // Encode back to dst RGB using its parametric transfer functions. + OpAndArg oa[3]; + int numOps = select_curve_ops(dst_curves, /*numChannels=*/3, oa); + for (int index = 0; index < numOps; ++index) { + assert(oa[index].op != Op::table_r && + oa[index].op != Op::table_g && + oa[index].op != Op::table_b && + oa[index].op != Op::table_a); + add_op_ctx(oa[index].op, oa[index].arg); + } + } + } + + // Clamp here before premul to make sure we're clamping to normalized values _and_ gamut, + // not just to values that fit in [0,1]. + // + // E.g. r = 1.1, a = 0.5 would fit fine in fixed point after premul (ra=0.55,a=0.5), + // but would be carrying r > 1, which is really unexpected for downstream consumers. + if (dstFmt < skcms_PixelFormat_RGB_hhh) { + add_op(Op::clamp); + } + + if (dstProfile->data_color_space == skcms_Signature_CMYK) { + // Photoshop creates CMYK images as inverse CMYK. + // These happen to be the only ones we've _ever_ seen. + add_op(Op::invert); + + // CMYK has no alpha channel, so make sure dstAlpha is a no-op. + dstAlpha = skcms_AlphaFormat_Unpremul; + } + + if (dstAlpha == skcms_AlphaFormat_Opaque) { + add_op(Op::force_opaque); + } else if (dstAlpha == skcms_AlphaFormat_PremulAsEncoded) { + add_op(Op::premul); + } + if (dstFmt & 1) { + add_op(Op::swap_rb); + } + switch (dstFmt >> 1) { + default: return false; + case skcms_PixelFormat_A_8 >> 1: add_op(Op::store_a8); break; + case skcms_PixelFormat_G_8 >> 1: add_op(Op::store_g8); break; + case skcms_PixelFormat_ABGR_4444 >> 1: add_op(Op::store_4444); break; + case skcms_PixelFormat_RGB_565 >> 1: add_op(Op::store_565); break; + case skcms_PixelFormat_RGB_888 >> 1: add_op(Op::store_888); break; + case skcms_PixelFormat_RGBA_8888 >> 1: add_op(Op::store_8888); break; + case skcms_PixelFormat_RGBA_1010102 >> 1: add_op(Op::store_1010102); break; + case skcms_PixelFormat_RGB_161616LE >> 1: add_op(Op::store_161616LE); break; + case skcms_PixelFormat_RGBA_16161616LE >> 1: add_op(Op::store_16161616LE); break; + case skcms_PixelFormat_RGB_161616BE >> 1: add_op(Op::store_161616BE); break; + case skcms_PixelFormat_RGBA_16161616BE >> 1: add_op(Op::store_16161616BE); break; + 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_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; + case skcms_PixelFormat_RGBA_ffff >> 1: add_op(Op::store_ffff); break; + + case skcms_PixelFormat_RGBA_8888_sRGB >> 1: + add_op_ctx(Op::tf_rgb, skcms_sRGB_Inverse_TransferFunction()); + add_op(Op::store_8888); + break; + } + + assert(ops <= program + ARRAY_COUNT(program)); + assert(contexts <= context + ARRAY_COUNT(context)); + + auto run = baseline::run_program; + switch (cpu_type()) { + case CpuType::SKX: + #if !defined(SKCMS_DISABLE_SKX) + run = skx::run_program; + break; + #endif + + case CpuType::HSW: + #if !defined(SKCMS_DISABLE_HSW) + run = hsw::run_program; + break; + #endif + + case CpuType::Baseline: + break; + } + + run(program, context, ops - program, (const char*)src, (char*)dst, n, src_bpp,dst_bpp); + return true; +} + +static void assert_usable_as_destination(const skcms_ICCProfile* profile) { +#if defined(NDEBUG) + (void)profile; +#else + skcms_Matrix3x3 fromXYZD50; + skcms_TransferFunction invR, invG, invB; + assert(prep_for_destination(profile, &fromXYZD50, &invR, &invG, &invB)); +#endif +} + +bool skcms_MakeUsableAsDestination(skcms_ICCProfile* profile) { + if (!profile->has_B2A) { + skcms_Matrix3x3 fromXYZD50; + if (!profile->has_trc || !profile->has_toXYZD50 + || !skcms_Matrix3x3_invert(&profile->toXYZD50, &fromXYZD50)) { + return false; + } + + skcms_TransferFunction tf[3]; + for (int i = 0; i < 3; i++) { + skcms_TransferFunction inv; + if (profile->trc[i].table_entries == 0 + && skcms_TransferFunction_invert(&profile->trc[i].parametric, &inv)) { + tf[i] = profile->trc[i].parametric; + continue; + } + + float max_error; + // Parametric curves from skcms_ApproximateCurve() are guaranteed to be invertible. + if (!skcms_ApproximateCurve(&profile->trc[i], &tf[i], &max_error)) { + return false; + } + } + + for (int i = 0; i < 3; ++i) { + profile->trc[i].table_entries = 0; + profile->trc[i].parametric = tf[i]; + } + } + assert_usable_as_destination(profile); + return true; +} + +bool skcms_MakeUsableAsDestinationWithSingleCurve(skcms_ICCProfile* profile) { + // Call skcms_MakeUsableAsDestination() with B2A disabled; + // on success that'll return a TRC/XYZ profile with three skcms_TransferFunctions. + skcms_ICCProfile result = *profile; + result.has_B2A = false; + if (!skcms_MakeUsableAsDestination(&result)) { + return false; + } + + // Of the three, pick the transfer function that best fits the other two. + int best_tf = 0; + float min_max_error = INFINITY_; + for (int i = 0; i < 3; i++) { + skcms_TransferFunction inv; + if (!skcms_TransferFunction_invert(&result.trc[i].parametric, &inv)) { + return false; + } + + float err = 0; + for (int j = 0; j < 3; ++j) { + err = fmaxf_(err, skcms_MaxRoundtripError(&profile->trc[j], &inv)); + } + if (min_max_error > err) { + min_max_error = err; + best_tf = i; + } + } + + for (int i = 0; i < 3; i++) { + result.trc[i].parametric = result.trc[best_tf].parametric; + } + + *profile = result; + assert_usable_as_destination(profile); + return true; +} diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/skcms.gni b/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/skcms.gni new file mode 100644 index 0000000000..62fe24873d --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/skcms.gni @@ -0,0 +1,57 @@ +# DO NOT EDIT: This is a generated file. +# See //bazel/exporter_tool/README.md for more information. +# +# The source of truth is //modules/skcms/BUILD.bazel + +# To update this file, run make -C bazel generate_gni + +_modules = get_path_info("../../modules", "abspath") + +# Generated by Bazel rule //modules/skcms:public_hdrs +skcms_public_headers = [ "$_modules/skcms/skcms.h" ] + +# List generated by Bazel rules: +# //modules/skcms:srcs +# //modules/skcms:textual_hdrs +skcms_sources = [ + "$_modules/skcms/skcms.cc", + "$_modules/skcms/src/Transform_inl.h", + "$_modules/skcms/src/skcms_Transform.h", + "$_modules/skcms/src/skcms_TransformBaseline.cc", + "$_modules/skcms/src/skcms_TransformHsw.cc", + "$_modules/skcms/src/skcms_TransformSkx.cc", + "$_modules/skcms/src/skcms_internals.h", + "$_modules/skcms/src/skcms_public.h", +] + +# Generated by Bazel rule //modules/skcms:skcms_public +skcms_public = [ + "$_modules/skcms/skcms.cc", + "$_modules/skcms/skcms.h", + "$_modules/skcms/src/skcms_internals.h", + "$_modules/skcms/src/skcms_public.h", +] + +# Generated by Bazel rule //modules/skcms:skcms_TransformBaseline +skcms_TransformBaseline = [ + "$_modules/skcms/src/skcms_Transform.h", + "$_modules/skcms/src/skcms_TransformBaseline.cc", + "$_modules/skcms/src/skcms_internals.h", + "$_modules/skcms/src/skcms_public.h", +] + +# Generated by Bazel rule //modules/skcms:skcms_TransformHsw +skcms_TransformHsw = [ + "$_modules/skcms/src/skcms_Transform.h", + "$_modules/skcms/src/skcms_TransformHsw.cc", + "$_modules/skcms/src/skcms_internals.h", + "$_modules/skcms/src/skcms_public.h", +] + +# Generated by Bazel rule //modules/skcms:skcms_TransformSkx +skcms_TransformSkx = [ + "$_modules/skcms/src/skcms_Transform.h", + "$_modules/skcms/src/skcms_TransformSkx.cc", + "$_modules/skcms/src/skcms_internals.h", + "$_modules/skcms/src/skcms_public.h", +] diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/skcms.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/skcms.h new file mode 100644 index 0000000000..7a9d4c1897 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/skcms.h @@ -0,0 +1,10 @@ +/* + * Copyright 2023 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#pragma once + +#include "src/skcms_public.h" // NO_G3_REWRITE diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/src/Transform_inl.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/src/Transform_inl.h new file mode 100644 index 0000000000..b9c27ac395 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/src/Transform_inl.h @@ -0,0 +1,1541 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +// Intentionally NO #pragma once... included multiple times. + +// This file is included from skcms.cc in a namespace with some pre-defines: +// - N: SIMD width of all vectors; 1, 4, 8 or 16 (preprocessor define) +// - V: a template to create a vector of N T's. + +using F = V; +using I32 = V; +using U64 = V; +using U32 = V; +using U16 = V; +using U8 = V; + +#if defined(__GNUC__) && !defined(__clang__) + // GCC is kind of weird, not allowing vector = scalar directly. + static constexpr F F0 = F() + 0.0f, + F1 = F() + 1.0f, + FInfBits = F() + 0x7f800000; // equals 2139095040, the bit pattern of +Inf +#else + static constexpr F F0 = 0.0f, + F1 = 1.0f, + FInfBits = 0x7f800000; // equals 2139095040, the bit pattern of +Inf +#endif + +// Instead of checking __AVX__ below, we'll check USING_AVX. +// This lets skcms.cc set USING_AVX to force us in even if the compiler's not set that way. +// Same deal for __F16C__ and __AVX2__ ~~~> USING_AVX_F16C, USING_AVX2. + +#if !defined(USING_AVX) && N == 8 && defined(__AVX__) + #define USING_AVX +#endif +#if !defined(USING_AVX_F16C) && defined(USING_AVX) && defined(__F16C__) + #define USING_AVX_F16C +#endif +#if !defined(USING_AVX2) && defined(USING_AVX) && defined(__AVX2__) + #define USING_AVX2 +#endif +#if !defined(USING_AVX512F) && N == 16 && defined(__AVX512F__) && defined(__AVX512DQ__) + #define USING_AVX512F +#endif + +// Similar to the AVX+ features, we define USING_NEON and USING_NEON_F16C. +// This is more for organizational clarity... skcms.cc doesn't force these. +#if N > 1 && defined(__ARM_NEON) + #define USING_NEON + + // We have to use two different mechanisms to enable the f16 conversion intrinsics: + #if defined(__clang__) + // Clang's arm_neon.h guards them with the FP hardware bit: + #if __ARM_FP & 2 + #define USING_NEON_F16C + #endif + #elif defined(__GNUC__) + // GCC's arm_neon.h guards them with the FP16 format macros (IEEE and ALTERNATIVE). + // We don't actually want the alternative format - we're reading/writing IEEE f16 values. + #if defined(__ARM_FP16_FORMAT_IEEE) + #define USING_NEON_F16C + #endif + #endif +#endif + +// These -Wvector-conversion warnings seem to trigger in very bogus situations, +// like vst3q_f32() expecting a 16x char rather than a 4x float vector. :/ +#if defined(USING_NEON) && defined(__clang__) + #pragma clang diagnostic ignored "-Wvector-conversion" +#endif + +// GCC & Clang (but not clang-cl) warn returning U64 on x86 is larger than a register. +// You'd see warnings like, "using AVX even though AVX is not enabled". +// We stifle these warnings; our helpers that return U64 are always inlined. +#if defined(__SSE__) && defined(__GNUC__) + #if !defined(__has_warning) + #pragma GCC diagnostic ignored "-Wpsabi" + #elif __has_warning("-Wpsabi") + #pragma GCC diagnostic ignored "-Wpsabi" + #endif +#endif + +// We tag most helper functions as SI, to enforce good code generation +// but also work around what we think is a bug in GCC: when targeting 32-bit +// x86, GCC tends to pass U16 (4x uint16_t vector) function arguments in the +// MMX mm0 register, which seems to mess with unrelated code that later uses +// x87 FP instructions (MMX's mm0 is an alias for x87's st0 register). +#if defined(__clang__) || defined(__GNUC__) + #define SI static inline __attribute__((always_inline)) +#else + #define SI static inline +#endif + +template +SI T load(const P* ptr) { + T val; + memcpy(&val, ptr, sizeof(val)); + return val; +} +template +SI void store(P* ptr, const T& val) { + memcpy(ptr, &val, sizeof(val)); +} + +// (T)v is a cast when N == 1 and a bit-pun when N>1, +// so we use cast(v) to actually cast or bit_pun(v) to bit-pun. +template +SI D cast(const S& v) { +#if N == 1 + return (D)v; +#elif defined(__clang__) + return __builtin_convertvector(v, D); +#else + D d; + for (int i = 0; i < N; i++) { + d[i] = v[i]; + } + return d; +#endif +} + +template +SI D bit_pun(const S& v) { + static_assert(sizeof(D) == sizeof(v), ""); + return load(&v); +} + +// When we convert from float to fixed point, it's very common to want to round, +// and for some reason compilers generate better code when converting to int32_t. +// To serve both those ends, we use this function to_fixed() instead of direct cast(). +SI U32 to_fixed(F f) { return (U32)cast(f + 0.5f); } + +// Sometimes we do something crazy on one branch of a conditonal, +// like divide by zero or convert a huge float to an integer, +// but then harmlessly select the other side. That trips up N==1 +// sanitizer builds, so we make if_then_else() a macro to avoid +// evaluating the unused side. + +#if N == 1 + #define if_then_else(cond, t, e) ((cond) ? (t) : (e)) +#else + template + SI T if_then_else(C cond, T t, T e) { + return bit_pun( ( cond & bit_pun(t)) | + (~cond & bit_pun(e)) ); + } +#endif + + +SI F F_from_Half(U16 half) { +#if defined(USING_NEON_F16C) + return vcvt_f32_f16((float16x4_t)half); +#elif defined(USING_AVX512F) + return (F)_mm512_cvtph_ps((__m256i)half); +#elif defined(USING_AVX_F16C) + typedef int16_t __attribute__((vector_size(16))) I16; + return __builtin_ia32_vcvtph2ps256((I16)half); +#else + U32 wide = cast(half); + // A half is 1-5-10 sign-exponent-mantissa, with 15 exponent bias. + U32 s = wide & 0x8000, + em = wide ^ s; + + // Constructing the float is easy if the half is not denormalized. + F norm = bit_pun( (s<<16) + (em<<13) + ((127-15)<<23) ); + + // Simply flush all denorm half floats to zero. + return if_then_else(em < 0x0400, F0, norm); +#endif +} + +#if defined(__clang__) + // The -((127-15)<<10) underflows that side of the math when + // we pass a denorm half float. It's harmless... we'll take the 0 side anyway. + __attribute__((no_sanitize("unsigned-integer-overflow"))) +#endif +SI U16 Half_from_F(F f) { +#if defined(USING_NEON_F16C) + return (U16)vcvt_f16_f32(f); +#elif defined(USING_AVX512F) + return (U16)_mm512_cvtps_ph((__m512 )f, _MM_FROUND_CUR_DIRECTION ); +#elif defined(USING_AVX_F16C) + return (U16)__builtin_ia32_vcvtps2ph256(f, 0x04/*_MM_FROUND_CUR_DIRECTION*/); +#else + // A float is 1-8-23 sign-exponent-mantissa, with 127 exponent bias. + U32 sem = bit_pun(f), + s = sem & 0x80000000, + em = sem ^ s; + + // For simplicity we flush denorm half floats (including all denorm floats) to zero. + return cast(if_then_else(em < 0x38800000, (U32)F0 + , (s>>16) + (em>>13) - ((127-15)<<10))); +#endif +} + +// Swap high and low bytes of 16-bit lanes, converting between big-endian and little-endian. +#if defined(USING_NEON) + SI U16 swap_endian_16(U16 v) { + return (U16)vrev16_u8((uint8x8_t) v); + } +#endif + +SI U64 swap_endian_16x4(const U64& rgba) { + return (rgba & 0x00ff00ff00ff00ff) << 8 + | (rgba & 0xff00ff00ff00ff00) >> 8; +} + +#if defined(USING_NEON) + SI F min_(F x, F y) { return (F)vminq_f32((float32x4_t)x, (float32x4_t)y); } + SI F max_(F x, F y) { return (F)vmaxq_f32((float32x4_t)x, (float32x4_t)y); } +#else + SI F min_(F x, F y) { return if_then_else(x > y, y, x); } + SI F max_(F x, F y) { return if_then_else(x < y, y, x); } +#endif + +SI F floor_(F x) { +#if N == 1 + return floorf_(x); +#elif defined(__aarch64__) + return vrndmq_f32(x); +#elif defined(USING_AVX512F) + // Clang's _mm512_floor_ps() passes its mask as -1, not (__mmask16)-1, + // and integer santizer catches that this implicit cast changes the + // value from -1 to 65535. We'll cast manually to work around it. + // Read this as `return _mm512_floor_ps(x)`. + return _mm512_mask_floor_ps(x, (__mmask16)-1, x); +#elif defined(USING_AVX) + return __builtin_ia32_roundps256(x, 0x01/*_MM_FROUND_FLOOR*/); +#elif defined(__SSE4_1__) + return _mm_floor_ps(x); +#else + // Round trip through integers with a truncating cast. + F roundtrip = cast(cast(x)); + // If x is negative, truncating gives the ceiling instead of the floor. + return roundtrip - if_then_else(roundtrip > x, F1, F0); + + // This implementation fails for values of x that are outside + // the range an integer can represent. We expect most x to be small. +#endif +} + +SI F approx_log2(F x) { + // The first approximation of log2(x) is its exponent 'e', minus 127. + I32 bits = bit_pun(x); + + F e = cast(bits) * (1.0f / (1<<23)); + + // If we use the mantissa too we can refine the error signficantly. + F m = bit_pun( (bits & 0x007fffff) | 0x3f000000 ); + + return e - 124.225514990f + - 1.498030302f*m + - 1.725879990f/(0.3520887068f + m); +} + +SI F approx_log(F x) { + const float ln2 = 0.69314718f; + return ln2 * approx_log2(x); +} + +SI F approx_exp2(F x) { + F fract = x - floor_(x); + + F fbits = (1.0f * (1<<23)) * (x + 121.274057500f + - 1.490129070f*fract + + 27.728023300f/(4.84252568f - fract)); + I32 bits = cast(min_(max_(fbits, F0), FInfBits)); + + return bit_pun(bits); +} + +SI F approx_pow(F x, float y) { + return if_then_else((x == F0) | (x == F1), x + , approx_exp2(approx_log2(x) * y)); +} + +SI F approx_exp(F x) { + const float log2_e = 1.4426950408889634074f; + return approx_exp2(log2_e * x); +} + +SI F strip_sign(F x, U32* sign) { + U32 bits = bit_pun(x); + *sign = bits & 0x80000000; + return bit_pun(bits ^ *sign); +} + +SI F apply_sign(F x, U32 sign) { + return bit_pun(sign | bit_pun(x)); +} + +// Return tf(x). +SI F apply_tf(const skcms_TransferFunction* tf, F x) { + // Peel off the sign bit and set x = |x|. + U32 sign; + x = strip_sign(x, &sign); + + // The transfer function has a linear part up to d, exponential at d and after. + F v = if_then_else(x < tf->d, tf->c*x + tf->f + , approx_pow(tf->a*x + tf->b, tf->g) + tf->e); + + // Tack the sign bit back on. + return apply_sign(v, sign); +} + +// Return the gamma function (|x|^G with the original sign re-applied to x). +SI F apply_gamma(const skcms_TransferFunction* tf, F x) { + U32 sign; + x = strip_sign(x, &sign); + return apply_sign(approx_pow(x, tf->g), sign); +} + +SI F apply_pq(const skcms_TransferFunction* tf, F x) { + U32 bits = bit_pun(x), + sign = bits & 0x80000000; + x = bit_pun(bits ^ sign); + + F v = approx_pow(max_(tf->a + tf->b * approx_pow(x, tf->c), F0) + / (tf->d + tf->e * approx_pow(x, tf->c)), + tf->f); + + return bit_pun(sign | bit_pun(v)); +} + +SI F apply_hlg(const skcms_TransferFunction* tf, F x) { + const float R = tf->a, G = tf->b, + a = tf->c, b = tf->d, c = tf->e, + K = tf->f + 1; + U32 bits = bit_pun(x), + sign = bits & 0x80000000; + x = bit_pun(bits ^ sign); + + F v = if_then_else(x*R <= 1, approx_pow(x*R, G) + , approx_exp((x-c)*a) + b); + + return K*bit_pun(sign | bit_pun(v)); +} + +SI F apply_hlginv(const skcms_TransferFunction* tf, F x) { + const float R = tf->a, G = tf->b, + a = tf->c, b = tf->d, c = tf->e, + K = tf->f + 1; + U32 bits = bit_pun(x), + sign = bits & 0x80000000; + x = bit_pun(bits ^ sign); + x /= K; + + F v = if_then_else(x <= 1, R * approx_pow(x, G) + , a * approx_log(x - b) + c); + + return bit_pun(sign | bit_pun(v)); +} + + +// Strided loads and stores of N values, starting from p. +template +SI T load_3(const P* p) { +#if N == 1 + return (T)p[0]; +#elif N == 4 + return T{p[ 0],p[ 3],p[ 6],p[ 9]}; +#elif N == 8 + return T{p[ 0],p[ 3],p[ 6],p[ 9], p[12],p[15],p[18],p[21]}; +#elif N == 16 + return T{p[ 0],p[ 3],p[ 6],p[ 9], p[12],p[15],p[18],p[21], + p[24],p[27],p[30],p[33], p[36],p[39],p[42],p[45]}; +#endif +} + +template +SI T load_4(const P* p) { +#if N == 1 + return (T)p[0]; +#elif N == 4 + return T{p[ 0],p[ 4],p[ 8],p[12]}; +#elif N == 8 + return T{p[ 0],p[ 4],p[ 8],p[12], p[16],p[20],p[24],p[28]}; +#elif N == 16 + return T{p[ 0],p[ 4],p[ 8],p[12], p[16],p[20],p[24],p[28], + p[32],p[36],p[40],p[44], p[48],p[52],p[56],p[60]}; +#endif +} + +template +SI void store_3(P* p, const T& v) { +#if N == 1 + p[0] = v; +#elif N == 4 + p[ 0] = v[ 0]; p[ 3] = v[ 1]; p[ 6] = v[ 2]; p[ 9] = v[ 3]; +#elif N == 8 + p[ 0] = v[ 0]; p[ 3] = v[ 1]; p[ 6] = v[ 2]; p[ 9] = v[ 3]; + p[12] = v[ 4]; p[15] = v[ 5]; p[18] = v[ 6]; p[21] = v[ 7]; +#elif N == 16 + p[ 0] = v[ 0]; p[ 3] = v[ 1]; p[ 6] = v[ 2]; p[ 9] = v[ 3]; + p[12] = v[ 4]; p[15] = v[ 5]; p[18] = v[ 6]; p[21] = v[ 7]; + p[24] = v[ 8]; p[27] = v[ 9]; p[30] = v[10]; p[33] = v[11]; + p[36] = v[12]; p[39] = v[13]; p[42] = v[14]; p[45] = v[15]; +#endif +} + +template +SI void store_4(P* p, const T& v) { +#if N == 1 + p[0] = v; +#elif N == 4 + p[ 0] = v[ 0]; p[ 4] = v[ 1]; p[ 8] = v[ 2]; p[12] = v[ 3]; +#elif N == 8 + p[ 0] = v[ 0]; p[ 4] = v[ 1]; p[ 8] = v[ 2]; p[12] = v[ 3]; + p[16] = v[ 4]; p[20] = v[ 5]; p[24] = v[ 6]; p[28] = v[ 7]; +#elif N == 16 + p[ 0] = v[ 0]; p[ 4] = v[ 1]; p[ 8] = v[ 2]; p[12] = v[ 3]; + p[16] = v[ 4]; p[20] = v[ 5]; p[24] = v[ 6]; p[28] = v[ 7]; + p[32] = v[ 8]; p[36] = v[ 9]; p[40] = v[10]; p[44] = v[11]; + p[48] = v[12]; p[52] = v[13]; p[56] = v[14]; p[60] = v[15]; +#endif +} + + +SI U8 gather_8(const uint8_t* p, I32 ix) { +#if N == 1 + U8 v = p[ix]; +#elif N == 4 + U8 v = { p[ix[0]], p[ix[1]], p[ix[2]], p[ix[3]] }; +#elif N == 8 + U8 v = { p[ix[0]], p[ix[1]], p[ix[2]], p[ix[3]], + p[ix[4]], p[ix[5]], p[ix[6]], p[ix[7]] }; +#elif N == 16 + U8 v = { p[ix[ 0]], p[ix[ 1]], p[ix[ 2]], p[ix[ 3]], + p[ix[ 4]], p[ix[ 5]], p[ix[ 6]], p[ix[ 7]], + p[ix[ 8]], p[ix[ 9]], p[ix[10]], p[ix[11]], + p[ix[12]], p[ix[13]], p[ix[14]], p[ix[15]] }; +#endif + return v; +} + +SI U16 gather_16(const uint8_t* p, I32 ix) { + // Load the i'th 16-bit value from p. + auto load_16 = [p](int i) { + return load(p + 2*i); + }; +#if N == 1 + U16 v = load_16(ix); +#elif N == 4 + U16 v = { load_16(ix[0]), load_16(ix[1]), load_16(ix[2]), load_16(ix[3]) }; +#elif N == 8 + U16 v = { load_16(ix[0]), load_16(ix[1]), load_16(ix[2]), load_16(ix[3]), + load_16(ix[4]), load_16(ix[5]), load_16(ix[6]), load_16(ix[7]) }; +#elif N == 16 + U16 v = { load_16(ix[ 0]), load_16(ix[ 1]), load_16(ix[ 2]), load_16(ix[ 3]), + load_16(ix[ 4]), load_16(ix[ 5]), load_16(ix[ 6]), load_16(ix[ 7]), + load_16(ix[ 8]), load_16(ix[ 9]), load_16(ix[10]), load_16(ix[11]), + load_16(ix[12]), load_16(ix[13]), load_16(ix[14]), load_16(ix[15]) }; +#endif + return v; +} + +SI U32 gather_32(const uint8_t* p, I32 ix) { + // Load the i'th 32-bit value from p. + auto load_32 = [p](int i) { + return load(p + 4*i); + }; +#if N == 1 + U32 v = load_32(ix); +#elif N == 4 + U32 v = { load_32(ix[0]), load_32(ix[1]), load_32(ix[2]), load_32(ix[3]) }; +#elif N == 8 + U32 v = { load_32(ix[0]), load_32(ix[1]), load_32(ix[2]), load_32(ix[3]), + load_32(ix[4]), load_32(ix[5]), load_32(ix[6]), load_32(ix[7]) }; +#elif N == 16 + U32 v = { load_32(ix[ 0]), load_32(ix[ 1]), load_32(ix[ 2]), load_32(ix[ 3]), + load_32(ix[ 4]), load_32(ix[ 5]), load_32(ix[ 6]), load_32(ix[ 7]), + load_32(ix[ 8]), load_32(ix[ 9]), load_32(ix[10]), load_32(ix[11]), + load_32(ix[12]), load_32(ix[13]), load_32(ix[14]), load_32(ix[15]) }; +#endif + // TODO: AVX2 and AVX-512 gathers (c.f. gather_24). + return v; +} + +SI U32 gather_24(const uint8_t* p, I32 ix) { + // First, back up a byte. Any place we're gathering from has a safe junk byte to read + // in front of it, either a previous table value, or some tag metadata. + p -= 1; + + // Load the i'th 24-bit value from p, and 1 extra byte. + auto load_24_32 = [p](int i) { + return load(p + 3*i); + }; + + // Now load multiples of 4 bytes (a junk byte, then r,g,b). +#if N == 1 + U32 v = load_24_32(ix); +#elif N == 4 + U32 v = { load_24_32(ix[0]), load_24_32(ix[1]), load_24_32(ix[2]), load_24_32(ix[3]) }; +#elif N == 8 && !defined(USING_AVX2) + U32 v = { load_24_32(ix[0]), load_24_32(ix[1]), load_24_32(ix[2]), load_24_32(ix[3]), + load_24_32(ix[4]), load_24_32(ix[5]), load_24_32(ix[6]), load_24_32(ix[7]) }; +#elif N == 8 + (void)load_24_32; + // The gather instruction here doesn't need any particular alignment, + // but the intrinsic takes a const int*. + const int* p4 = bit_pun(p); + I32 zero = { 0, 0, 0, 0, 0, 0, 0, 0}, + mask = {-1,-1,-1,-1, -1,-1,-1,-1}; + #if defined(__clang__) + U32 v = (U32)__builtin_ia32_gatherd_d256(zero, p4, 3*ix, mask, 1); + #elif defined(__GNUC__) + U32 v = (U32)__builtin_ia32_gathersiv8si(zero, p4, 3*ix, mask, 1); + #endif +#elif N == 16 + (void)load_24_32; + // The intrinsic is supposed to take const void* now, but it takes const int*, just like AVX2. + // And AVX-512 swapped the order of arguments. :/ + const int* p4 = bit_pun(p); + U32 v = (U32)_mm512_i32gather_epi32((__m512i)(3*ix), p4, 1); +#endif + + // Shift off the junk byte, leaving r,g,b in low 24 bits (and zero in the top 8). + return v >> 8; +} + +#if !defined(__arm__) + SI void gather_48(const uint8_t* p, I32 ix, U64* v) { + // As in gather_24(), with everything doubled. + p -= 2; + + // Load the i'th 48-bit value from p, and 2 extra bytes. + auto load_48_64 = [p](int i) { + return load(p + 6*i); + }; + + #if N == 1 + *v = load_48_64(ix); + #elif N == 4 + *v = U64{ + load_48_64(ix[0]), load_48_64(ix[1]), load_48_64(ix[2]), load_48_64(ix[3]), + }; + #elif N == 8 && !defined(USING_AVX2) + *v = U64{ + load_48_64(ix[0]), load_48_64(ix[1]), load_48_64(ix[2]), load_48_64(ix[3]), + load_48_64(ix[4]), load_48_64(ix[5]), load_48_64(ix[6]), load_48_64(ix[7]), + }; + #elif N == 8 + (void)load_48_64; + typedef int32_t __attribute__((vector_size(16))) Half_I32; + typedef long long __attribute__((vector_size(32))) Half_I64; + + // The gather instruction here doesn't need any particular alignment, + // but the intrinsic takes a const long long*. + const long long int* p8 = bit_pun(p); + + Half_I64 zero = { 0, 0, 0, 0}, + mask = {-1,-1,-1,-1}; + + ix *= 6; + Half_I32 ix_lo = { ix[0], ix[1], ix[2], ix[3] }, + ix_hi = { ix[4], ix[5], ix[6], ix[7] }; + + #if defined(__clang__) + Half_I64 lo = (Half_I64)__builtin_ia32_gatherd_q256(zero, p8, ix_lo, mask, 1), + hi = (Half_I64)__builtin_ia32_gatherd_q256(zero, p8, ix_hi, mask, 1); + #elif defined(__GNUC__) + Half_I64 lo = (Half_I64)__builtin_ia32_gathersiv4di(zero, p8, ix_lo, mask, 1), + hi = (Half_I64)__builtin_ia32_gathersiv4di(zero, p8, ix_hi, mask, 1); + #endif + store((char*)v + 0, lo); + store((char*)v + 32, hi); + #elif N == 16 + (void)load_48_64; + const long long int* p8 = bit_pun(p); + __m512i lo = _mm512_i32gather_epi64(_mm512_extracti32x8_epi32((__m512i)(6*ix), 0), p8, 1), + hi = _mm512_i32gather_epi64(_mm512_extracti32x8_epi32((__m512i)(6*ix), 1), p8, 1); + store((char*)v + 0, lo); + store((char*)v + 64, hi); + #endif + + *v >>= 16; + } +#endif + +SI F F_from_U8(U8 v) { + return cast(v) * (1/255.0f); +} + +SI F F_from_U16_BE(U16 v) { + // All 16-bit ICC values are big-endian, so we byte swap before converting to float. + // MSVC catches the "loss" of data here in the portable path, so we also make sure to mask. + U16 lo = (v >> 8), + hi = (v << 8) & 0xffff; + return cast(lo|hi) * (1/65535.0f); +} + +SI U16 U16_from_F(F v) { + // 65535 == inf in FP16, so promote to FP32 before converting. + return cast(cast>(v) * 65535 + 0.5f); +} + +SI F minus_1_ulp(F v) { + return bit_pun( bit_pun(v) - 1 ); +} + +SI F table(const skcms_Curve* curve, F v) { + // Clamp the input to [0,1], then scale to a table index. + F ix = max_(F0, min_(v, F1)) * (float)(curve->table_entries - 1); + + // We'll look up (equal or adjacent) entries at lo and hi, then lerp by t between the two. + I32 lo = cast( ix ), + hi = cast(minus_1_ulp(ix+1.0f)); + F t = ix - cast(lo); // i.e. the fractional part of ix. + + // TODO: can we load l and h simultaneously? Each entry in 'h' is either + // the same as in 'l' or adjacent. We have a rough idea that's it'd always be safe + // to read adjacent entries and perhaps underflow the table by a byte or two + // (it'd be junk, but always safe to read). Not sure how to lerp yet. + F l,h; + if (curve->table_8) { + l = F_from_U8(gather_8(curve->table_8, lo)); + h = F_from_U8(gather_8(curve->table_8, hi)); + } else { + l = F_from_U16_BE(gather_16(curve->table_16, lo)); + h = F_from_U16_BE(gather_16(curve->table_16, hi)); + } + return l + (h-l)*t; +} + +SI void sample_clut_8(const uint8_t* grid_8, I32 ix, F* r, F* g, F* b) { + U32 rgb = gather_24(grid_8, ix); + + *r = cast((rgb >> 0) & 0xff) * (1/255.0f); + *g = cast((rgb >> 8) & 0xff) * (1/255.0f); + *b = cast((rgb >> 16) & 0xff) * (1/255.0f); +} + +SI void sample_clut_8(const uint8_t* grid_8, I32 ix, F* r, F* g, F* b, F* a) { + // TODO: don't forget to optimize gather_32(). + U32 rgba = gather_32(grid_8, ix); + + *r = cast((rgba >> 0) & 0xff) * (1/255.0f); + *g = cast((rgba >> 8) & 0xff) * (1/255.0f); + *b = cast((rgba >> 16) & 0xff) * (1/255.0f); + *a = cast((rgba >> 24) & 0xff) * (1/255.0f); +} + +SI void sample_clut_16(const uint8_t* grid_16, I32 ix, F* r, F* g, F* b) { +#if defined(__arm__) + // This is up to 2x faster on 32-bit ARM than the #else-case fast path. + *r = F_from_U16_BE(gather_16(grid_16, 3*ix+0)); + *g = F_from_U16_BE(gather_16(grid_16, 3*ix+1)); + *b = F_from_U16_BE(gather_16(grid_16, 3*ix+2)); +#else + // This strategy is much faster for 64-bit builds, and fine for 32-bit x86 too. + U64 rgb; + gather_48(grid_16, ix, &rgb); + rgb = swap_endian_16x4(rgb); + + *r = cast((rgb >> 0) & 0xffff) * (1/65535.0f); + *g = cast((rgb >> 16) & 0xffff) * (1/65535.0f); + *b = cast((rgb >> 32) & 0xffff) * (1/65535.0f); +#endif +} + +SI void sample_clut_16(const uint8_t* grid_16, I32 ix, F* r, F* g, F* b, F* a) { + // TODO: gather_64()-based fast path? + *r = F_from_U16_BE(gather_16(grid_16, 4*ix+0)); + *g = F_from_U16_BE(gather_16(grid_16, 4*ix+1)); + *b = F_from_U16_BE(gather_16(grid_16, 4*ix+2)); + *a = F_from_U16_BE(gather_16(grid_16, 4*ix+3)); +} + +static void clut(uint32_t input_channels, uint32_t output_channels, + const uint8_t grid_points[4], const uint8_t* grid_8, const uint8_t* grid_16, + F* r, F* g, F* b, F* a) { + + const int dim = (int)input_channels; + assert (0 < dim && dim <= 4); + assert (output_channels == 3 || + output_channels == 4); + + // For each of these arrays, think foo[2*dim], but we use foo[8] since we know dim <= 4. + I32 index [8]; // Index contribution by dimension, first low from 0, then high from 4. + F weight[8]; // Weight for each contribution, again first low, then high. + + // O(dim) work first: calculate index,weight from r,g,b,a. + const F inputs[] = { *r,*g,*b,*a }; + for (int i = dim-1, stride = 1; i >= 0; i--) { + // x is where we logically want to sample the grid in the i-th dimension. + F x = inputs[i] * (float)(grid_points[i] - 1); + + // But we can't index at floats. lo and hi are the two integer grid points surrounding x. + I32 lo = cast( x ), // i.e. trunc(x) == floor(x) here. + hi = cast(minus_1_ulp(x+1.0f)); + // Notice how we fold in the accumulated stride across previous dimensions here. + index[i+0] = lo * stride; + index[i+4] = hi * stride; + stride *= grid_points[i]; + + // We'll interpolate between those two integer grid points by t. + F t = x - cast(lo); // i.e. fract(x) + weight[i+0] = 1-t; + weight[i+4] = t; + } + + *r = *g = *b = F0; + if (output_channels == 4) { + *a = F0; + } + + // We'll sample 2^dim == 1<input_channels, a2b->output_channels, + a2b->grid_points, a2b->grid_8, a2b->grid_16, + r,g,b,&a); +} +static void clut(const skcms_B2A* b2a, F* r, F* g, F* b, F* a) { + clut(b2a->input_channels, b2a->output_channels, + b2a->grid_points, b2a->grid_8, b2a->grid_16, + r,g,b,a); +} + +struct NoCtx {}; + +struct Ctx { + const void* fArg; + operator NoCtx() { return NoCtx{}; } + template operator T*() { return (const T*)fArg; } +}; + +#define STAGE_PARAMS(MAYBE_REF) SKCMS_MAYBE_UNUSED const char* src, \ + SKCMS_MAYBE_UNUSED char* dst, \ + SKCMS_MAYBE_UNUSED F MAYBE_REF r, \ + SKCMS_MAYBE_UNUSED F MAYBE_REF g, \ + SKCMS_MAYBE_UNUSED F MAYBE_REF b, \ + SKCMS_MAYBE_UNUSED F MAYBE_REF a, \ + SKCMS_MAYBE_UNUSED int i + +#if SKCMS_HAS_MUSTTAIL + + // Stages take a stage list, and each stage is responsible for tail-calling the next one. + // + // Unfortunately, we can't declare a StageFn as a function pointer which takes a pointer to + // another StageFn; declaring this leads to a circular dependency. To avoid this, StageFn is + // wrapped in a single-element `struct StageList` which we are able to forward-declare. + struct StageList; + using StageFn = void (*)(StageList stages, const void** ctx, STAGE_PARAMS()); + struct StageList { + const StageFn* fn; + }; + + #define DECLARE_STAGE(name, arg, CALL_NEXT) \ + SI void Exec_##name##_k(arg, STAGE_PARAMS(&)); \ + \ + SI void Exec_##name(StageList list, const void** ctx, STAGE_PARAMS()) { \ + Exec_##name##_k(Ctx{*ctx}, src, dst, r, g, b, a, i); \ + ++list.fn; ++ctx; \ + CALL_NEXT; \ + } \ + \ + SI void Exec_##name##_k(arg, STAGE_PARAMS(&)) + + #define STAGE(name, arg) \ + DECLARE_STAGE(name, arg, [[clang::musttail]] return (*list.fn)(list, ctx, src, dst, \ + r, g, b, a, i)) + + #define FINAL_STAGE(name, arg) \ + DECLARE_STAGE(name, arg, /* Stop executing stages and return to the caller. */) + +#else + + #define DECLARE_STAGE(name, arg) \ + SI void Exec_##name##_k(arg, STAGE_PARAMS(&)); \ + \ + SI void Exec_##name(const void* ctx, STAGE_PARAMS(&)) { \ + Exec_##name##_k(Ctx{ctx}, src, dst, r, g, b, a, i); \ + } \ + \ + SI void Exec_##name##_k(arg, STAGE_PARAMS(&)) + + #define STAGE(name, arg) DECLARE_STAGE(name, arg) + #define FINAL_STAGE(name, arg) DECLARE_STAGE(name, arg) + +#endif + +STAGE(load_a8, NoCtx) { + a = F_from_U8(load(src + 1*i)); +} + +STAGE(load_g8, NoCtx) { + r = g = b = F_from_U8(load(src + 1*i)); +} + +STAGE(load_4444, NoCtx) { + U16 abgr = load(src + 2*i); + + r = cast((abgr >> 12) & 0xf) * (1/15.0f); + g = cast((abgr >> 8) & 0xf) * (1/15.0f); + b = cast((abgr >> 4) & 0xf) * (1/15.0f); + a = cast((abgr >> 0) & 0xf) * (1/15.0f); +} + +STAGE(load_565, NoCtx) { + U16 rgb = load(src + 2*i); + + r = cast(rgb & (uint16_t)(31<< 0)) * (1.0f / (31<< 0)); + g = cast(rgb & (uint16_t)(63<< 5)) * (1.0f / (63<< 5)); + b = cast(rgb & (uint16_t)(31<<11)) * (1.0f / (31<<11)); +} + +STAGE(load_888, NoCtx) { + const uint8_t* rgb = (const uint8_t*)(src + 3*i); +#if defined(USING_NEON) + // There's no uint8x4x3_t or vld3 load for it, so we'll load each rgb pixel one at + // a time. Since we're doing that, we might as well load them into 16-bit lanes. + // (We'd even load into 32-bit lanes, but that's not possible on ARMv7.) + uint8x8x3_t v = {{ vdup_n_u8(0), vdup_n_u8(0), vdup_n_u8(0) }}; + v = vld3_lane_u8(rgb+0, v, 0); + v = vld3_lane_u8(rgb+3, v, 2); + v = vld3_lane_u8(rgb+6, v, 4); + v = vld3_lane_u8(rgb+9, v, 6); + + // Now if we squint, those 3 uint8x8_t we constructed are really U16s, easy to + // convert to F. (Again, U32 would be even better here if drop ARMv7 or split + // ARMv7 and ARMv8 impls.) + r = cast((U16)v.val[0]) * (1/255.0f); + g = cast((U16)v.val[1]) * (1/255.0f); + b = cast((U16)v.val[2]) * (1/255.0f); +#else + r = cast(load_3(rgb+0) ) * (1/255.0f); + g = cast(load_3(rgb+1) ) * (1/255.0f); + b = cast(load_3(rgb+2) ) * (1/255.0f); +#endif +} + +STAGE(load_8888, NoCtx) { + U32 rgba = load(src + 4*i); + + r = cast((rgba >> 0) & 0xff) * (1/255.0f); + g = cast((rgba >> 8) & 0xff) * (1/255.0f); + b = cast((rgba >> 16) & 0xff) * (1/255.0f); + a = cast((rgba >> 24) & 0xff) * (1/255.0f); +} + +STAGE(load_1010102, NoCtx) { + U32 rgba = load(src + 4*i); + + r = cast((rgba >> 0) & 0x3ff) * (1/1023.0f); + g = cast((rgba >> 10) & 0x3ff) * (1/1023.0f); + b = cast((rgba >> 20) & 0x3ff) * (1/1023.0f); + a = cast((rgba >> 30) & 0x3 ) * (1/ 3.0f); +} + +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(src + 4*i); + r = cast((rgba >> 0) & 0x3ff) * (1/1023.0f) * range + min; + g = cast((rgba >> 10) & 0x3ff) * (1/1023.0f) * range + min; + b = cast((rgba >> 20) & 0x3ff) * (1/1023.0f) * range + min; +} + +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(src + 8*i); + r = cast((rgba >> 0) & 0x3ff) * (1/1023.0f) * range + min; + g = cast((rgba >> 16) & 0x3ff) * (1/1023.0f) * range + min; + b = cast((rgba >> 32) & 0x3ff) * (1/1023.0f) * range + min; + a = cast((rgba >> 48) & 0x3ff) * (1/1023.0f) * range + min; +} + +STAGE(load_161616LE, NoCtx) { + uintptr_t ptr = (uintptr_t)(src + 6*i); + assert( (ptr & 1) == 0 ); // src must be 2-byte aligned for this + const uint16_t* rgb = (const uint16_t*)ptr; // cast to const uint16_t* to be safe. +#if defined(USING_NEON) + uint16x4x3_t v = vld3_u16(rgb); + r = cast((U16)v.val[0]) * (1/65535.0f); + g = cast((U16)v.val[1]) * (1/65535.0f); + b = cast((U16)v.val[2]) * (1/65535.0f); +#else + r = cast(load_3(rgb+0)) * (1/65535.0f); + g = cast(load_3(rgb+1)) * (1/65535.0f); + b = cast(load_3(rgb+2)) * (1/65535.0f); +#endif +} + +STAGE(load_16161616LE, NoCtx) { + uintptr_t ptr = (uintptr_t)(src + 8*i); + assert( (ptr & 1) == 0 ); // src must be 2-byte aligned for this + const uint16_t* rgba = (const uint16_t*)ptr; // cast to const uint16_t* to be safe. +#if defined(USING_NEON) + uint16x4x4_t v = vld4_u16(rgba); + r = cast((U16)v.val[0]) * (1/65535.0f); + g = cast((U16)v.val[1]) * (1/65535.0f); + b = cast((U16)v.val[2]) * (1/65535.0f); + a = cast((U16)v.val[3]) * (1/65535.0f); +#else + U64 px = load(rgba); + + r = cast((px >> 0) & 0xffff) * (1/65535.0f); + g = cast((px >> 16) & 0xffff) * (1/65535.0f); + b = cast((px >> 32) & 0xffff) * (1/65535.0f); + a = cast((px >> 48) & 0xffff) * (1/65535.0f); +#endif +} + +STAGE(load_161616BE, NoCtx) { + uintptr_t ptr = (uintptr_t)(src + 6*i); + assert( (ptr & 1) == 0 ); // src must be 2-byte aligned for this + const uint16_t* rgb = (const uint16_t*)ptr; // cast to const uint16_t* to be safe. +#if defined(USING_NEON) + uint16x4x3_t v = vld3_u16(rgb); + r = cast(swap_endian_16((U16)v.val[0])) * (1/65535.0f); + g = cast(swap_endian_16((U16)v.val[1])) * (1/65535.0f); + b = cast(swap_endian_16((U16)v.val[2])) * (1/65535.0f); +#else + U32 R = load_3(rgb+0), + G = load_3(rgb+1), + B = load_3(rgb+2); + // R,G,B are big-endian 16-bit, so byte swap them before converting to float. + r = cast((R & 0x00ff)<<8 | (R & 0xff00)>>8) * (1/65535.0f); + g = cast((G & 0x00ff)<<8 | (G & 0xff00)>>8) * (1/65535.0f); + b = cast((B & 0x00ff)<<8 | (B & 0xff00)>>8) * (1/65535.0f); +#endif +} + +STAGE(load_16161616BE, NoCtx) { + uintptr_t ptr = (uintptr_t)(src + 8*i); + assert( (ptr & 1) == 0 ); // src must be 2-byte aligned for this + const uint16_t* rgba = (const uint16_t*)ptr; // cast to const uint16_t* to be safe. +#if defined(USING_NEON) + uint16x4x4_t v = vld4_u16(rgba); + r = cast(swap_endian_16((U16)v.val[0])) * (1/65535.0f); + g = cast(swap_endian_16((U16)v.val[1])) * (1/65535.0f); + b = cast(swap_endian_16((U16)v.val[2])) * (1/65535.0f); + a = cast(swap_endian_16((U16)v.val[3])) * (1/65535.0f); +#else + U64 px = swap_endian_16x4(load(rgba)); + + r = cast((px >> 0) & 0xffff) * (1/65535.0f); + g = cast((px >> 16) & 0xffff) * (1/65535.0f); + b = cast((px >> 32) & 0xffff) * (1/65535.0f); + a = cast((px >> 48) & 0xffff) * (1/65535.0f); +#endif +} + +STAGE(load_hhh, NoCtx) { + uintptr_t ptr = (uintptr_t)(src + 6*i); + assert( (ptr & 1) == 0 ); // src must be 2-byte aligned for this + const uint16_t* rgb = (const uint16_t*)ptr; // cast to const uint16_t* to be safe. +#if defined(USING_NEON) + uint16x4x3_t v = vld3_u16(rgb); + U16 R = (U16)v.val[0], + G = (U16)v.val[1], + B = (U16)v.val[2]; +#else + U16 R = load_3(rgb+0), + G = load_3(rgb+1), + B = load_3(rgb+2); +#endif + r = F_from_Half(R); + g = F_from_Half(G); + b = F_from_Half(B); +} + +STAGE(load_hhhh, NoCtx) { + uintptr_t ptr = (uintptr_t)(src + 8*i); + assert( (ptr & 1) == 0 ); // src must be 2-byte aligned for this + const uint16_t* rgba = (const uint16_t*)ptr; // cast to const uint16_t* to be safe. +#if defined(USING_NEON) + uint16x4x4_t v = vld4_u16(rgba); + U16 R = (U16)v.val[0], + G = (U16)v.val[1], + B = (U16)v.val[2], + A = (U16)v.val[3]; +#else + U64 px = load(rgba); + U16 R = cast((px >> 0) & 0xffff), + G = cast((px >> 16) & 0xffff), + B = cast((px >> 32) & 0xffff), + A = cast((px >> 48) & 0xffff); +#endif + r = F_from_Half(R); + g = F_from_Half(G); + b = F_from_Half(B); + a = F_from_Half(A); +} + +STAGE(load_fff, NoCtx) { + uintptr_t ptr = (uintptr_t)(src + 12*i); + assert( (ptr & 3) == 0 ); // src must be 4-byte aligned for this + const float* rgb = (const float*)ptr; // cast to const float* to be safe. +#if defined(USING_NEON) + float32x4x3_t v = vld3q_f32(rgb); + r = (F)v.val[0]; + g = (F)v.val[1]; + b = (F)v.val[2]; +#else + r = load_3(rgb+0); + g = load_3(rgb+1); + b = load_3(rgb+2); +#endif +} + +STAGE(load_ffff, NoCtx) { + uintptr_t ptr = (uintptr_t)(src + 16*i); + assert( (ptr & 3) == 0 ); // src must be 4-byte aligned for this + const float* rgba = (const float*)ptr; // cast to const float* to be safe. +#if defined(USING_NEON) + float32x4x4_t v = vld4q_f32(rgba); + r = (F)v.val[0]; + g = (F)v.val[1]; + b = (F)v.val[2]; + a = (F)v.val[3]; +#else + r = load_4(rgba+0); + g = load_4(rgba+1); + b = load_4(rgba+2); + a = load_4(rgba+3); +#endif +} + +STAGE(swap_rb, NoCtx) { + F t = r; + r = b; + b = t; +} + +STAGE(clamp, NoCtx) { + r = max_(F0, min_(r, F1)); + g = max_(F0, min_(g, F1)); + b = max_(F0, min_(b, F1)); + a = max_(F0, min_(a, F1)); +} + +STAGE(invert, NoCtx) { + r = F1 - r; + g = F1 - g; + b = F1 - b; + a = F1 - a; +} + +STAGE(force_opaque, NoCtx) { + a = F1; +} + +STAGE(premul, NoCtx) { + r *= a; + g *= a; + b *= a; +} + +STAGE(unpremul, NoCtx) { + F scale = if_then_else(F1 / a < INFINITY_, F1 / a, F0); + r *= scale; + g *= scale; + b *= scale; +} + +STAGE(matrix_3x3, const skcms_Matrix3x3* matrix) { + const float* m = &matrix->vals[0][0]; + + F R = m[0]*r + m[1]*g + m[2]*b, + G = m[3]*r + m[4]*g + m[5]*b, + B = m[6]*r + m[7]*g + m[8]*b; + + r = R; + g = G; + b = B; +} + +STAGE(matrix_3x4, const skcms_Matrix3x4* matrix) { + const float* m = &matrix->vals[0][0]; + + F R = m[0]*r + m[1]*g + m[ 2]*b + m[ 3], + G = m[4]*r + m[5]*g + m[ 6]*b + m[ 7], + B = m[8]*r + m[9]*g + m[10]*b + m[11]; + + r = R; + g = G; + b = B; +} + +STAGE(lab_to_xyz, NoCtx) { + // The L*a*b values are in r,g,b, but normalized to [0,1]. Reconstruct them: + F L = r * 100.0f, + A = g * 255.0f - 128.0f, + B = b * 255.0f - 128.0f; + + // Convert to CIE XYZ. + F Y = (L + 16.0f) * (1/116.0f), + X = Y + A*(1/500.0f), + Z = Y - B*(1/200.0f); + + X = if_then_else(X*X*X > 0.008856f, X*X*X, (X - (16/116.0f)) * (1/7.787f)); + Y = if_then_else(Y*Y*Y > 0.008856f, Y*Y*Y, (Y - (16/116.0f)) * (1/7.787f)); + Z = if_then_else(Z*Z*Z > 0.008856f, Z*Z*Z, (Z - (16/116.0f)) * (1/7.787f)); + + // Adjust to XYZD50 illuminant, and stuff back into r,g,b for the next op. + r = X * 0.9642f; + g = Y ; + b = Z * 0.8249f; +} + +// As above, in reverse. +STAGE(xyz_to_lab, NoCtx) { + F X = r * (1/0.9642f), + Y = g, + Z = b * (1/0.8249f); + + X = if_then_else(X > 0.008856f, approx_pow(X, 1/3.0f), X*7.787f + (16/116.0f)); + Y = if_then_else(Y > 0.008856f, approx_pow(Y, 1/3.0f), Y*7.787f + (16/116.0f)); + Z = if_then_else(Z > 0.008856f, approx_pow(Z, 1/3.0f), Z*7.787f + (16/116.0f)); + + F L = Y*116.0f - 16.0f, + A = (X-Y)*500.0f, + B = (Y-Z)*200.0f; + + r = L * (1/100.f); + g = (A + 128.0f) * (1/255.0f); + b = (B + 128.0f) * (1/255.0f); +} + +STAGE(gamma_r, const skcms_TransferFunction* tf) { r = apply_gamma(tf, r); } +STAGE(gamma_g, const skcms_TransferFunction* tf) { g = apply_gamma(tf, g); } +STAGE(gamma_b, const skcms_TransferFunction* tf) { b = apply_gamma(tf, b); } +STAGE(gamma_a, const skcms_TransferFunction* tf) { a = apply_gamma(tf, a); } + +STAGE(gamma_rgb, const skcms_TransferFunction* tf) { + r = apply_gamma(tf, r); + g = apply_gamma(tf, g); + b = apply_gamma(tf, b); +} + +STAGE(tf_r, const skcms_TransferFunction* tf) { r = apply_tf(tf, r); } +STAGE(tf_g, const skcms_TransferFunction* tf) { g = apply_tf(tf, g); } +STAGE(tf_b, const skcms_TransferFunction* tf) { b = apply_tf(tf, b); } +STAGE(tf_a, const skcms_TransferFunction* tf) { a = apply_tf(tf, a); } + +STAGE(tf_rgb, const skcms_TransferFunction* tf) { + r = apply_tf(tf, r); + g = apply_tf(tf, g); + b = apply_tf(tf, b); +} + +STAGE(pq_r, const skcms_TransferFunction* tf) { r = apply_pq(tf, r); } +STAGE(pq_g, const skcms_TransferFunction* tf) { g = apply_pq(tf, g); } +STAGE(pq_b, const skcms_TransferFunction* tf) { b = apply_pq(tf, b); } +STAGE(pq_a, const skcms_TransferFunction* tf) { a = apply_pq(tf, a); } + +STAGE(pq_rgb, const skcms_TransferFunction* tf) { + r = apply_pq(tf, r); + g = apply_pq(tf, g); + b = apply_pq(tf, b); +} + +STAGE(hlg_r, const skcms_TransferFunction* tf) { r = apply_hlg(tf, r); } +STAGE(hlg_g, const skcms_TransferFunction* tf) { g = apply_hlg(tf, g); } +STAGE(hlg_b, const skcms_TransferFunction* tf) { b = apply_hlg(tf, b); } +STAGE(hlg_a, const skcms_TransferFunction* tf) { a = apply_hlg(tf, a); } + +STAGE(hlg_rgb, const skcms_TransferFunction* tf) { + r = apply_hlg(tf, r); + g = apply_hlg(tf, g); + b = apply_hlg(tf, b); +} + +STAGE(hlginv_r, const skcms_TransferFunction* tf) { r = apply_hlginv(tf, r); } +STAGE(hlginv_g, const skcms_TransferFunction* tf) { g = apply_hlginv(tf, g); } +STAGE(hlginv_b, const skcms_TransferFunction* tf) { b = apply_hlginv(tf, b); } +STAGE(hlginv_a, const skcms_TransferFunction* tf) { a = apply_hlginv(tf, a); } + +STAGE(hlginv_rgb, const skcms_TransferFunction* tf) { + r = apply_hlginv(tf, r); + g = apply_hlginv(tf, g); + b = apply_hlginv(tf, b); +} + +STAGE(table_r, const skcms_Curve* curve) { r = table(curve, r); } +STAGE(table_g, const skcms_Curve* curve) { g = table(curve, g); } +STAGE(table_b, const skcms_Curve* curve) { b = table(curve, b); } +STAGE(table_a, const skcms_Curve* curve) { a = table(curve, a); } + +STAGE(clut_A2B, const skcms_A2B* a2b) { + clut(a2b, &r,&g,&b,a); + + if (a2b->input_channels == 4) { + // CMYK is opaque. + a = F1; + } +} + +STAGE(clut_B2A, const skcms_B2A* b2a) { + clut(b2a, &r,&g,&b,&a); +} + +// From here on down, the store_ ops are all "final stages," terminating processing of this group. + +FINAL_STAGE(store_a8, NoCtx) { + store(dst + 1*i, cast(to_fixed(a * 255))); +} + +FINAL_STAGE(store_g8, NoCtx) { + // g should be holding luminance (Y) (r,g,b ~~~> X,Y,Z) + store(dst + 1*i, cast(to_fixed(g * 255))); +} + +FINAL_STAGE(store_4444, NoCtx) { + store(dst + 2*i, cast(to_fixed(r * 15) << 12) + | cast(to_fixed(g * 15) << 8) + | cast(to_fixed(b * 15) << 4) + | cast(to_fixed(a * 15) << 0)); +} + +FINAL_STAGE(store_565, NoCtx) { + store(dst + 2*i, cast(to_fixed(r * 31) << 0 ) + | cast(to_fixed(g * 63) << 5 ) + | cast(to_fixed(b * 31) << 11 )); +} + +FINAL_STAGE(store_888, NoCtx) { + uint8_t* rgb = (uint8_t*)dst + 3*i; +#if defined(USING_NEON) + // Same deal as load_888 but in reverse... we'll store using uint8x8x3_t, but + // get there via U16 to save some instructions converting to float. And just + // like load_888, we'd prefer to go via U32 but for ARMv7 support. + U16 R = cast(to_fixed(r * 255)), + G = cast(to_fixed(g * 255)), + B = cast(to_fixed(b * 255)); + + uint8x8x3_t v = {{ (uint8x8_t)R, (uint8x8_t)G, (uint8x8_t)B }}; + vst3_lane_u8(rgb+0, v, 0); + vst3_lane_u8(rgb+3, v, 2); + vst3_lane_u8(rgb+6, v, 4); + vst3_lane_u8(rgb+9, v, 6); +#else + store_3(rgb+0, cast(to_fixed(r * 255)) ); + store_3(rgb+1, cast(to_fixed(g * 255)) ); + store_3(rgb+2, cast(to_fixed(b * 255)) ); +#endif +} + +FINAL_STAGE(store_8888, NoCtx) { + store(dst + 4*i, cast(to_fixed(r * 255)) << 0 + | cast(to_fixed(g * 255)) << 8 + | cast(to_fixed(b * 255)) << 16 + | cast(to_fixed(a * 255)) << 24); +} + +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(to_fixed(((r - min) / range) * 1023)) << 0 + | cast(to_fixed(((g - min) / range) * 1023)) << 10 + | cast(to_fixed(((b - min) / range) * 1023)) << 20); +} + +FINAL_STAGE(store_1010102, NoCtx) { + store(dst + 4*i, cast(to_fixed(r * 1023)) << 0 + | cast(to_fixed(g * 1023)) << 10 + | cast(to_fixed(b * 1023)) << 20 + | cast(to_fixed(a * 3)) << 30); +} + +FINAL_STAGE(store_161616LE, NoCtx) { + uintptr_t ptr = (uintptr_t)(dst + 6*i); + assert( (ptr & 1) == 0 ); // The dst pointer must be 2-byte aligned + uint16_t* rgb = (uint16_t*)ptr; // for this cast to uint16_t* to be safe. +#if defined(USING_NEON) + uint16x4x3_t v = {{ + (uint16x4_t)U16_from_F(r), + (uint16x4_t)U16_from_F(g), + (uint16x4_t)U16_from_F(b), + }}; + vst3_u16(rgb, v); +#else + store_3(rgb+0, U16_from_F(r)); + store_3(rgb+1, U16_from_F(g)); + store_3(rgb+2, U16_from_F(b)); +#endif + +} + +FINAL_STAGE(store_16161616LE, NoCtx) { + uintptr_t ptr = (uintptr_t)(dst + 8*i); + assert( (ptr & 1) == 0 ); // The dst pointer must be 2-byte aligned + uint16_t* rgba = (uint16_t*)ptr; // for this cast to uint16_t* to be safe. +#if defined(USING_NEON) + uint16x4x4_t v = {{ + (uint16x4_t)U16_from_F(r), + (uint16x4_t)U16_from_F(g), + (uint16x4_t)U16_from_F(b), + (uint16x4_t)U16_from_F(a), + }}; + vst4_u16(rgba, v); +#else + U64 px = cast(to_fixed(r * 65535)) << 0 + | cast(to_fixed(g * 65535)) << 16 + | cast(to_fixed(b * 65535)) << 32 + | cast(to_fixed(a * 65535)) << 48; + store(rgba, px); +#endif +} + +FINAL_STAGE(store_161616BE, NoCtx) { + uintptr_t ptr = (uintptr_t)(dst + 6*i); + assert( (ptr & 1) == 0 ); // The dst pointer must be 2-byte aligned + uint16_t* rgb = (uint16_t*)ptr; // for this cast to uint16_t* to be safe. +#if defined(USING_NEON) + uint16x4x3_t v = {{ + (uint16x4_t)swap_endian_16(cast(U16_from_F(r))), + (uint16x4_t)swap_endian_16(cast(U16_from_F(g))), + (uint16x4_t)swap_endian_16(cast(U16_from_F(b))), + }}; + vst3_u16(rgb, v); +#else + U32 R = to_fixed(r * 65535), + G = to_fixed(g * 65535), + B = to_fixed(b * 65535); + store_3(rgb+0, cast((R & 0x00ff) << 8 | (R & 0xff00) >> 8) ); + store_3(rgb+1, cast((G & 0x00ff) << 8 | (G & 0xff00) >> 8) ); + store_3(rgb+2, cast((B & 0x00ff) << 8 | (B & 0xff00) >> 8) ); +#endif + +} + +FINAL_STAGE(store_16161616BE, NoCtx) { + uintptr_t ptr = (uintptr_t)(dst + 8*i); + assert( (ptr & 1) == 0 ); // The dst pointer must be 2-byte aligned + uint16_t* rgba = (uint16_t*)ptr; // for this cast to uint16_t* to be safe. +#if defined(USING_NEON) + uint16x4x4_t v = {{ + (uint16x4_t)swap_endian_16(cast(U16_from_F(r))), + (uint16x4_t)swap_endian_16(cast(U16_from_F(g))), + (uint16x4_t)swap_endian_16(cast(U16_from_F(b))), + (uint16x4_t)swap_endian_16(cast(U16_from_F(a))), + }}; + vst4_u16(rgba, v); +#else + U64 px = cast(to_fixed(r * 65535)) << 0 + | cast(to_fixed(g * 65535)) << 16 + | cast(to_fixed(b * 65535)) << 32 + | cast(to_fixed(a * 65535)) << 48; + store(rgba, swap_endian_16x4(px)); +#endif +} + +FINAL_STAGE(store_hhh, NoCtx) { + uintptr_t ptr = (uintptr_t)(dst + 6*i); + assert( (ptr & 1) == 0 ); // The dst pointer must be 2-byte aligned + uint16_t* rgb = (uint16_t*)ptr; // for this cast to uint16_t* to be safe. + + U16 R = Half_from_F(r), + G = Half_from_F(g), + B = Half_from_F(b); +#if defined(USING_NEON) + uint16x4x3_t v = {{ + (uint16x4_t)R, + (uint16x4_t)G, + (uint16x4_t)B, + }}; + vst3_u16(rgb, v); +#else + store_3(rgb+0, R); + store_3(rgb+1, G); + store_3(rgb+2, B); +#endif +} + +FINAL_STAGE(store_hhhh, NoCtx) { + uintptr_t ptr = (uintptr_t)(dst + 8*i); + assert( (ptr & 1) == 0 ); // The dst pointer must be 2-byte aligned + uint16_t* rgba = (uint16_t*)ptr; // for this cast to uint16_t* to be safe. + + U16 R = Half_from_F(r), + G = Half_from_F(g), + B = Half_from_F(b), + A = Half_from_F(a); +#if defined(USING_NEON) + uint16x4x4_t v = {{ + (uint16x4_t)R, + (uint16x4_t)G, + (uint16x4_t)B, + (uint16x4_t)A, + }}; + vst4_u16(rgba, v); +#else + store(rgba, cast(R) << 0 + | cast(G) << 16 + | cast(B) << 32 + | cast(A) << 48); +#endif +} + +FINAL_STAGE(store_fff, NoCtx) { + uintptr_t ptr = (uintptr_t)(dst + 12*i); + assert( (ptr & 3) == 0 ); // The dst pointer must be 4-byte aligned + float* rgb = (float*)ptr; // for this cast to float* to be safe. +#if defined(USING_NEON) + float32x4x3_t v = {{ + (float32x4_t)r, + (float32x4_t)g, + (float32x4_t)b, + }}; + vst3q_f32(rgb, v); +#else + store_3(rgb+0, r); + store_3(rgb+1, g); + store_3(rgb+2, b); +#endif +} + +FINAL_STAGE(store_ffff, NoCtx) { + uintptr_t ptr = (uintptr_t)(dst + 16*i); + assert( (ptr & 3) == 0 ); // The dst pointer must be 4-byte aligned + float* rgba = (float*)ptr; // for this cast to float* to be safe. +#if defined(USING_NEON) + float32x4x4_t v = {{ + (float32x4_t)r, + (float32x4_t)g, + (float32x4_t)b, + (float32x4_t)a, + }}; + vst4q_f32(rgba, v); +#else + store_4(rgba+0, r); + store_4(rgba+1, g); + store_4(rgba+2, b); + store_4(rgba+3, a); +#endif +} + +#if SKCMS_HAS_MUSTTAIL + + SI void exec_stages(StageFn* stages, const void** contexts, const char* src, char* dst, int i) { + (*stages)({stages}, contexts, src, dst, F0, F0, F0, F1, i); + } + +#else + + static void exec_stages(const Op* ops, const void** contexts, + const char* src, char* dst, int i) { + F r = F0, g = F0, b = F0, a = F1; + while (true) { + switch (*ops++) { +#define M(name) case Op::name: Exec_##name(*contexts++, src, dst, r, g, b, a, i); break; + SKCMS_WORK_OPS(M) +#undef M +#define M(name) case Op::name: Exec_##name(*contexts++, src, dst, r, g, b, a, i); return; + SKCMS_STORE_OPS(M) +#undef M + } + } + } + +#endif + +// 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) { +#if SKCMS_HAS_MUSTTAIL + // Convert the program into an array of tailcall stages. + StageFn stages[32]; + assert(programSize <= ARRAY_COUNT(stages)); + + static constexpr StageFn kStageFns[] = { +#define M(name) &Exec_##name, + SKCMS_WORK_OPS(M) + SKCMS_STORE_OPS(M) +#undef M + }; + + for (ptrdiff_t index = 0; index < programSize; ++index) { + stages[index] = kStageFns[(int)program[index]]; + } +#else + // Use the op array as-is. + const Op* stages = program; +#endif + + int i = 0; + while (n >= N) { + exec_stages(stages, contexts, src, dst, i); + i += N; + n -= N; + } + if (n > 0) { + char tmp[4*4*N] = {0}; + + memcpy(tmp, (const char*)src + (size_t)i*src_bpp, (size_t)n*src_bpp); + exec_stages(stages, contexts, tmp, tmp, 0); + memcpy((char*)dst + (size_t)i*dst_bpp, tmp, (size_t)n*dst_bpp); + } +} diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/src/skcms_Transform.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/src/skcms_Transform.h new file mode 100644 index 0000000000..9f02e792fb --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/src/skcms_Transform.h @@ -0,0 +1,162 @@ +/* + * Copyright 2018 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#pragma once + +#include +#include + +// skcms_Transform.h contains skcms implementation details. +// Please don't use this header from outside the skcms repo. + +namespace skcms_private { + +/** All transform ops */ + +#define SKCMS_WORK_OPS(M) \ + M(load_a8) \ + M(load_g8) \ + M(load_4444) \ + M(load_565) \ + M(load_888) \ + M(load_8888) \ + M(load_1010102) \ + M(load_101010x_XR) \ + M(load_10101010_XR) \ + M(load_161616LE) \ + M(load_16161616LE) \ + M(load_161616BE) \ + M(load_16161616BE) \ + M(load_hhh) \ + M(load_hhhh) \ + M(load_fff) \ + M(load_ffff) \ + \ + M(swap_rb) \ + M(clamp) \ + M(invert) \ + M(force_opaque) \ + M(premul) \ + M(unpremul) \ + M(matrix_3x3) \ + M(matrix_3x4) \ + \ + M(lab_to_xyz) \ + M(xyz_to_lab) \ + \ + M(gamma_r) \ + M(gamma_g) \ + M(gamma_b) \ + M(gamma_a) \ + M(gamma_rgb) \ + \ + M(tf_r) \ + M(tf_g) \ + M(tf_b) \ + M(tf_a) \ + M(tf_rgb) \ + \ + M(pq_r) \ + M(pq_g) \ + M(pq_b) \ + M(pq_a) \ + M(pq_rgb) \ + \ + M(hlg_r) \ + M(hlg_g) \ + M(hlg_b) \ + M(hlg_a) \ + M(hlg_rgb) \ + \ + M(hlginv_r) \ + M(hlginv_g) \ + M(hlginv_b) \ + M(hlginv_a) \ + M(hlginv_rgb) \ + \ + M(table_r) \ + M(table_g) \ + M(table_b) \ + M(table_a) \ + \ + M(clut_A2B) \ + M(clut_B2A) + +#define SKCMS_STORE_OPS(M) \ + M(store_a8) \ + M(store_g8) \ + M(store_4444) \ + M(store_565) \ + M(store_888) \ + M(store_8888) \ + M(store_1010102) \ + M(store_161616LE) \ + M(store_16161616LE) \ + M(store_161616BE) \ + M(store_16161616BE) \ + M(store_101010x_XR) \ + M(store_hhh) \ + M(store_hhhh) \ + M(store_fff) \ + M(store_ffff) + +enum class Op : int { +#define M(op) op, + SKCMS_WORK_OPS(M) + SKCMS_STORE_OPS(M) +#undef M +}; + +/** Constants */ + +#if defined(__clang__) || defined(__GNUC__) + static constexpr float INFINITY_ = __builtin_inff(); +#else + static const union { + uint32_t bits; + float f; + } inf_ = { 0x7f800000 }; + #define INFINITY_ inf_.f +#endif + +/** Vector type */ + +#if defined(__clang__) + template using Vec = T __attribute__((ext_vector_type(N))); +#elif defined(__GNUC__) + // Unfortunately, GCC does not allow us to omit the struct. This will not compile: + // template using Vec = T __attribute__((vector_size(N*sizeof(T)))); + template struct VecHelper { + typedef T __attribute__((vector_size(N * sizeof(T)))) V; + }; + template using Vec = typename VecHelper::V; +#endif + +/** Interface */ + +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); + +} +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); + +} +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); + +} +} // namespace skcms_private diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/src/skcms_TransformBaseline.cc b/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/src/skcms_TransformBaseline.cc new file mode 100644 index 0000000000..bfe1df60a0 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/src/skcms_TransformBaseline.cc @@ -0,0 +1,48 @@ +/* + * Copyright 2018 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "skcms_public.h" // NO_G3_REWRITE +#include "skcms_internals.h" // NO_G3_REWRITE +#include "skcms_Transform.h" // NO_G3_REWRITE +#include +#include +#include +#include +#include + +#if defined(__ARM_NEON) + #include +#elif defined(__SSE__) + #include + + #if defined(__clang__) + // That #include is usually enough, but Clang's headers + // avoid #including the whole kitchen sink when _MSC_VER is defined, + // because lots of programs on Windows would include that and it'd be + // a lot slower. But we want all those headers included, so we can use + // their features (after making runtime checks). + #include + #endif +#endif + +namespace skcms_private { +namespace baseline { + +#if defined(SKCMS_PORTABLE) + // Build skcms in a portable scalar configuration. + #define N 1 + template using V = T; +#else + // Build skcms with basic four-line SIMD support. (SSE on Intel, or Neon on ARM) + #define N 4 + template using V = skcms_private::Vec; +#endif + +#include "Transform_inl.h" + +} // namespace baseline +} // namespace skcms_private diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/src/skcms_TransformHsw.cc b/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/src/skcms_TransformHsw.cc new file mode 100644 index 0000000000..cd3673b1b0 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/src/skcms_TransformHsw.cc @@ -0,0 +1,61 @@ +/* + * Copyright 2018 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "skcms_public.h" // NO_G3_REWRITE +#include "skcms_internals.h" // NO_G3_REWRITE +#include "skcms_Transform.h" // NO_G3_REWRITE +#include +#include +#include +#include +#include + +#if defined(__ARM_NEON) + #include +#elif defined(__SSE__) + #include + + #if defined(__clang__) + // That #include is usually enough, but Clang's headers + // avoid #including the whole kitchen sink when _MSC_VER is defined, + // because lots of programs on Windows would include that and it'd be + // a lot slower. But we want all those headers included, so we can use + // their features (after making runtime checks). + #include + #include + #include + #include + #include + #endif +#endif + +namespace skcms_private { +namespace hsw { + +#if defined(SKCMS_DISABLE_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) { + skcms_private::baseline::run_program(program, contexts, programSize, + src, dst, n, src_bpp, dst_bpp); +} + +#else + +#define USING_AVX +#define USING_AVX_F16C +#define USING_AVX2 +#define N 8 +template using V = skcms_private::Vec; + +#include "Transform_inl.h" + +#endif + +} // namespace hsw +} // namespace skcms_private diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/src/skcms_TransformSkx.cc b/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/src/skcms_TransformSkx.cc new file mode 100644 index 0000000000..3e849dd4ec --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/src/skcms_TransformSkx.cc @@ -0,0 +1,58 @@ +/* + * Copyright 2018 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "skcms_public.h" // NO_G3_REWRITE +#include "skcms_internals.h" // NO_G3_REWRITE +#include "skcms_Transform.h" // NO_G3_REWRITE +#include +#include +#include +#include +#include + +#if defined(__ARM_NEON) + #include +#elif defined(__SSE__) + #include + + #if defined(__clang__) + // That #include is usually enough, but Clang's headers + // avoid #including the whole kitchen sink when _MSC_VER is defined, + // because lots of programs on Windows would include that and it'd be + // a lot slower. But we want all those headers included, so we can use + // their features (after making runtime checks). + #include + #include + #include + #include + #include + #endif +#endif + +namespace skcms_private { +namespace skx { + +#if defined(SKCMS_DISABLE_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) { + skcms_private::baseline::run_program(program, contexts, programSize, + src, dst, n, src_bpp, dst_bpp); +} + +#else + +#define USING_AVX512F +#define N 16 +template using V = skcms_private::Vec; +#include "Transform_inl.h" + +#endif + +} // namespace skx +} // namespace skcms_private diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/src/skcms_internals.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/src/skcms_internals.h new file mode 100644 index 0000000000..f3f0a2d6cb --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/src/skcms_internals.h @@ -0,0 +1,138 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#pragma once + +// skcms_internals.h contains APIs shared by skcms' internals and its test tools. +// Please don't use this header from outside the skcms repo. + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// ~~~~ General Helper Macros ~~~~ +// skcms can leverage some C++ extensions when they are present. +#define ARRAY_COUNT(arr) (int)(sizeof((arr)) / sizeof(*(arr))) + +#if defined(__clang__) && defined(__has_cpp_attribute) + #if __has_cpp_attribute(clang::fallthrough) + #define SKCMS_FALLTHROUGH [[clang::fallthrough]] + #endif + + #ifndef SKCMS_HAS_MUSTTAIL + // [[clang::musttail]] is great for performance, but it's not well supported and we run into + // a variety of problems when we use it. Fortunately, it's an optional feature that doesn't + // affect correctness, and usually the compiler will generate a tail-call even for us + // whether or not we force it to do so. + // + // Known limitations: + // - Sanitizers do not work well with [[clang::musttail]], and corrupt src/dst pointers. + // (https://github.com/llvm/llvm-project/issues/70849) + // - Wasm tail-calls were only introduced in 2023 and aren't a mainstream feature yet. + // - Clang 18 runs into an ICE on armv7/androideabi with [[clang::musttail]]. + // (http://crbug.com/1504548) + // - Android RISC-V also runs into an ICE (b/314692534) + // - LoongArch developers indicate they had to turn it off + // - Windows builds generate incorrect code with [[clang::musttail]] and crash mysteriously. + // (http://crbug.com/1505442) + #if __has_cpp_attribute(clang::musttail) && !__has_feature(memory_sanitizer) \ + && !__has_feature(address_sanitizer) \ + && !defined(__EMSCRIPTEN__) \ + && !defined(__arm__) \ + && !defined(__riscv) \ + && !defined(__loongarch__) \ + && !defined(_WIN32) && !defined(__SYMBIAN32__) + #define SKCMS_HAS_MUSTTAIL 1 + #endif + #endif +#endif + +#ifndef SKCMS_FALLTHROUGH + #define SKCMS_FALLTHROUGH +#endif +#ifndef SKCMS_HAS_MUSTTAIL + #define SKCMS_HAS_MUSTTAIL 0 +#endif + +#if defined(__clang__) + #define SKCMS_MAYBE_UNUSED __attribute__((unused)) + #pragma clang diagnostic ignored "-Wused-but-marked-unused" +#elif defined(__GNUC__) + #define SKCMS_MAYBE_UNUSED __attribute__((unused)) +#elif defined(_MSC_VER) + #define SKCMS_MAYBE_UNUSED __pragma(warning(suppress:4100)) +#else + #define SKCMS_MAYBE_UNUSED +#endif + +// sizeof(x) will return size_t, which is 32-bit on some machines and 64-bit on others. +// We have better testing on 64-bit machines, so force 32-bit machines to behave like 64-bit. +// +// Please do not use sizeof() directly, and size_t only when required. +// (We have no way of enforcing these requests...) +#define SAFE_SIZEOF(x) ((uint64_t)sizeof(x)) + +// Same sort of thing for _Layout structs with a variable sized array at the end (named "variable"). +#define SAFE_FIXED_SIZE(type) ((uint64_t)offsetof(type, variable)) + +// If this isn't Clang, GCC, or Emscripten with SIMD support, we are in SKCMS_PORTABLE mode. +#if !defined(SKCMS_PORTABLE) && !(defined(__clang__) || \ + defined(__GNUC__) || \ + (defined(__EMSCRIPTEN__) && defined(__wasm_simd128__))) + #define SKCMS_PORTABLE 1 +#endif + +// If we are in SKCMS_PORTABLE mode or running on a non-x86-64 platform, we can't enable HSW or SKX. +// We also disable HSW/SKX on Android, even if it's Android on x64, since it's unlikely to benefit. +#if defined(SKCMS_PORTABLE) || !defined(__x86_64__) || defined(ANDROID) || defined(__ANDROID__) + #undef SKCMS_FORCE_HSW + #if !defined(SKCMS_DISABLE_HSW) + #define SKCMS_DISABLE_HSW 1 + #endif + + #undef SKCMS_FORCE_SKX + #if !defined(SKCMS_DISABLE_SKX) + #define SKCMS_DISABLE_SKX 1 + #endif +#endif + +// ~~~~ Shared ~~~~ +typedef struct skcms_ICCTag { + uint32_t signature; + uint32_t type; + uint32_t size; + const uint8_t* buf; +} skcms_ICCTag; + +typedef struct skcms_ICCProfile skcms_ICCProfile; +typedef struct skcms_TransferFunction skcms_TransferFunction; +typedef union skcms_Curve skcms_Curve; + +void skcms_GetTagByIndex (const skcms_ICCProfile*, uint32_t idx, skcms_ICCTag*); +bool skcms_GetTagBySignature(const skcms_ICCProfile*, uint32_t sig, skcms_ICCTag*); + +float skcms_MaxRoundtripError(const skcms_Curve* curve, const skcms_TransferFunction* inv_tf); + +// 252 of a random shuffle of all possible bytes. +// 252 is evenly divisible by 3 and 4. Only 192, 10, 241, and 43 are missing. +// Used for ICC profile equivalence testing. +extern const uint8_t skcms_252_random_bytes[252]; + +// ~~~~ Portable Math ~~~~ +static inline float floorf_(float x) { + float roundtrip = (float)((int)x); + return roundtrip > x ? roundtrip - 1 : roundtrip; +} +static inline float fabsf_(float x) { return x < 0 ? -x : x; } +float powf_(float, float); + +#ifdef __cplusplus +} +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/src/skcms_public.h b/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/src/skcms_public.h new file mode 100644 index 0000000000..3510f89ef8 --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/src/skcms_public.h @@ -0,0 +1,406 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#pragma once + +// skcms_public.h contains the entire public API for skcms. + +#ifndef SKCMS_API + #define SKCMS_API +#endif + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// A row-major 3x3 matrix (ie vals[row][col]) +typedef struct skcms_Matrix3x3 { + float vals[3][3]; +} skcms_Matrix3x3; + +// It is _not_ safe to alias the pointers to invert in-place. +SKCMS_API bool skcms_Matrix3x3_invert(const skcms_Matrix3x3*, skcms_Matrix3x3*); +SKCMS_API skcms_Matrix3x3 skcms_Matrix3x3_concat(const skcms_Matrix3x3*, const skcms_Matrix3x3*); + +// A row-major 3x4 matrix (ie vals[row][col]) +typedef struct skcms_Matrix3x4 { + float vals[3][4]; +} skcms_Matrix3x4; + +// A transfer function mapping encoded values to linear values, +// represented by this 7-parameter piecewise function: +// +// linear = sign(encoded) * (c*|encoded| + f) , 0 <= |encoded| < d +// = sign(encoded) * ((a*|encoded| + b)^g + e), d <= |encoded| +// +// (A simple gamma transfer function sets g to gamma and a to 1.) +typedef struct skcms_TransferFunction { + float g, a,b,c,d,e,f; +} skcms_TransferFunction; + +SKCMS_API float skcms_TransferFunction_eval (const skcms_TransferFunction*, float); +SKCMS_API bool skcms_TransferFunction_invert(const skcms_TransferFunction*, + skcms_TransferFunction*); + +typedef enum skcms_TFType { + skcms_TFType_Invalid, + skcms_TFType_sRGBish, + skcms_TFType_PQish, + skcms_TFType_HLGish, + skcms_TFType_HLGinvish, +} skcms_TFType; + +// Identify which kind of transfer function is encoded in an skcms_TransferFunction +SKCMS_API skcms_TFType skcms_TransferFunction_getType(const skcms_TransferFunction*); + +// We can jam a couple alternate transfer function forms into skcms_TransferFunction, +// including those matching the general forms of the SMPTE ST 2084 PQ function or HLG. +// +// PQish: +// max(A + B|encoded|^C, 0) +// linear = sign(encoded) * (------------------------) ^ F +// D + E|encoded|^C +SKCMS_API bool skcms_TransferFunction_makePQish(skcms_TransferFunction*, + float A, float B, float C, + float D, float E, float F); +// HLGish: +// { K * sign(encoded) * ( (R|encoded|)^G ) when 0 <= |encoded| <= 1/R +// linear = { K * sign(encoded) * ( e^(a(|encoded|-c)) + b ) when 1/R < |encoded| +SKCMS_API bool skcms_TransferFunction_makeScaledHLGish(skcms_TransferFunction*, + float K, float R, float G, + float a, float b, float c); + +// Compatibility shim with K=1 for old callers. +static inline bool skcms_TransferFunction_makeHLGish(skcms_TransferFunction* fn, + float R, float G, + float a, float b, float c) { + return skcms_TransferFunction_makeScaledHLGish(fn, 1.0f, R,G, a,b,c); +} + +// PQ mapping encoded [0,1] to linear [0,1]. +static inline bool skcms_TransferFunction_makePQ(skcms_TransferFunction* tf) { + return skcms_TransferFunction_makePQish(tf, -107/128.0f, 1.0f, 32/2523.0f + , 2413/128.0f, -2392/128.0f, 8192/1305.0f); +} +// HLG mapping encoded [0,1] to linear [0,12]. +static inline bool skcms_TransferFunction_makeHLG(skcms_TransferFunction* tf) { + return skcms_TransferFunction_makeHLGish(tf, 2.0f, 2.0f + , 1/0.17883277f, 0.28466892f, 0.55991073f); +} + +// Is this an ordinary sRGB-ish transfer function, or one of the HDR forms we support? +SKCMS_API bool skcms_TransferFunction_isSRGBish(const skcms_TransferFunction*); +SKCMS_API bool skcms_TransferFunction_isPQish (const skcms_TransferFunction*); +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 { + uint32_t alias_of_table_entries; + skcms_TransferFunction parametric; + }; + struct { + uint32_t table_entries; + const uint8_t* table_8; + const uint8_t* table_16; + }; +} skcms_Curve; + +// Complex transforms between device space (A) and profile connection space (B): +// A2B: device -> [ "A" curves -> CLUT ] -> [ "M" curves -> matrix ] -> "B" curves -> PCS +// B2A: device <- [ "A" curves <- CLUT ] <- [ "M" curves <- matrix ] <- "B" curves <- PCS + +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; + + // 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; + + // Required: 3 1D "B" curves. Always present, and output_channels must be 3. + uint32_t output_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]; + + // 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; + skcms_Curve matrix_curves[3]; + + // 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]; + const uint8_t* grid_8; + const uint8_t* grid_16; + skcms_Curve output_curves[4]; +} skcms_B2A; + +typedef struct skcms_CICP { + uint8_t color_primaries; + uint8_t transfer_characteristics; + uint8_t matrix_coefficients; + uint8_t video_full_range_flag; +} skcms_CICP; + +typedef struct skcms_ICCProfile { + const uint8_t* buffer; + + uint32_t size; + uint32_t data_color_space; + uint32_t pcs; + uint32_t tag_count; + + // skcms_Parse() will set commonly-used fields for you when possible: + + // 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; +} skcms_ICCProfile; + +// The sRGB color profile is so commonly used that we offer a canonical skcms_ICCProfile for it. +SKCMS_API const skcms_ICCProfile* skcms_sRGB_profile(void); +// Ditto for XYZD50, the most common profile connection space. +SKCMS_API const skcms_ICCProfile* skcms_XYZD50_profile(void); + +SKCMS_API const skcms_TransferFunction* skcms_sRGB_TransferFunction(void); +SKCMS_API const skcms_TransferFunction* skcms_sRGB_Inverse_TransferFunction(void); +SKCMS_API const skcms_TransferFunction* skcms_Identity_TransferFunction(void); + +// Practical equality test for two skcms_ICCProfiles. +// The implementation is subject to change, but it will always try to answer +// "can I substitute A for B?" and "can I skip transforming from A to B?". +SKCMS_API bool skcms_ApproximatelyEqualProfiles(const skcms_ICCProfile* A, + const skcms_ICCProfile* B); + +// Practical test that answers: Is curve roughly the inverse of inv_tf? Typically used by passing +// the inverse of a known parametric transfer function (like sRGB), to determine if a particular +// curve is very close to sRGB. +SKCMS_API bool skcms_AreApproximateInverses(const skcms_Curve* curve, + const skcms_TransferFunction* inv_tf); + +// Similar to above, answering the question for all three TRC curves of the given profile. Again, +// passing skcms_sRGB_InverseTransferFunction as inv_tf will answer the question: +// "Does this profile have a transfer function that is very close to sRGB?" +SKCMS_API bool skcms_TRCs_AreApproximateInverse(const skcms_ICCProfile* profile, + const skcms_TransferFunction* inv_tf); + +// Parse an ICC profile and return true if possible, otherwise return false. +// Selects an A2B profile (if present) according to priority list (each entry 0-2). +// The buffer is not copied; it must remain valid as long as the skcms_ICCProfile will be used. +SKCMS_API bool skcms_ParseWithA2BPriority(const void*, size_t, + const int priority[], int priorities, + skcms_ICCProfile*); + +static inline bool skcms_Parse(const void* buf, size_t len, skcms_ICCProfile* profile) { + // For continuity of existing user expectations, + // prefer A2B0 (perceptual) over A2B1 (relative colormetric), and ignore A2B2 (saturation). + const int priority[] = {0,1}; + return skcms_ParseWithA2BPriority(buf, len, + priority, sizeof(priority)/sizeof(*priority), + profile); +} + +SKCMS_API bool skcms_ApproximateCurve(const skcms_Curve* curve, + skcms_TransferFunction* approx, + float* max_error); + +SKCMS_API bool skcms_GetCHAD(const skcms_ICCProfile*, skcms_Matrix3x3*); +SKCMS_API bool skcms_GetWTPT(const skcms_ICCProfile*, float xyz[3]); + +// These are common ICC signature values +enum { + // data_color_space + skcms_Signature_CMYK = 0x434D594B, + skcms_Signature_Gray = 0x47524159, + skcms_Signature_RGB = 0x52474220, + + // pcs + skcms_Signature_Lab = 0x4C616220, + skcms_Signature_XYZ = 0x58595A20, +}; + +typedef enum skcms_PixelFormat { + skcms_PixelFormat_A_8, + skcms_PixelFormat_A_8_, + skcms_PixelFormat_G_8, + skcms_PixelFormat_G_8_, + + skcms_PixelFormat_RGB_565, + skcms_PixelFormat_BGR_565, + + skcms_PixelFormat_ABGR_4444, + skcms_PixelFormat_ARGB_4444, + + skcms_PixelFormat_RGB_888, + skcms_PixelFormat_BGR_888, + skcms_PixelFormat_RGBA_8888, + skcms_PixelFormat_BGRA_8888, + skcms_PixelFormat_RGBA_8888_sRGB, // Automatic sRGB encoding / decoding. + skcms_PixelFormat_BGRA_8888_sRGB, // (Generally used with linear transfer functions.) + + skcms_PixelFormat_RGBA_1010102, + skcms_PixelFormat_BGRA_1010102, + + skcms_PixelFormat_RGB_161616LE, // Little-endian. Pointers must be 16-bit aligned. + skcms_PixelFormat_BGR_161616LE, + skcms_PixelFormat_RGBA_16161616LE, + skcms_PixelFormat_BGRA_16161616LE, + + skcms_PixelFormat_RGB_161616BE, // Big-endian. Pointers must be 16-bit aligned. + skcms_PixelFormat_BGR_161616BE, + skcms_PixelFormat_RGBA_16161616BE, + skcms_PixelFormat_BGRA_16161616BE, + + skcms_PixelFormat_RGB_hhh_Norm, // 1-5-10 half-precision float in [0,1] + skcms_PixelFormat_BGR_hhh_Norm, // Pointers must be 16-bit aligned. + skcms_PixelFormat_RGBA_hhhh_Norm, + skcms_PixelFormat_BGRA_hhhh_Norm, + + skcms_PixelFormat_RGB_hhh, // 1-5-10 half-precision float. + skcms_PixelFormat_BGR_hhh, // Pointers must be 16-bit aligned. + skcms_PixelFormat_RGBA_hhhh, + skcms_PixelFormat_BGRA_hhhh, + + skcms_PixelFormat_RGB_fff, // 1-8-23 single-precision float (the normal kind). + skcms_PixelFormat_BGR_fff, // Pointers must be 32-bit aligned. + skcms_PixelFormat_RGBA_ffff, + skcms_PixelFormat_BGRA_ffff, + + skcms_PixelFormat_RGB_101010x_XR, // Note: This is located here to signal no clamping. + skcms_PixelFormat_BGR_101010x_XR, // Compatible with MTLPixelFormatBGR10_XR. + skcms_PixelFormat_RGBA_10101010_XR, // Note: This is located here to signal no clamping. + skcms_PixelFormat_BGRA_10101010_XR, // Compatible with MTLPixelFormatBGRA10_XR. +} skcms_PixelFormat; + +// We always store any alpha channel linearly. In the chart below, tf-1() is the inverse +// transfer function for the given color profile (applying the transfer function linearizes). + +// We treat opaque as a strong requirement, not just a performance hint: we will ignore +// any source alpha and treat it as 1.0, and will make sure that any destination alpha +// channel is filled with the equivalent of 1.0. + +// We used to offer multiple types of premultiplication, but now just one, PremulAsEncoded. +// This is the premul you're probably used to working with. + +typedef enum skcms_AlphaFormat { + skcms_AlphaFormat_Opaque, // alpha is always opaque + // tf-1(r), tf-1(g), tf-1(b), 1.0 + skcms_AlphaFormat_Unpremul, // alpha and color are unassociated + // tf-1(r), tf-1(g), tf-1(b), a + skcms_AlphaFormat_PremulAsEncoded, // premultiplied while encoded + // tf-1(r)*a, tf-1(g)*a, tf-1(b)*a, a +} skcms_AlphaFormat; + +// Convert npixels pixels from src format and color profile to dst format and color profile +// and return true, otherwise return false. It is safe to alias dst == src if dstFmt == srcFmt. +SKCMS_API bool skcms_Transform(const void* src, + skcms_PixelFormat srcFmt, + skcms_AlphaFormat srcAlpha, + const skcms_ICCProfile* srcProfile, + void* dst, + skcms_PixelFormat dstFmt, + skcms_AlphaFormat dstAlpha, + const skcms_ICCProfile* dstProfile, + size_t npixels); + +// If profile can be used as a destination in skcms_Transform, return true. Otherwise, attempt to +// rewrite it with approximations where reasonable. If successful, return true. If no reasonable +// approximation exists, leave the profile unchanged and return false. +SKCMS_API bool skcms_MakeUsableAsDestination(skcms_ICCProfile* profile); + +// If profile can be used as a destination with a single parametric transfer function (ie for +// rasterization), return true. Otherwise, attempt to rewrite it with approximations where +// reasonable. If successful, return true. If no reasonable approximation exists, leave the +// profile unchanged and return false. +SKCMS_API bool skcms_MakeUsableAsDestinationWithSingleCurve(skcms_ICCProfile* profile); + +// Returns a matrix to adapt XYZ color from given the whitepoint to D50. +SKCMS_API bool skcms_AdaptToXYZD50(float wx, float wy, + skcms_Matrix3x3* toXYZD50); + +// Returns a matrix to convert RGB color into XYZ adapted to D50, given the +// primaries and whitepoint of the RGB model. +SKCMS_API bool skcms_PrimariesToXYZD50(float rx, float ry, + float gx, float gy, + float bx, float by, + float wx, float wy, + skcms_Matrix3x3* toXYZD50); + +// Call before your first call to skcms_Transform() to skip runtime CPU detection. +SKCMS_API void skcms_DisableRuntimeCPUDetection(void); + +// Utilities for programmatically constructing profiles +static inline void skcms_Init(skcms_ICCProfile* p) { + memset(p, 0, sizeof(*p)); + p->data_color_space = skcms_Signature_RGB; + p->pcs = skcms_Signature_XYZ; +} + +static inline void skcms_SetTransferFunction(skcms_ICCProfile* p, + const skcms_TransferFunction* tf) { + p->has_trc = true; + for (int i = 0; i < 3; ++i) { + p->trc[i].table_entries = 0; + p->trc[i].parametric = *tf; + } +} + +static inline void skcms_SetXYZD50(skcms_ICCProfile* p, const skcms_Matrix3x3* m) { + p->has_toXYZD50 = true; + p->toXYZD50 = *m; +} + +#ifdef __cplusplus +} +#endif diff --git a/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/version.sha1 b/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/version.sha1 new file mode 100755 index 0000000000..aac43a8c6e --- /dev/null +++ b/Tests/LottieMetalTest/skia/PublicHeaders/skia/modules/skcms/version.sha1 @@ -0,0 +1 @@ +5d9221d28f9cbbe6db0b745b36d4e5efc09e168e diff --git a/Tests/LottieMetalTest/skia/libskia.framework/Info.plist b/Tests/LottieMetalTest/skia/libskia.framework/Info.plist new file mode 100644 index 0000000000000000000000000000000000000000..ac23c3e15d0f5ea176cec4ab46168d1312d3c5bd GIT binary patch literal 756 zcmZWl%Wl&^6rCAf;ZZVaXd#922oxwF%abTPR!E5nib~oLI}M)VqsEFVf z`Voaa3%-D5zXBGB1rlP(nz6Adg4x`ebI(0<&l&lgg^?_O#tRS~I(+2lvBL2aCr{0t zF5=RB`OL!N*>jbp^B0ycUbxM(0E1IgTCAXoXJu^Lv zzDSW~%($iFO3qd24&|QjP&evRn|ZE|i+OL824RYn)2XguPMAwK)bzEjXgzL3V=r)$ zUvyl~$9jf2HEgQQe1R_Is5~)vg3|Dg_)69#Rz79 zmYUD|k6uwTj*JmcEVewyXPgE)#$DzzCw4Z}|3^BO@XN-G2HS&}1 zRc5A-8IwUvdM#aTQcq{xu7Q8;CWNQLJPl&~YXnUS5n8|K$Z`{NXBkiF$8fi$iO;T?WSq25r08Q`&Xg~+A!7g|Q zK7f7j6(U%L_h1ta;7d4zyKoP_hacfS`~<(iZ}2<(0e_+bnnNX2Mk{C)t)o7&kc}9c TA|J&dJqZ4;;7(Svg5LZBTE6EC literal 0 HcmV?d00001 diff --git a/Tests/LottieMetalTest/skia/libskia.framework/libskia b/Tests/LottieMetalTest/skia/libskia.framework/libskia new file mode 100755 index 0000000000000000000000000000000000000000..a3688204a910c7fa69546ed0df7b3b6a4b31a5fa GIT binary patch literal 2961400 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

I~}@*s{*BlLhu>U`Ka`%@^23onfyL*jv@8 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�v)5Phef+pY)gI?6?_PYB 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π$f+9Muelu@LQZ8Or!pgQN@s(tW}a2< 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¬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 q=v@ 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 C{vdczov-mxU#;+3nQK1s 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 z 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

E+ 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 z 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

7Fk>T@sv_V-z;G{ka7(wL#PRKM{6Z0rIZ?q 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

ax3nhJzOp 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