From e1fbc38338dab8de22f118f2660f38560cc8d084 Mon Sep 17 00:00:00 2001 From: Lee Salzman Date: Fri, 4 Apr 2025 03:46:00 +0000 Subject: [PATCH] Bug 1958191 - Update Skia to m136. r=aosmond This looks like a fairly mild update with some of our local patches being upstreamed and otherwise reasonably small amounts of code churn. Differential Revision: https://phabricator.services.mozilla.com/D244290 --- gfx/skia/generate_mozbuild.py | 6 + gfx/skia/moz.build | 7 + gfx/skia/skia/include/codec/SkCodec.h | 49 +- gfx/skia/skia/include/config/SkUserConfig.h | 22 +- gfx/skia/skia/include/core/SkCanvas.h | 30 +- gfx/skia/skia/include/core/SkColor.h | 10 + gfx/skia/skia/include/core/SkColorSpace.h | 186 +- gfx/skia/skia/include/core/SkColorType.h | 146 +- gfx/skia/skia/include/core/SkContourMeasure.h | 4 +- gfx/skia/skia/include/core/SkFontScanner.h | 13 +- gfx/skia/skia/include/core/SkFontStyle.h | 2 +- gfx/skia/skia/include/core/SkMaskFilter.h | 8 - gfx/skia/skia/include/core/SkMilestone.h | 2 +- gfx/skia/skia/include/core/SkPaint.h | 13 +- gfx/skia/skia/include/core/SkPath.h | 2 +- gfx/skia/skia/include/core/SkRRect.h | 4 +- gfx/skia/skia/include/core/SkShader.h | 6 +- gfx/skia/skia/include/core/SkStream.h | 11 +- gfx/skia/skia/include/core/SkString.h | 16 +- gfx/skia/skia/include/core/SkSurface.h | 14 + gfx/skia/skia/include/core/SkTextBlob.h | 18 +- gfx/skia/skia/include/core/SkTypeface.h | 41 +- gfx/skia/skia/include/core/SkTypes.h | 5 + gfx/skia/skia/include/docs/SkPDFDocument.h | 49 +- gfx/skia/skia/include/docs/SkPDFJpegHelpers.h | 41 + .../skia/include/effects/SkGradientShader.h | 7 +- .../skia/include/effects/SkRuntimeEffect.h | 7 +- .../skia/include/effects/SkTableMaskFilter.h | 4 + gfx/skia/skia/include/encode/SkPngEncoder.h | 14 + gfx/skia/skia/include/gpu/GpuTypes.h | 9 + .../include/gpu/ganesh/GrContextOptions.h | 268 +-- .../skia/include/gpu/ganesh/GrDirectContext.h | 5 + .../gpu/ganesh/GrDriverBugWorkarounds.h | 6 +- gfx/skia/skia/include/gpu/ganesh/GrTypes.h | 23 +- .../include/gpu/graphite/BackendTexture.h | 2 - gfx/skia/skia/include/gpu/graphite/Context.h | 27 +- .../include/gpu/graphite/ContextOptions.h | 45 +- .../skia/include/gpu/graphite/GraphiteTypes.h | 60 +- gfx/skia/skia/include/gpu/graphite/Image.h | 1 + .../skia/include/gpu/graphite/LogPriority.h | 36 + .../include/gpu/graphite/PrecompileContext.h | 76 + gfx/skia/skia/include/gpu/graphite/Recorder.h | 6 + .../skia/include/gpu/graphite/Recording.h | 4 +- .../skia/include/gpu/graphite/TextureInfo.h | 145 +- .../gpu/graphite/YUVABackendTextures.h | 17 +- .../gpu/graphite/dawn/DawnBackendContext.h | 9 + .../gpu/graphite/dawn/DawnGraphiteTypes.h | 166 ++ .../include/gpu/graphite/dawn/DawnTypes.h | 144 +- .../include/gpu/graphite/dawn/DawnUtils.h | 23 +- .../gpu/graphite/mtl/MtlBackendContext.h | 4 + .../gpu/graphite/mtl/MtlGraphiteTypes.h | 68 +- .../gpu/graphite/mtl/MtlGraphiteTypesUtils.h | 10 + .../gpu/graphite/mtl/MtlGraphiteTypes_cpp.h | 50 + .../gpu/graphite/mtl/MtlGraphiteUtils.h | 22 +- .../gpu/graphite/precompile/Precompile.h | 23 +- .../graphite/precompile/PrecompileShader.h | 20 +- .../gpu/graphite/vk/VulkanGraphiteContext.h | 31 + .../gpu/graphite/vk/VulkanGraphiteTypes.h | 50 +- .../gpu/graphite/vk/VulkanGraphiteUtils.h | 24 +- gfx/skia/skia/include/pathops/SkPathOps.h | 10 +- .../ports/SkFontMgr_FontConfigInterface.h | 3 + .../include/ports/SkTypeface_fontations.h | 3 + .../include/private/SkJpegMetadataDecoder.h | 7 + gfx/skia/skia/include/private/SkPathRef.h | 36 +- .../skia/include/private/base/SkAnySubclass.h | 6 + .../skia/include/private/base/SkAttributes.h | 28 +- .../include/private/base/SkFloatingPoint.h | 12 - gfx/skia/skia/include/private/base/SkMutex.h | 8 + gfx/skia/skia/include/private/base/SkTArray.h | 6 +- .../skia/include/private/base/SkTDArray.h | 2 +- .../skia/include/private/base/SkTemplates.h | 35 +- .../skia/include/private/chromium/SkPMColor.h | 38 + .../include/private/gpu/ganesh/GrTypesPriv.h | 2 +- gfx/skia/skia/include/sksl/SkSLDebugTrace.h | 3 - gfx/skia/skia/modules/skcms/README.chromium | 6 + gfx/skia/skia/modules/skcms/skcms.cc | 177 +- .../skia/modules/skcms/src/Transform_inl.h | 40 +- .../skia/modules/skcms/src/skcms_Transform.h | 7 +- .../modules/skcms/src/skcms_TransformHsw.cc | 2 +- .../modules/skcms/src/skcms_TransformSkx.cc | 2 +- .../skia/modules/skcms/src/skcms_public.h | 34 +- gfx/skia/skia/modules/skcms/version.sha1 | 1 + gfx/skia/skia/src/base/SkCubics.cpp | 6 +- gfx/skia/skia/src/base/SkSharedMutex.h | 4 + gfx/skia/skia/src/base/SkTDArray.cpp | 1 - gfx/skia/skia/src/base/SkTInternalLList.h | 2 +- gfx/skia/skia/src/base/SkVx.h | 4 +- gfx/skia/skia/src/base/SkZip.h | 3 +- gfx/skia/skia/src/codec/SkAndroidCodec.cpp | 560 ------ .../skia/src/codec/SkAndroidCodecAdapter.cpp | 30 - .../skia/src/codec/SkAndroidCodecAdapter.h | 44 - gfx/skia/skia/src/codec/SkAvifCodec.cpp | 274 --- gfx/skia/skia/src/codec/SkAvifCodec.h | 110 -- gfx/skia/skia/src/codec/SkBmpBaseCodec.cpp | 21 - gfx/skia/skia/src/codec/SkBmpBaseCodec.h | 45 - gfx/skia/skia/src/codec/SkBmpCodec.cpp | 695 -------- gfx/skia/skia/src/codec/SkBmpCodec.h | 157 -- gfx/skia/skia/src/codec/SkBmpMaskCodec.cpp | 113 -- gfx/skia/skia/src/codec/SkBmpMaskCodec.h | 72 - gfx/skia/skia/src/codec/SkBmpRLECodec.cpp | 581 ------ gfx/skia/skia/src/codec/SkBmpRLECodec.h | 130 -- .../skia/src/codec/SkBmpStandardCodec.cpp | 352 ---- gfx/skia/skia/src/codec/SkBmpStandardCodec.h | 104 -- gfx/skia/skia/src/codec/SkCodec.cpp | 30 +- gfx/skia/skia/src/codec/SkCodecPriv.h | 471 ++--- gfx/skia/skia/src/codec/SkCrabbyAvifCodec.cpp | 404 ----- gfx/skia/skia/src/codec/SkCrabbyAvifCodec.h | 123 -- gfx/skia/skia/src/codec/SkEncodedInfo.cpp | 30 - gfx/skia/skia/src/codec/SkExif.cpp | 364 ---- gfx/skia/skia/src/codec/SkGainmapInfo.cpp | 295 ---- gfx/skia/skia/src/codec/SkHeifCodec.cpp | 568 ------ gfx/skia/skia/src/codec/SkHeifCodec.h | 137 -- gfx/skia/skia/src/codec/SkIcoCodec.cpp | 439 ----- gfx/skia/skia/src/codec/SkIcoCodec.h | 109 -- gfx/skia/skia/src/codec/SkJpegCodec.cpp | 971 ---------- gfx/skia/skia/src/codec/SkJpegCodec.h | 167 -- gfx/skia/skia/src/codec/SkJpegConstants.h | 68 - gfx/skia/skia/src/codec/SkJpegDecoderMgr.cpp | 157 -- gfx/skia/skia/src/codec/SkJpegDecoderMgr.h | 93 - .../src/codec/SkJpegMetadataDecoderImpl.cpp | 495 ------ .../src/codec/SkJpegMetadataDecoderImpl.h | 53 - .../skia/src/codec/SkJpegMultiPicture.cpp | 321 ---- gfx/skia/skia/src/codec/SkJpegMultiPicture.h | 73 - gfx/skia/skia/src/codec/SkJpegPriv.h | 64 - gfx/skia/skia/src/codec/SkJpegSegmentScan.cpp | 215 --- gfx/skia/skia/src/codec/SkJpegSegmentScan.h | 135 -- gfx/skia/skia/src/codec/SkJpegSourceMgr.cpp | 439 ----- gfx/skia/skia/src/codec/SkJpegSourceMgr.h | 77 - gfx/skia/skia/src/codec/SkJpegUtility.cpp | 152 -- gfx/skia/skia/src/codec/SkJpegUtility.h | 43 - gfx/skia/skia/src/codec/SkJpegXmp.cpp | 206 --- gfx/skia/skia/src/codec/SkJpegXmp.h | 22 - gfx/skia/skia/src/codec/SkJpegxlCodec.cpp | 495 ------ gfx/skia/skia/src/codec/SkJpegxlCodec.h | 107 -- gfx/skia/skia/src/codec/SkMaskSwizzler.cpp | 575 ------ gfx/skia/skia/src/codec/SkMaskSwizzler.h | 76 - .../skia/src/codec/SkParseEncodedOrigin.cpp | 26 - .../skia/src/codec/SkParseEncodedOrigin.h | 20 - gfx/skia/skia/src/codec/SkPngCodec.cpp | 1039 ----------- gfx/skia/skia/src/codec/SkPngCodec.h | 99 -- gfx/skia/skia/src/codec/SkPngCodecBase.cpp | 355 ---- gfx/skia/skia/src/codec/SkPngCodecBase.h | 109 -- gfx/skia/skia/src/codec/SkPngPriv.h | 19 - gfx/skia/skia/src/codec/SkRawCodec.cpp | 856 --------- gfx/skia/skia/src/codec/SkRawCodec.h | 69 - gfx/skia/skia/src/codec/SkSampledCodec.cpp | 353 ---- gfx/skia/skia/src/codec/SkSampledCodec.h | 65 - gfx/skia/skia/src/codec/SkSampler.h | 2 +- gfx/skia/skia/src/codec/SkScalingCodec.h | 45 - .../skia/src/codec/SkStubHeifDecoderAPI.h | 81 - gfx/skia/skia/src/codec/SkSwizzler.cpp | 1259 ------------- gfx/skia/skia/src/codec/SkSwizzler.h | 233 --- gfx/skia/skia/src/codec/SkTiffUtility.cpp | 296 ---- gfx/skia/skia/src/codec/SkTiffUtility.h | 152 -- gfx/skia/skia/src/codec/SkWbmpCodec.cpp | 237 --- gfx/skia/skia/src/codec/SkWbmpCodec.h | 73 - gfx/skia/skia/src/codec/SkWebpCodec.cpp | 610 ------- gfx/skia/skia/src/codec/SkWebpCodec.h | 113 -- gfx/skia/skia/src/codec/SkWuffsCodec.cpp | 1114 ------------ gfx/skia/skia/src/codec/SkXmp.cpp | 669 ------- gfx/skia/skia/src/core/Sk4px.h | 2 +- gfx/skia/skia/src/core/SkAAClip.cpp | 8 +- gfx/skia/skia/src/core/SkAnalyticEdge.cpp | 38 +- gfx/skia/skia/src/core/SkAnalyticEdge.h | 25 +- gfx/skia/skia/src/core/SkAutoBlitterChoose.h | 17 +- gfx/skia/skia/src/core/SkBitmapDevice.cpp | 24 +- gfx/skia/skia/src/core/SkBitmapDevice.h | 5 +- gfx/skia/skia/src/core/SkBitmapProcState.cpp | 2 +- gfx/skia/skia/src/core/SkBlendMode.cpp | 67 +- gfx/skia/skia/src/core/SkBlendModePriv.h | 2 +- gfx/skia/skia/src/core/SkBlitRow_D32.cpp | 7 +- gfx/skia/skia/src/core/SkBlitter.cpp | 57 +- gfx/skia/skia/src/core/SkBlitter.h | 37 +- gfx/skia/skia/src/core/SkBlitter_A8.cpp | 6 +- gfx/skia/skia/src/core/SkBlitter_A8.h | 3 +- gfx/skia/skia/src/core/SkBlitter_ARGB32.cpp | 124 +- gfx/skia/skia/src/core/SkBlitter_Sprite.cpp | 6 +- gfx/skia/skia/src/core/SkBlurEngine.cpp | 18 + gfx/skia/skia/src/core/SkBlurMask.cpp | 54 +- gfx/skia/skia/src/core/SkBlurMask.h | 20 +- .../skia/src/core/SkBlurMaskFilterImpl.cpp | 272 +-- gfx/skia/skia/src/core/SkBlurMaskFilterImpl.h | 24 +- gfx/skia/skia/src/core/SkCanvas.cpp | 138 +- gfx/skia/skia/src/core/SkColor.cpp | 25 +- .../private => src/core}/SkColorData.h | 44 +- gfx/skia/skia/src/core/SkColorFilter.cpp | 7 +- .../skia/{include => src}/core/SkColorPriv.h | 30 +- gfx/skia/skia/src/core/SkColorSpace.cpp | 120 ++ .../skia/src/core/SkColorSpaceXformSteps.cpp | 100 +- .../skia/src/core/SkColorSpaceXformSteps.h | 8 +- .../skia/src/core/SkCompressedDataUtils.cpp | 4 +- gfx/skia/skia/src/core/SkConvertPixels.cpp | 20 +- gfx/skia/skia/src/core/SkCoreBlitters.h | 46 +- gfx/skia/skia/src/core/SkDebugUtils.h | 32 + gfx/skia/skia/src/core/SkDevice.cpp | 16 +- gfx/skia/skia/src/core/SkDevice.h | 14 +- gfx/skia/skia/src/core/SkDraw.cpp | 4 +- gfx/skia/skia/src/core/SkDrawBase.cpp | 58 +- gfx/skia/skia/src/core/SkDrawBase.h | 47 +- gfx/skia/skia/src/core/SkDrawTypes.h | 21 + gfx/skia/skia/src/core/SkDraw_atlas.cpp | 6 +- gfx/skia/skia/src/core/SkDraw_text.cpp | 6 +- gfx/skia/skia/src/core/SkDraw_vertices.cpp | 2 +- gfx/skia/skia/src/core/SkEdge.cpp | 30 +- gfx/skia/skia/src/core/SkEdge.h | 26 +- gfx/skia/skia/src/core/SkEdgeBuilder.cpp | 78 +- gfx/skia/skia/src/core/SkFontPriv.h | 8 +- gfx/skia/skia/src/core/SkFontScanner.h | 43 - gfx/skia/skia/src/core/SkGlyph.cpp | 6 - gfx/skia/skia/src/core/SkGlyph.h | 19 +- gfx/skia/skia/src/core/SkGlyphRunPainter.cpp | 70 +- gfx/skia/skia/src/core/SkImageFilter.cpp | 8 +- gfx/skia/skia/src/core/SkImageFilterTypes.cpp | 77 +- gfx/skia/skia/src/core/SkImageFilterTypes.h | 56 +- gfx/skia/skia/src/core/SkImageInfoPriv.h | 17 + .../skia/src/core/SkKnownRuntimeEffects.cpp | 511 +++--- .../skia/src/core/SkKnownRuntimeEffects.h | 37 +- gfx/skia/skia/src/core/SkLRUCache.h | 37 +- gfx/skia/skia/src/core/SkMD5.cpp | 4 +- gfx/skia/skia/src/core/SkMask.h | 4 +- gfx/skia/skia/src/core/SkMaskBlurFilter.cpp | 20 +- gfx/skia/skia/src/core/SkMaskCache.cpp | 27 +- gfx/skia/skia/src/core/SkMaskCache.h | 14 +- gfx/skia/skia/src/core/SkMaskFilter.cpp | 308 ---- gfx/skia/skia/src/core/SkMaskFilterBase.cpp | 313 ++++ gfx/skia/skia/src/core/SkMaskFilterBase.h | 68 +- gfx/skia/skia/src/core/SkMaskGamma.h | 2 +- gfx/skia/skia/src/core/SkMatrix.cpp | 62 +- .../skia/src/core/SkMipmapHQDownSampler.cpp | 3 +- gfx/skia/skia/src/core/SkOpts.cpp | 4 +- gfx/skia/skia/src/core/SkOpts.h | 8 +- gfx/skia/skia/src/core/SkPaint.cpp | 2 +- gfx/skia/skia/src/core/SkPath.cpp | 4 +- gfx/skia/skia/src/core/SkPathPriv.h | 11 +- gfx/skia/skia/src/core/SkPathRef.cpp | 3 +- gfx/skia/skia/src/core/SkPath_serial.cpp | 2 +- gfx/skia/skia/src/core/SkPicturePriv.h | 5 +- gfx/skia/skia/src/core/SkPixmap.cpp | 4 +- gfx/skia/skia/src/core/SkRRect.cpp | 6 +- gfx/skia/skia/src/core/SkRasterPipeline.cpp | 64 +- gfx/skia/skia/src/core/SkRasterPipeline.h | 24 +- .../skia/src/core/SkRasterPipelineBlitter.cpp | 174 +- .../src/core/SkRasterPipelineOpContexts.h | 176 +- .../skia/src/core/SkRasterPipelineOpList.h | 88 +- .../src/core/SkRasterPipelineVizualizer.h | 114 ++ gfx/skia/skia/src/core/SkResourceCache.cpp | 4 +- gfx/skia/skia/src/core/SkRuntimeBlender.cpp | 41 +- gfx/skia/skia/src/core/SkRuntimeEffect.cpp | 11 +- gfx/skia/skia/src/core/SkRuntimeEffectPriv.h | 33 +- gfx/skia/skia/src/core/SkScalerContext.cpp | 19 +- gfx/skia/skia/src/core/SkScalerContext.h | 14 +- gfx/skia/skia/src/core/SkScan_AAAPath.cpp | 62 +- gfx/skia/skia/src/core/SkScan_Antihair.cpp | 2 +- gfx/skia/skia/src/core/SkScan_Path.cpp | 16 +- gfx/skia/skia/src/core/SkStream.cpp | 4 + gfx/skia/skia/src/core/SkString.cpp | 2 +- gfx/skia/skia/src/core/SkSwizzlePriv.h | 2 +- gfx/skia/skia/src/core/SkTHash.h | 54 +- gfx/skia/skia/src/core/SkTextBlob.cpp | 10 +- gfx/skia/skia/src/core/SkTextBlobPriv.h | 8 +- gfx/skia/skia/src/core/SkTraceEventCommon.h | 2 - gfx/skia/skia/src/core/SkTypeface.cpp | 41 +- gfx/skia/skia/src/core/SkTypeface_remote.cpp | 4 +- gfx/skia/skia/src/core/SkTypeface_remote.h | 4 +- gfx/skia/skia/src/core/SkUnPreMultiply.cpp | 2 +- gfx/skia/skia/src/core/SkVertices.cpp | 46 +- .../skia/src/effects/SkEmbossMaskFilter.h | 1 - .../skia/src/effects/SkTableMaskFilter.cpp | 19 + .../colorfilters/SkBlendModeColorFilter.cpp | 18 +- .../colorfilters/SkColorFilterBase.cpp | 4 +- .../effects/colorfilters/SkColorFilterBase.h | 2 +- .../colorfilters/SkRuntimeColorFilter.cpp | 33 +- .../colorfilters/SkTableColorFilter.cpp | 3 +- .../SkWorkingFormatColorFilter.cpp | 100 +- .../colorfilters/SkWorkingFormatColorFilter.h | 30 +- .../SkDisplacementMapImageFilter.cpp | 14 +- .../skia/src/encode/SkJpegEncoderImpl.cpp | 123 +- .../skia/src/encode/SkJpegGainmapEncoder.cpp | 22 +- gfx/skia/skia/src/encode/SkPngEncoderBase.cpp | 276 +++ gfx/skia/skia/src/encode/SkPngEncoderBase.h | 64 + gfx/skia/skia/src/encode/SkPngEncoderImpl.cpp | 365 ++-- gfx/skia/skia/src/encode/SkPngEncoderImpl.h | 13 +- .../src/image/SkImage_AndroidFactories.cpp | 5 +- gfx/skia/skia/src/image/SkImage_Picture.cpp | 25 + gfx/skia/skia/src/image/SkImage_Picture.h | 8 + gfx/skia/skia/src/image/SkSurface.cpp | 4 + gfx/skia/skia/src/image/SkSurface_Base.h | 2 + gfx/skia/skia/src/opts/SkBlitMask_opts.h | 58 +- gfx/skia/skia/src/opts/SkBlitRow_opts.h | 17 +- gfx/skia/skia/src/opts/SkOpts_SetTarget.h | 1 + .../skia/src/opts/SkRasterPipeline_opts.h | 1561 +++++++++-------- gfx/skia/skia/src/opts/SkSwizzler_opts.inc | 2 +- gfx/skia/skia/src/pathops/SkOpSegment.cpp | 22 +- gfx/skia/skia/src/pathops/SkPathOpsCommon.h | 3 + gfx/skia/skia/src/pathops/SkPathOpsCubic.cpp | 6 +- gfx/skia/skia/src/pathops/SkPathOpsDebug.cpp | 4 +- gfx/skia/skia/src/pathops/SkPathOpsPoint.h | 2 +- gfx/skia/skia/src/pathops/SkPathOpsTSect.cpp | 3 +- gfx/skia/skia/src/pathops/SkPathOpsTSect.h | 2 - .../skia/src/pathops/SkPathOpsTightBounds.cpp | 4 +- gfx/skia/skia/src/pdf/SkClusterator.h | 2 +- gfx/skia/skia/src/pdf/SkJpegInfo.h | 10 - .../skia/src/pdf/SkJpegInfo_libjpegturbo.cpp | 9 - gfx/skia/skia/src/pdf/SkJpegInfo_none.cpp | 10 - gfx/skia/skia/src/pdf/SkPDFBitmap.cpp | 54 +- gfx/skia/skia/src/pdf/SkPDFBitmap.h | 7 - gfx/skia/skia/src/pdf/SkPDFDevice.cpp | 103 +- gfx/skia/skia/src/pdf/SkPDFDevice.h | 5 +- gfx/skia/skia/src/pdf/SkPDFDocument.cpp | 21 +- gfx/skia/skia/src/pdf/SkPDFDocumentPriv.h | 13 +- gfx/skia/skia/src/pdf/SkPDFFont.cpp | 38 +- gfx/skia/skia/src/pdf/SkPDFGlyphUse.h | 6 +- .../src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp | 2 +- gfx/skia/skia/src/pdf/SkPDFTag.cpp | 171 +- gfx/skia/skia/src/pdf/SkPDFTypes.cpp | 20 +- gfx/skia/skia/src/pdf/SkPDFTypes.h | 34 +- gfx/skia/skia/src/pdf/SkPDFUtils.cpp | 37 - gfx/skia/skia/src/pdf/SkPDFUtils.h | 4 - .../skia/src/ports/SkFontConfigTypeface.h | 62 +- .../skia/src/ports/SkFontHost_FreeType.cpp | 175 +- .../src/ports/SkFontHost_FreeType_common.cpp | 12 +- gfx/skia/skia/src/ports/SkFontHost_cairo.cpp | 8 +- gfx/skia/skia/src/ports/SkFontHost_win.cpp | 16 +- .../ports/SkFontMgr_FontConfigInterface.cpp | 28 +- gfx/skia/skia/src/ports/SkFontMgr_android.cpp | 194 +- .../skia/src/ports/SkFontMgr_android_ndk.cpp | 211 +-- .../src/ports/SkFontMgr_custom_directory.cpp | 2 +- .../src/ports/SkFontMgr_custom_embedded.cpp | 2 +- .../skia/src/ports/SkFontMgr_fontconfig.cpp | 221 +-- gfx/skia/skia/src/ports/SkFontMgr_fuchsia.cpp | 8 +- gfx/skia/skia/src/ports/SkFontMgr_mac_ct.cpp | 72 +- .../src/ports/SkFontScanner_FreeType_priv.h | 11 +- .../src/ports/SkFontScanner_fontations.cpp | 21 +- .../skia/src/ports/SkFontScanner_fontations.h | 43 - .../src/ports/SkFontScanner_fontations_priv.h | 3 +- .../ports/SkGlobalInitialization_default.cpp | 2 +- .../skia/src/ports/SkScalerContext_mac_ct.cpp | 8 +- .../skia/src/ports/SkScalerContext_mac_ct.h | 2 +- .../skia/src/ports/SkScalerContext_win_dw.cpp | 8 +- .../skia/src/ports/SkScalerContext_win_dw.h | 2 +- gfx/skia/skia/src/ports/SkTypeface_FreeType.h | 13 +- .../skia/src/ports/SkTypeface_fontations.cpp | 237 ++- .../src/ports/SkTypeface_fontations_priv.h | 12 +- gfx/skia/skia/src/ports/SkTypeface_mac_ct.cpp | 188 +- gfx/skia/skia/src/ports/SkTypeface_proxy.cpp | 138 ++ gfx/skia/skia/src/ports/SkTypeface_proxy.h | 81 + gfx/skia/skia/src/ports/SkTypeface_win_dw.cpp | 46 +- gfx/skia/skia/src/ports/SkTypeface_win_dw.h | 1 + gfx/skia/skia/src/shaders/SkBlendShader.cpp | 4 +- gfx/skia/skia/src/shaders/SkColorShader.cpp | 100 +- gfx/skia/skia/src/shaders/SkColorShader.h | 47 +- .../skia/src/shaders/SkCoordClampShader.cpp | 2 +- gfx/skia/skia/src/shaders/SkImageShader.cpp | 23 +- .../src/shaders/SkPerlinNoiseShaderImpl.cpp | 2 +- gfx/skia/skia/src/shaders/SkRuntimeShader.cpp | 48 +- gfx/skia/skia/src/shaders/SkShaderBase.cpp | 2 +- gfx/skia/skia/src/shaders/SkShaderBase.h | 2 - .../skia/src/shaders/SkTriColorShader.cpp | 2 +- gfx/skia/skia/src/shaders/SkTriColorShader.h | 2 +- .../shaders/gradients/SkConicalGradient.cpp | 4 +- .../gradients/SkGradientBaseShader.cpp | 303 ++-- .../shaders/gradients/SkGradientBaseShader.h | 5 +- gfx/skia/skia/src/sksl/SkSLProgramSettings.h | 2 +- .../skia/src/sksl/codegen/SkSLCodeGenerator.h | 4 - .../sksl/codegen/SkSLGLSLCodeGenerator.cpp | 192 +- .../sksl/codegen/SkSLMetalCodeGenerator.cpp | 307 ++-- .../codegen/SkSLRasterPipelineBuilder.cpp | 100 +- .../sksl/codegen/SkSLSPIRVCodeGenerator.cpp | 2 +- .../sksl/codegen/SkSLWGSLCodeGenerator.cpp | 2 + .../sksl_graphite_frag.minified.sksl | 393 +++-- .../sksl_graphite_frag.unoptimized.sksl | 311 ++-- .../sksl_graphite_vert.minified.sksl | 114 +- .../sksl_graphite_vert.unoptimized.sksl | 8 +- gfx/skia/skia/src/sksl/ir/SkSLType.cpp | 3 +- .../skia/src/sksl/sksl_graphite_frag.sksl | 292 ++- .../skia/src/sksl/sksl_graphite_vert.sksl | 12 + .../src/sksl/tracing/SkSLDebugTracePriv.cpp | 13 - .../src/sksl/tracing/SkSLDebugTracePriv.h | 5 - gfx/skia/skia/src/text/gpu/StrikeCache.cpp | 4 + .../skia/src/text/gpu/SubRunContainer.cpp | 2 +- gfx/skia/skia/src/text/gpu/TextBlob.cpp | 2 +- gfx/skia/skia/src/utils/SkBitSet.h | 4 +- .../skia/src/utils/SkCharToGlyphCache.cpp | 34 +- gfx/skia/skia/src/utils/SkCharToGlyphCache.h | 8 +- gfx/skia/skia/src/utils/SkCustomTypeface.cpp | 9 +- gfx/skia/skia/src/utils/SkFloatUtils.h | 2 +- gfx/skia/skia/src/utils/SkJSONWriter.cpp | 47 + gfx/skia/skia/src/utils/SkJSONWriter.h | 419 +++++ gfx/skia/skia/src/utils/SkPatchUtils.cpp | 2 +- .../skia/src/utils/SkShadowTessellator.cpp | 2 +- gfx/skia/skia/src/utils/SkShadowUtils.cpp | 22 +- gfx/skia/skia/src/utils/mac/SkCTFont.cpp | 13 +- .../skia/src/utils/mac/SkCreateCGImageRef.cpp | 2 +- image/test/gtest/TestBlendAnimationFilter.cpp | 2 +- layout/reftests/bugs/reftest.list | 6 +- 395 files changed, 9423 insertions(+), 24891 deletions(-) create mode 100644 gfx/skia/skia/include/docs/SkPDFJpegHelpers.h create mode 100644 gfx/skia/skia/include/gpu/graphite/LogPriority.h create mode 100644 gfx/skia/skia/include/gpu/graphite/PrecompileContext.h create mode 100644 gfx/skia/skia/include/gpu/graphite/dawn/DawnGraphiteTypes.h create mode 100644 gfx/skia/skia/include/gpu/graphite/mtl/MtlGraphiteTypesUtils.h create mode 100644 gfx/skia/skia/include/gpu/graphite/mtl/MtlGraphiteTypes_cpp.h create mode 100644 gfx/skia/skia/include/gpu/graphite/vk/VulkanGraphiteContext.h create mode 100644 gfx/skia/skia/include/private/chromium/SkPMColor.h create mode 100644 gfx/skia/skia/modules/skcms/README.chromium create mode 100755 gfx/skia/skia/modules/skcms/version.sha1 delete mode 100644 gfx/skia/skia/src/codec/SkAndroidCodec.cpp delete mode 100644 gfx/skia/skia/src/codec/SkAndroidCodecAdapter.cpp delete mode 100644 gfx/skia/skia/src/codec/SkAndroidCodecAdapter.h delete mode 100644 gfx/skia/skia/src/codec/SkAvifCodec.cpp delete mode 100644 gfx/skia/skia/src/codec/SkAvifCodec.h delete mode 100644 gfx/skia/skia/src/codec/SkBmpBaseCodec.cpp delete mode 100644 gfx/skia/skia/src/codec/SkBmpBaseCodec.h delete mode 100644 gfx/skia/skia/src/codec/SkBmpCodec.cpp delete mode 100644 gfx/skia/skia/src/codec/SkBmpCodec.h delete mode 100644 gfx/skia/skia/src/codec/SkBmpMaskCodec.cpp delete mode 100644 gfx/skia/skia/src/codec/SkBmpMaskCodec.h delete mode 100644 gfx/skia/skia/src/codec/SkBmpRLECodec.cpp delete mode 100644 gfx/skia/skia/src/codec/SkBmpRLECodec.h delete mode 100644 gfx/skia/skia/src/codec/SkBmpStandardCodec.cpp delete mode 100644 gfx/skia/skia/src/codec/SkBmpStandardCodec.h delete mode 100644 gfx/skia/skia/src/codec/SkCrabbyAvifCodec.cpp delete mode 100644 gfx/skia/skia/src/codec/SkCrabbyAvifCodec.h delete mode 100644 gfx/skia/skia/src/codec/SkEncodedInfo.cpp delete mode 100644 gfx/skia/skia/src/codec/SkExif.cpp delete mode 100644 gfx/skia/skia/src/codec/SkGainmapInfo.cpp delete mode 100644 gfx/skia/skia/src/codec/SkHeifCodec.cpp delete mode 100644 gfx/skia/skia/src/codec/SkHeifCodec.h delete mode 100644 gfx/skia/skia/src/codec/SkIcoCodec.cpp delete mode 100644 gfx/skia/skia/src/codec/SkIcoCodec.h delete mode 100644 gfx/skia/skia/src/codec/SkJpegCodec.cpp delete mode 100644 gfx/skia/skia/src/codec/SkJpegCodec.h delete mode 100644 gfx/skia/skia/src/codec/SkJpegConstants.h delete mode 100644 gfx/skia/skia/src/codec/SkJpegDecoderMgr.cpp delete mode 100644 gfx/skia/skia/src/codec/SkJpegDecoderMgr.h delete mode 100644 gfx/skia/skia/src/codec/SkJpegMetadataDecoderImpl.cpp delete mode 100644 gfx/skia/skia/src/codec/SkJpegMetadataDecoderImpl.h delete mode 100644 gfx/skia/skia/src/codec/SkJpegMultiPicture.cpp delete mode 100644 gfx/skia/skia/src/codec/SkJpegMultiPicture.h delete mode 100644 gfx/skia/skia/src/codec/SkJpegPriv.h delete mode 100644 gfx/skia/skia/src/codec/SkJpegSegmentScan.cpp delete mode 100644 gfx/skia/skia/src/codec/SkJpegSegmentScan.h delete mode 100644 gfx/skia/skia/src/codec/SkJpegSourceMgr.cpp delete mode 100644 gfx/skia/skia/src/codec/SkJpegSourceMgr.h delete mode 100644 gfx/skia/skia/src/codec/SkJpegUtility.cpp delete mode 100644 gfx/skia/skia/src/codec/SkJpegUtility.h delete mode 100644 gfx/skia/skia/src/codec/SkJpegXmp.cpp delete mode 100644 gfx/skia/skia/src/codec/SkJpegXmp.h delete mode 100644 gfx/skia/skia/src/codec/SkJpegxlCodec.cpp delete mode 100644 gfx/skia/skia/src/codec/SkJpegxlCodec.h delete mode 100644 gfx/skia/skia/src/codec/SkMaskSwizzler.cpp delete mode 100644 gfx/skia/skia/src/codec/SkMaskSwizzler.h delete mode 100644 gfx/skia/skia/src/codec/SkParseEncodedOrigin.cpp delete mode 100644 gfx/skia/skia/src/codec/SkParseEncodedOrigin.h delete mode 100644 gfx/skia/skia/src/codec/SkPngCodec.cpp delete mode 100644 gfx/skia/skia/src/codec/SkPngCodec.h delete mode 100644 gfx/skia/skia/src/codec/SkPngCodecBase.cpp delete mode 100644 gfx/skia/skia/src/codec/SkPngCodecBase.h delete mode 100644 gfx/skia/skia/src/codec/SkPngPriv.h delete mode 100644 gfx/skia/skia/src/codec/SkRawCodec.cpp delete mode 100644 gfx/skia/skia/src/codec/SkRawCodec.h delete mode 100644 gfx/skia/skia/src/codec/SkSampledCodec.cpp delete mode 100644 gfx/skia/skia/src/codec/SkSampledCodec.h delete mode 100644 gfx/skia/skia/src/codec/SkScalingCodec.h delete mode 100644 gfx/skia/skia/src/codec/SkStubHeifDecoderAPI.h delete mode 100644 gfx/skia/skia/src/codec/SkSwizzler.cpp delete mode 100644 gfx/skia/skia/src/codec/SkSwizzler.h delete mode 100644 gfx/skia/skia/src/codec/SkTiffUtility.cpp delete mode 100644 gfx/skia/skia/src/codec/SkTiffUtility.h delete mode 100644 gfx/skia/skia/src/codec/SkWbmpCodec.cpp delete mode 100644 gfx/skia/skia/src/codec/SkWbmpCodec.h delete mode 100644 gfx/skia/skia/src/codec/SkWebpCodec.cpp delete mode 100644 gfx/skia/skia/src/codec/SkWebpCodec.h delete mode 100644 gfx/skia/skia/src/codec/SkWuffsCodec.cpp delete mode 100644 gfx/skia/skia/src/codec/SkXmp.cpp rename gfx/skia/skia/{include/private => src/core}/SkColorData.h (91%) rename gfx/skia/skia/{include => src}/core/SkColorPriv.h (88%) create mode 100644 gfx/skia/skia/src/core/SkDrawTypes.h delete mode 100644 gfx/skia/skia/src/core/SkFontScanner.h create mode 100644 gfx/skia/skia/src/core/SkMaskFilterBase.cpp create mode 100644 gfx/skia/skia/src/core/SkRasterPipelineVizualizer.h create mode 100644 gfx/skia/skia/src/encode/SkPngEncoderBase.cpp create mode 100644 gfx/skia/skia/src/encode/SkPngEncoderBase.h delete mode 100644 gfx/skia/skia/src/pdf/SkJpegInfo.h delete mode 100644 gfx/skia/skia/src/pdf/SkJpegInfo_libjpegturbo.cpp delete mode 100644 gfx/skia/skia/src/pdf/SkJpegInfo_none.cpp delete mode 100644 gfx/skia/skia/src/ports/SkFontScanner_fontations.h create mode 100644 gfx/skia/skia/src/ports/SkTypeface_proxy.cpp create mode 100644 gfx/skia/skia/src/ports/SkTypeface_proxy.h create mode 100644 gfx/skia/skia/src/utils/SkJSONWriter.cpp create mode 100644 gfx/skia/skia/src/utils/SkJSONWriter.h diff --git a/gfx/skia/generate_mozbuild.py b/gfx/skia/generate_mozbuild.py index ef4544614194..c44410310e87 100755 --- a/gfx/skia/generate_mozbuild.py +++ b/gfx/skia/generate_mozbuild.py @@ -219,6 +219,12 @@ def generate_separated_sources(platform_sources): separated = defaultdict(set, { 'common': { + 'skia/src/codec/SkCodec.cpp', + 'skia/src/codec/SkCodecImageGenerator.cpp', + 'skia/src/codec/SkColorPalette.cpp', + 'skia/src/codec/SkImageGenerator_FromEncoded.cpp', + 'skia/src/codec/SkPixmapUtils.cpp', + 'skia/src/codec/SkSampler.cpp', 'skia/src/effects/imagefilters/SkBlendImageFilter.cpp', 'skia/src/effects/imagefilters/SkBlurImageFilter.cpp', 'skia/src/effects/imagefilters/SkComposeImageFilter.cpp', diff --git a/gfx/skia/moz.build b/gfx/skia/moz.build index cd3fcc946764..b09617175ffc 100644 --- a/gfx/skia/moz.build +++ b/gfx/skia/moz.build @@ -49,6 +49,12 @@ UNIFIED_SOURCES += [ 'skia/src/base/SkTSearch.cpp', 'skia/src/base/SkUTF.cpp', 'skia/src/base/SkUtils.cpp', + 'skia/src/codec/SkCodec.cpp', + 'skia/src/codec/SkCodecImageGenerator.cpp', + 'skia/src/codec/SkColorPalette.cpp', + 'skia/src/codec/SkImageGenerator_FromEncoded.cpp', + 'skia/src/codec/SkPixmapUtils.cpp', + 'skia/src/codec/SkSampler.cpp', 'skia/src/core/SkAAClip.cpp', 'skia/src/core/SkAlphaRuns.cpp', 'skia/src/core/SkAnalyticEdge.cpp', @@ -127,6 +133,7 @@ UNIFIED_SOURCES += [ 'skia/src/core/SkMaskBlurFilter.cpp', 'skia/src/core/SkMaskCache.cpp', 'skia/src/core/SkMaskFilter.cpp', + 'skia/src/core/SkMaskFilterBase.cpp', 'skia/src/core/SkMaskGamma.cpp', 'skia/src/core/SkMasks.cpp', 'skia/src/core/SkMatrixInvert.cpp', diff --git a/gfx/skia/skia/include/codec/SkCodec.h b/gfx/skia/skia/include/codec/SkCodec.h index 3ff126ec2226..dc5f202da250 100644 --- a/gfx/skia/skia/include/codec/SkCodec.h +++ b/gfx/skia/skia/include/codec/SkCodec.h @@ -412,6 +412,9 @@ public: * If a scanline decode is in progress, scanline mode will end, requiring the client to call * startScanlineDecode() in order to return to decoding scanlines. * + * For certain codecs, reading into a smaller bitmap than the original dimensions may not + * produce correct results (e.g. animated webp). + * * @return Result kSuccess, or another value explaining the type of failure. */ Result getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, const Options*); @@ -781,12 +784,46 @@ public: * * As such, future decoding calls may require a rewind. * - * For still (non-animated) image codecs, this will return 0. + * `getRepetitionCount` will return `0` in two cases: + * 1. Still (non-animated) images. + * 2. Animated images that only play the animation once (i.e. that don't + * repeat the animation) + * `isAnimated` can be used to disambiguate between these two cases. */ int getRepetitionCount() { return this->onGetRepetitionCount(); } + /** + * `isAnimated` returns whether the full input is expected to contain an + * animated image (i.e. more than 1 image frame). This can be used to + * disambiguate the meaning of `getRepetitionCount` returning `0` (see + * `getRepetitionCount`'s doc comment for more details). + * + * Note that in some codecs `getFrameCount()` only returns the number of + * frames for which all the metadata has been already successfully decoded. + * Therefore for a partial input `isAnimated()` may return "yes", even + * though `getFrameCount()` may temporarily return `1` until more of the + * input is available. + * + * When handling partial input, some codecs may not know until later (e.g. + * until encountering additional image frames) whether the given image has + * more than one frame. Such codecs may initially return + * `IsAnimated::kUnknown` and only later give a definitive "yes" or "no" + * answer. GIF format is one example where this may happen. + * + * Other codecs may be able to decode the information from the metadata + * present before the first image frame. Such codecs should be able to give + * a definitive "yes" or "no" answer as soon as they are constructed. PNG + * format is one example where this happens. + */ + enum class IsAnimated { + kYes, + kNo, + kUnknown, + }; + IsAnimated isAnimated() { return this->onIsAnimated(); } + // Register a decoder at runtime by passing two function pointers: // - peek() to return true if the span of bytes appears to be your encoded format; // - make() to attempt to create an SkCodec from the given stream. @@ -812,6 +849,7 @@ protected: } virtual bool onGetGainmapCodec(SkGainmapInfo*, std::unique_ptr*) { return false; } + virtual bool onGetGainmapInfo(SkGainmapInfo*) { return false; } // TODO(issues.skia.org/363544350): This API only works for JPEG images. Remove this API once // it is no longer used. @@ -933,6 +971,10 @@ protected: return 0; } + virtual IsAnimated onIsAnimated() { + return IsAnimated::kNo; + } + private: const SkEncodedInfo fEncodedInfo; XformFormat fSrcXformFormat; @@ -1053,8 +1095,9 @@ private: friend class PNGCodecGM; // for fillIncompleteImage friend class SkSampledCodec; friend class SkIcoCodec; - friend class SkAndroidCodec; // for fEncodedInfo - friend class SkPDFBitmap; // for fEncodedInfo + friend class SkPngCodec; // for onGetGainmapCodec + friend class SkAndroidCodec; // for handleFrameIndex + friend class SkCodecPriv; // for fEncodedInfo }; namespace SkCodecs { diff --git a/gfx/skia/skia/include/config/SkUserConfig.h b/gfx/skia/skia/include/config/SkUserConfig.h index 3e01c39fbb30..571f151de333 100644 --- a/gfx/skia/skia/include/config/SkUserConfig.h +++ b/gfx/skia/skia/include/config/SkUserConfig.h @@ -74,15 +74,11 @@ */ //#define SK_R32_SHIFT 16 -/* Determines whether to build code that supports the Ganesh GPU backend. Some classes - that are not GPU-specific, such as SkShader subclasses, have optional code - that is used allows them to interact with this GPU backend. If you'd like to - include this code, include -DSK_GANESH in your cflags or uncomment below. - Defaults to not set (No Ganesh GPU backend). - This define affects the ABI of Skia, so make sure it matches the client which uses - the compiled version of Skia. +/* This controls how much space should be pre-allocated in an SkCanvas object + to store the SkMatrix and clip via calls to SkCanvas::save() (and balanced with + SkCanvas::restore()). */ -//#define SK_GANESH +//#define SK_CANVAS_SAVE_RESTORE_PREALLOC_COUNT 32 /* Skia makes use of histogram logging macros to trace the frequency of events. By default, Skia provides no-op versions of these macros. @@ -93,8 +89,14 @@ //#define SK_HISTOGRAM_ENUMERATION(name, sampleEnum, enumSize) //#define SK_HISTOGRAM_EXACT_LINEAR(name, sample, valueMax) //#define SK_HISTOGRAM_MEMORY_KB(name, sample) +//#define SK_HISTOGRAM_CUSTOM_COUNTS(name, sample, countMin, countMax, bucketCount) //#define SK_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(name, sampleUSec, minUSec, maxUSec, bucketCount) +/* + * Skia can provide extensive logging of Graphite Pipeline lifetimes. + */ +//#define SK_PIPELINE_LIFETIME_LOGGING + // To use smaller but slower mipmap builder //#define SK_USE_DRAWING_MIPMAP_DOWNSAMPLER @@ -141,6 +143,8 @@ #define SK_USE_FREETYPE_EMBOLDEN +#define SK_DISABLE_DIRECTWRITE_COLRv1 1 + #ifndef MOZ_IMPLICIT # ifdef MOZ_CLANG_PLUGIN # define MOZ_IMPLICIT __attribute__((annotate("moz_implicit"))) @@ -151,6 +155,4 @@ #define SK_DISABLE_LEGACY_IMAGE_READBUFFER -#define SK_DISABLE_DIRECTWRITE_COLRv1 1 - #endif diff --git a/gfx/skia/skia/include/core/SkCanvas.h b/gfx/skia/skia/include/core/SkCanvas.h index 6372c6a59681..e7c059074695 100644 --- a/gfx/skia/skia/include/core/SkCanvas.h +++ b/gfx/skia/skia/include/core/SkCanvas.h @@ -34,6 +34,7 @@ #include "include/private/base/SkDeque.h" #include "include/private/base/SkTArray.h" +#include #include #include #include @@ -53,6 +54,7 @@ class GrRecordingContext; class SkBitmap; class SkBlender; +class SkBlurMaskFilterImpl; class SkColorSpace; class SkData; class SkDevice; @@ -2502,15 +2504,18 @@ private: void reset(SkDevice* device); }; - // the first N recs that can fit here mean we won't call malloc - static constexpr int kMCRecSize = 96; // most recent measurement - static constexpr int kMCRecCount = 32; // common depth for save/restores +#if defined(SK_CANVAS_SAVE_RESTORE_PREALLOC_COUNT) + static constexpr int kMCRecCount = SK_CANVAS_SAVE_RESTORE_PREALLOC_COUNT; +#else + static constexpr int kMCRecCount = 32; // common depth for save/restores +#endif - intptr_t fMCRecStorage[kMCRecSize * kMCRecCount / sizeof(intptr_t)]; + // This stack allocation of memory will be used to house the first kMCRecCount + // layers without need to call malloc. + alignas(MCRec) std::byte fMCRecStorage[sizeof(MCRec) * kMCRecCount]; - SkDeque fMCStack; - // points to top of stack - MCRec* fMCRec; + SkDeque fMCStack; // uses the stack memory + MCRec* fMCRec; // points to top of stack for convenience // Installed via init() sk_sp fRootDevice; @@ -2675,11 +2680,14 @@ private: // into the canvas' global space. SkRect computeDeviceClipBounds(bool outsetForAA=true) const; - // Attempt to draw a rrect with an analytic blur. If the paint does not contain a blur, or the - // geometry can't be drawn with an analytic blur by the device, a layer is returned for a - // regular draw. If the draw succeeds or predrawNotify fails, nullopt is returned indicating - // that nothing further should be drawn. + // Returns the paint's mask filter if it can be used to draw an rrect with an analytic blur, and + // returns null otherwise. + const SkBlurMaskFilterImpl* canAttemptBlurredRRectDraw(const SkPaint&) const; + + // Attempt to draw a rrect with an analytic blur. If the draw succeeds or predrawNotify fails, + // nullopt is returned indicating that nothing further should be drawn. std::optional attemptBlurredRRectDraw(const SkRRect&, + const SkBlurMaskFilterImpl*, const SkPaint&, SkEnumBitMask); diff --git a/gfx/skia/skia/include/core/SkColor.h b/gfx/skia/skia/include/core/SkColor.h index 33352c7a838c..69439a9ee259 100644 --- a/gfx/skia/skia/include/core/SkColor.h +++ b/gfx/skia/skia/include/core/SkColor.h @@ -12,6 +12,7 @@ #include "include/core/SkScalar.h" #include "include/core/SkTypes.h" #include "include/private/base/SkCPUTypes.h" +#include "include/private/base/SkTPin.h" #include #include @@ -415,6 +416,15 @@ struct SkRGBA4f { SkRGBA4f makeOpaque() const { return { fR, fG, fB, 1.0f }; } + + /** + Returns a copy of the SkRGBA4f but with the alpha component pinned to [0, 1]. + + @return color with pinned alpha + */ + SkRGBA4f pinAlpha() const { + return { fR, fG, fB, SkTPin(fA, 0.f, 1.f) }; + } }; /** \struct SkColor4f diff --git a/gfx/skia/skia/include/core/SkColorSpace.h b/gfx/skia/skia/include/core/SkColorSpace.h index 57c29e222a43..5bfc8bc78453 100644 --- a/gfx/skia/skia/include/core/SkColorSpace.h +++ b/gfx/skia/skia/include/core/SkColorSpace.h @@ -39,6 +39,85 @@ struct SK_API SkColorSpacePrimaries { bool toXYZD50(skcms_Matrix3x3* toXYZD50) const; }; +namespace SkNamedPrimaries { + +//////////////////////////////////////////////////////////////////////////////// +// Color primaries defined by ITU-T H.273, table 2. Names are given by the first +// specification referenced in the value's row. + +// Rec. ITU-R BT.709-6, value 1. +static constexpr SkColorSpacePrimaries kRec709 = { + 0.64f, 0.33f, 0.3f, 0.6f, 0.15f, 0.06f, 0.3127f, 0.329f}; + +// Rec. ITU-R BT.470-6 System M (historical), value 4. +static constexpr SkColorSpacePrimaries kRec470SystemM = { + 0.67f, 0.33f, 0.21f, 0.71f, 0.14f, 0.08f, 0.31f, 0.316f}; + +// Rec. ITU-R BT.470-6 System B, G (historical), value 5. +static constexpr SkColorSpacePrimaries kRec470SystemBG = { + 0.64f, 0.33f, 0.29f, 0.60f, 0.15f, 0.06f, 0.3127f, 0.3290f}; + +// Rec. ITU-R BT.601-7 525, value 6. +static constexpr SkColorSpacePrimaries kRec601 = { + 0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f, 0.3127f, 0.3290f}; + +// SMPTE ST 240, value 7 (functionally the same as value 6). +static constexpr SkColorSpacePrimaries kSMPTE_ST_240 = kRec601; + +// Generic film (colour filters using Illuminant C), value 8. +static constexpr SkColorSpacePrimaries kGenericFilm = { + 0.681f, 0.319f, 0.243f, 0.692f, 0.145f, 0.049f, 0.310f, 0.316f}; + +// Rec. ITU-R BT.2020-2, value 9. +static constexpr SkColorSpacePrimaries kRec2020{ + 0.708f, 0.292f, 0.170f, 0.797f, 0.131f, 0.046f, 0.3127f, 0.3290f}; + +// SMPTE ST 428-1, value 10. +static constexpr SkColorSpacePrimaries kSMPTE_ST_428_1 = { + 1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f / 3.f, 1.f / 3.f}; + +// SMPTE RP 431-2, value 11. +static constexpr SkColorSpacePrimaries kSMPTE_RP_431_2 = { + 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f, 0.314f, 0.351f}; + +// SMPTE EG 432-1, value 12. +static constexpr SkColorSpacePrimaries kSMPTE_EG_432_1 = { + 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f, 0.3127f, 0.3290f}; + +// No corresponding industry specification identified, value 22. +// This is sometimes referred to as EBU 3213-E, but that document doesn't +// specify these values. +static constexpr SkColorSpacePrimaries kITU_T_H273_Value22 = { + 0.630f, 0.340f, 0.295f, 0.605f, 0.155f, 0.077f, 0.3127f, 0.3290f}; + +// Mapping between names of color primaries and the number of the corresponding +// row in ITU-T H.273, table 2. As above, the constants are named based on the +// first specification referenced in the value's row. +enum class CicpId : uint8_t { + // Value 0 is reserved. + kRec709 = 1, + // Value 2 is unspecified. + // Value 3 is reserved. + kRec470SystemM = 4, + kRec470SystemBG = 5, + kRec601 = 6, + kSMPTE_ST_240 = 7, + kGenericFilm = 8, + kRec2020 = 9, + kSMPTE_ST_428_1 = 10, + kSMPTE_RP_431_2 = 11, + kSMPTE_EG_432_1 = 12, + // Values 13-21 are reserved. + kITU_T_H273_Value22 = 22, + // Values 23-255 are reserved. +}; + +// https://www.w3.org/TR/css-color-4/#predefined-prophoto-rgb +static constexpr SkColorSpacePrimaries kProPhotoRGB = { + 0.7347f, 0.2653f, 0.1596f, 0.8404f, 0.0366f, 0.0001f, 0.34567f, 0.35850f}; + +} // namespace SkNamedPrimaries + namespace SkNamedTransferFn { // Like SkNamedGamut::kSRGB, keeping this bitwise exactly the same as skcms makes things fastest. @@ -48,18 +127,98 @@ static constexpr skcms_TransferFunction kSRGB = static constexpr skcms_TransferFunction k2Dot2 = { 2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; +static constexpr skcms_TransferFunction kRec2020 = { + 2.22222f, 0.909672f, 0.0903276f, 0.222222f, 0.0812429f, 0, 0}; + +//////////////////////////////////////////////////////////////////////////////// +// Color primaries defined by ITU-T H.273, table 3. Names are given by the first +// specification referenced in the value's row. + +// Rec. ITU-R BT.709-6, value 1. +static constexpr skcms_TransferFunction kRec709 = {2.222222222222f, + 0.909672415686f, + 0.090327584314f, + 0.222222222222f, + 0.081242858299f, + 0.f, + 0.f}; + +// Rec. ITU-R BT.470-6 System M (historical) assumed display gamma 2.2, value 4. +static constexpr skcms_TransferFunction kRec470SystemM = {2.2f, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f}; + +// Rec. ITU-R BT.470-6 System B, G (historical) assumed display gamma 2.8, +// value 5. +static constexpr skcms_TransferFunction kRec470SystemBG = {2.8f, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f}; + +// Rec. ITU-R BT.601-7, same as kRec709, value 6. +static constexpr skcms_TransferFunction kRec601 = kRec709; + +// SMPTE ST 240, value 7. +static constexpr skcms_TransferFunction kSMPTE_ST_240 = { + 2.222222222222f, 0.899626676224f, 0.100373323776f, 0.25f, 0.091286342118f, 0.f, 0.f}; + +// Linear, value 8 static constexpr skcms_TransferFunction kLinear = { 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; -static constexpr skcms_TransferFunction kRec2020 = - {2.22222f, 0.909672f, 0.0903276f, 0.222222f, 0.0812429f, 0, 0}; +// IEC 61966-2-4, value 11, same as kRec709 (but is explicitly extended). +static constexpr skcms_TransferFunction kIEC61966_2_4 = kRec709; +// IEC 61966-2-1 sRGB, value 13. +static constexpr skcms_TransferFunction kIEC61966_2_1 = kSRGB; + +// Rec. ITU-R BT.2020-2 (10-bit system), value 14. +static constexpr skcms_TransferFunction kRec2020_10bit = kRec709; + +// Rec. ITU-R BT.2020-2 (12-bit system), value 15. +static constexpr skcms_TransferFunction kRec2020_12bit = kRec709; + +// Rec. ITU-R BT.2100-2 perceptual quantization (PQ) system, value 16. static constexpr skcms_TransferFunction kPQ = {-2.0f, -107/128.0f, 1.0f, 32/2523.0f, 2413/128.0f, -2392/128.0f, 8192/1305.0f }; +// SMPTE ST 428-1, value 17. +static constexpr skcms_TransferFunction kSMPTE_ST_428_1 = { + 2.6f, 1.034080527699f, 0.f, 0.f, 0.f, 0.f, 0.f}; + +// Rec. ITU-R BT.2100-2 hybrid log-gamma (HLG) system, value 18. static constexpr skcms_TransferFunction kHLG = {-3.0f, 2.0f, 2.0f, 1/0.17883277f, 0.28466892f, 0.55991073f, 0.0f }; +// Mapping between transfer function names and the number of the corresponding +// row in ITU-T H.273, table 3. As above, the constants are named based on the +// first specification referenced in the value's row. +enum class CicpId : uint8_t { + // Value 0 is reserved. + kRec709 = 1, + // Value 2 is unspecified. + // Value 3 is reserved. + kRec470SystemM = 4, + kRec470SystemBG = 5, + kRec601 = 6, + kSMPTE_ST_240 = 7, + kLinear = 8, + // Value 9 is not supported by `SkColorSpace::MakeCICP`. + // Value 10 is not supported by `SkColorSpace::MakeCICP`. + kIEC61966_2_4 = 11, + // Value 12 is not supported by `SkColorSpace::MakeCICP`. + kIEC61966_2_1 = 13, + kSRGB = kIEC61966_2_1, + kRec2020_10bit = 14, + kRec2020_12bit = 15, + kPQ = 16, + kSMPTE_ST_428_1 = 17, + kHLG = 18, + // Values 19-255 are reserved. +}; + +// https://w3.org/TR/css-color-4/#valdef-color-prophoto-rgb +// "The transfer curve is a gamma function with a value of 1/1.8" +static constexpr skcms_TransferFunction kProPhotoRGB = {1.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}; + +// https://www.w3.org/TR/css-color-4/#predefined-a98-rgb +static constexpr skcms_TransferFunction kA98RGB = k2Dot2; + } // namespace SkNamedTransferFn namespace SkNamedGamut { @@ -122,6 +281,29 @@ public: static sk_sp MakeRGB(const skcms_TransferFunction& transferFn, const skcms_Matrix3x3& toXYZ); + /** + * Create an SkColorSpace from code points specified in Rec. ITU-T H.273. + * Null will be returned for invalid or unsupported combination of code + * points. + * + * Parameters: + * + * - `color_primaries` identifies an entry in Rec. ITU-T H.273, Table 2. + * - `transfer_characteristics` identifies an entry in Rec. ITU-T H.273, Table 3. + * + * `SkColorSpace` (and the underlying `skcms_ICCProfile`) only supports RGB + * color spaces and therefore this function does not take a + * `matrix_coefficients` parameter - the caller is expected to verify that + * `matrix_coefficients` is `0`. + * + * Narrow range images are extremely rare - see + * https://github.com/w3c/png/issues/312#issuecomment-2327349614. Therefore + * this function doesn't take a `video_full_range_flag` - the caller is + * expected to verify that it is `1` (indicating a full range image). + */ + static sk_sp MakeCICP(SkNamedPrimaries::CicpId color_primaries, + SkNamedTransferFn::CicpId transfer_characteristics); + /** * Create an SkColorSpace from a parsed (skcms) ICC profile. */ diff --git a/gfx/skia/skia/include/core/SkColorType.h b/gfx/skia/skia/include/core/SkColorType.h index fa36d5978d0e..77c4d632ae53 100644 --- a/gfx/skia/skia/include/core/SkColorType.h +++ b/gfx/skia/skia/include/core/SkColorType.h @@ -15,43 +15,125 @@ kN32_SkColorType selects the native 32-bit ARGB format for the current configuration. This can lead to inconsistent results across platforms, so use with caution. + + By default, Skia operates with the assumption of a little-Endian system. The names of each + SkColorType implicitly define the channel ordering and size in memory. Due to historical reasons + the names do not follow 100% identical convention, but are typically labeled from least + significant to most significant. To help clarify when the actual data layout differs from the + default convention, every SkColorType's comment includes a bit-labeled description of a pixel + in that color type on a LE system. + + Unless specified otherwise, a channel's value is treated as an unsigned integer with a range of + of [0, 2^N-1] and this is mapped uniformly to a floating point value of [0.0, 1.0]. Some color + types instead store data directly in 32-bit floating point (assumed to be IEEE), or in 16-bit + "half" floating point values. A half float, or F16/float16, is interpreted as FP 1-5-10 or + Bits: [sign:15 exp:14..10 man:9..0] */ enum SkColorType : int { - kUnknown_SkColorType, //!< uninitialized - kAlpha_8_SkColorType, //!< pixel with alpha in 8-bit byte - kRGB_565_SkColorType, //!< pixel with 5 bits red, 6 bits green, 5 bits blue, in 16-bit word - kARGB_4444_SkColorType, //!< pixel with 4 bits for alpha, red, green, blue; in 16-bit word - kRGBA_8888_SkColorType, //!< pixel with 8 bits for red, green, blue, alpha; in 32-bit word - kRGB_888x_SkColorType, //!< pixel with 8 bits each for red, green, blue; in 32-bit word - kBGRA_8888_SkColorType, //!< pixel with 8 bits for blue, green, red, alpha; in 32-bit word - kRGBA_1010102_SkColorType, //!< 10 bits for red, green, blue; 2 bits for alpha; in 32-bit word - kBGRA_1010102_SkColorType, //!< 10 bits for blue, green, red; 2 bits for alpha; in 32-bit word - kRGB_101010x_SkColorType, //!< pixel with 10 bits each for red, green, blue; in 32-bit word - kBGR_101010x_SkColorType, //!< pixel with 10 bits each for blue, green, red; in 32-bit word - kBGR_101010x_XR_SkColorType, //!< pixel with 10 bits each for blue, green, red; in 32-bit word, extended range - kBGRA_10101010_XR_SkColorType, //!< pixel with 10 bits each for blue, green, red, alpha; in 64-bit word, extended range - kRGBA_10x6_SkColorType, //!< pixel with 10 used bits (most significant) followed by 6 unused - // bits for red, green, blue, alpha; in 64-bit word - kGray_8_SkColorType, //!< pixel with grayscale level in 8-bit byte - kRGBA_F16Norm_SkColorType, //!< pixel with half floats in [0,1] for red, green, blue, alpha; - // in 64-bit word - kRGBA_F16_SkColorType, //!< pixel with half floats for red, green, blue, alpha; - // in 64-bit word - kRGB_F16F16F16x_SkColorType, //!< pixel with half floats for red, green, blue; in 64-bit word - kRGBA_F32_SkColorType, //!< pixel using C float for red, green, blue, alpha; in 128-bit word + // Unknown or unrepresentable as an SkColorType. + kUnknown_SkColorType, + // Single channel data (8-bit) interpreted as an alpha value. RGB are 0. + // Bits: [A:7..0] + kAlpha_8_SkColorType, + // Three channel BGR data (5 bits red, 6 bits green, 5 bits blue) packed into a LE 16-bit word. + // NOTE: The name of this enum value does not match the standard convention for SkColorType. + // Bits: [R:15..11 G:10..5 B:4..0] + kRGB_565_SkColorType, + // Four channel ABGR data (4 bits per channel) packed into a LE 16-bit word. + // NOTE: The name of this enum value does not match the standard convention for SkColorType. + // Bits: [R:15..12 G:11..8 B:7..4 A:3..0] + kARGB_4444_SkColorType, + // Four channel RGBA data (8 bits per channel) packed into a LE 32-bit word. + // Bits: [A:31..24 B:23..16 G:15..8 R:7..0] + kRGBA_8888_SkColorType, + // Three channel RGB data (8 bits per channel) packed into a LE 32-bit word. The remaining bits + // are ignored and alpha is forced to opaque. + // Bits: [x:31..24 B:23..16 G:15..8 R:7..0] + kRGB_888x_SkColorType, + // Four channel BGRA data (8 bits per channel) packed into a LE 32-bit word. R and B are swapped + // relative to kRGBA_8888. + // Bits: [A:31..24 R:23..16 G:15..8 B:7..0] + kBGRA_8888_SkColorType, + // Four channel RGBA data (10 bits per color, 2 bits for alpha) packed into a LE 32-bit word. + // Bits: [A:31..30 B:29..20 G:19..10 R:9..0] + kRGBA_1010102_SkColorType, + // Four channel BGRA data (10 bits per color, 2 bits for alpha) packed into a LE 32-bit word. + // R and B are swapped relative to kRGBA_1010102. + // Bits: [A:31..30 R:29..20 G:19..10 B:9..0] + kBGRA_1010102_SkColorType, + // Three channel RGB data (10 bits per channel) packed into a LE 32-bit word. The remaining bits + // are ignored and alpha is forced to opaque. + // Bits: [x:31..30 B:29..20 G:19..10 R:9..0] + kRGB_101010x_SkColorType, + // Three channel BGR data (10 bits per channel) packed into a LE 32-bit word. The remaining bits + // are ignored and alpha is forced to opaque. R and B are swapped relative to kRGB_101010x. + // Bits: [x:31..30 R:29..20 G:19..10 B:9..0] + kBGR_101010x_SkColorType, + // Three channel BGR data (10 bits per channel) packed into a LE 32-bit word. The remaining bits + // are ignored and alpha is forced to opaque. Instead of normalizing [0, 1023] to [0.0, 1.0] the + // color channels map to an extended range of [-0.752941, 1.25098], compatible with + // MTLPixelFormatBGR10_XR. + // Bits: [x:31..30 R:29..20 G:19..10 B:9..0] + kBGR_101010x_XR_SkColorType, + // Four channel BGRA data (10 bits per channel) packed into a LE 64-bit word. Each channel is + // preceded by 6 bits of padding. Instead of normalizing [0, 1023] to [0.0, 1.0] the color and + // alpha channels map to an extended range of [-0.752941, 1.25098], compatible with + // MTLPixelFormatBGRA10_XR. + // Bits: [A:63..54 x:53..48 R:47..38 x:37..32 G:31..22 x:21..16 B:15..6 x:5..0] + kBGRA_10101010_XR_SkColorType, + // Four channel RGBA data (10 bits per channel) packed into a LE 64-bit word. Each channel is + // preceded by 6 bits of padding. + // Bits: [A:63..54 x:53..48 B:47..38 x:37..32 G:31..22 x:21..16 R:15..6 x:5..0] + kRGBA_10x6_SkColorType, + // Single channel data (8-bit) interpreted as a grayscale value (e.g. replicated to RGB). + // Bits: [G:7..0] + kGray_8_SkColorType, + // Four channel RGBA data (16-bit half-float per channel) packed into a LE 64-bit word. Values + // are assumed to be in [0.0,1.0] range, unlike kRGBA_F16. + // Bits: [A:63..48 B:47..32 G:31..16 R:15..0] + kRGBA_F16Norm_SkColorType, + // Four channel RGBA data (16-bit half-float per channel) packed into a LE 64-bit word. + // This has extended range compared to kRGBA_F16Norm. + // Bits: [A:63..48 B:47..32 G:31..16 R:15..0] + kRGBA_F16_SkColorType, + // Three channel RGB data (16-bit half-float per channel) packed into a LE 64-bit word. The last + // 16 bits are ignored and alpha is forced to opaque. + // Bits: [x:63..48 B:47..32 G:31..16 R:15..0] + kRGB_F16F16F16x_SkColorType, + // Four channel RGBA data (32-bit float per channel) packed into a LE 128-bit word. + // Bits: [A:127..96 B:95..64 G:63..32 R:31..0] + kRGBA_F32_SkColorType, - // The following 6 colortypes are just for reading from - not for rendering to - kR8G8_unorm_SkColorType, //!< pixel with a uint8_t for red and green - - kA16_float_SkColorType, //!< pixel with a half float for alpha - kR16G16_float_SkColorType, //!< pixel with a half float for red and green - - kA16_unorm_SkColorType, //!< pixel with a little endian uint16_t for alpha - kR16G16_unorm_SkColorType, //!< pixel with a little endian uint16_t for red and green - kR16G16B16A16_unorm_SkColorType, //!< pixel with a little endian uint16_t for red, green, blue - // and alpha + // The following 8 colortypes are just for reading from - not for rendering to + // Two channel RG data (8 bits per channel). Blue is forced to 0, alpha is forced to opaque. + // Bits: [G:15..8 R:7..0] + kR8G8_unorm_SkColorType, + // Single channel data (16-bit half-float) interpreted as alpha. RGB are 0. + // Bits: [A:15..0] + kA16_float_SkColorType, + // Two channel RG data (16-bit half-float per channel) packed into a LE 32-bit word. + // Blue is forced to 0, alpha is forced to opaque. + // Bits: [G:31..16 R:15..0] + kR16G16_float_SkColorType, + // Single channel data (16 bits) interpreted as alpha. RGB are 0. + // Bits: [A:15..0] + kA16_unorm_SkColorType, + // Two channel RG data (16 bits per channel) packed into a LE 32-bit word. B is forced to 0, + // alpha is forced to opaque. + // Bits: [G:31..16 R:15..0] + kR16G16_unorm_SkColorType, + // Four channel RGBA data (16 bits per channel) packed into a LE 64-bit word. + // Bits: [A:63..48 B:47..32 G:31..16 R:15..0] + kR16G16B16A16_unorm_SkColorType, + // Four channel RGBA data (8 bits per channel) packed into a LE 32-bit word. The RGB values are + // assumed to be encoded with the sRGB transfer function, which can be decoded automatically + // by GPU hardware with certain texture formats. + // Bits: [A:31..24 B:23..16 G:15..8 R:7..0] kSRGBA_8888_SkColorType, + // Single channel data (8 bits) interpreted as red. G and B are forced to 0, alpha is forced to + // opaque. + // Bits: [R:7..0] kR8_unorm_SkColorType, kLastEnum_SkColorType = kR8_unorm_SkColorType, //!< last valid value diff --git a/gfx/skia/skia/include/core/SkContourMeasure.h b/gfx/skia/skia/include/core/SkContourMeasure.h index c542c6218c0d..94dbfaa725d2 100644 --- a/gfx/skia/skia/include/core/SkContourMeasure.h +++ b/gfx/skia/skia/include/core/SkContourMeasure.h @@ -91,13 +91,13 @@ public: return *this; } - bool operator==(const ForwardVerbIterator& other) { + bool operator==(const ForwardVerbIterator& other) const { SkASSERT(fSegments.data() != other.fSegments.data() || fSegments.size() == other.fSegments.size()); return fSegments.data() == other.fSegments.data(); } - bool operator!=(const ForwardVerbIterator& other) { + bool operator!=(const ForwardVerbIterator& other) const { return !((*this) == other); } diff --git a/gfx/skia/skia/include/core/SkFontScanner.h b/gfx/skia/skia/include/core/SkFontScanner.h index 5ed3308d9f16..0308f32113f9 100644 --- a/gfx/skia/skia/include/core/SkFontScanner.h +++ b/gfx/skia/skia/include/core/SkFontScanner.h @@ -9,6 +9,7 @@ #define SKFONTSCANNER_H_ #include "include/core/SkFontArguments.h" +#include "include/core/SkFontParameters.h" #include "include/core/SkRefCnt.h" #include "include/core/SkTypes.h" #include "include/private/base/SkFixed.h" @@ -22,13 +23,8 @@ class SkTypeface; class SkFontScanner : public SkNoncopyable { public: virtual ~SkFontScanner() = default; - struct AxisDefinition { - SkFourByteTag fTag; - SkScalar fMinimum; - SkScalar fDefault; - SkScalar fMaximum; - }; - typedef skia_private::STArray<4, AxisDefinition, true> AxisDefinitions; + using AxisDefinitions = skia_private::STArray<4, SkFontParameters::Variation::Axis, true>; + using VariationPosition = skia_private::STArray<4, SkFontArguments::VariationPosition::Coordinate, true>; virtual bool scanFile(SkStreamAsset* stream, int* numFaces) const = 0; virtual bool scanFace(SkStreamAsset* stream, int faceIndex, int* numInstances) const = 0; @@ -39,7 +35,8 @@ public: SkString* name, SkFontStyle* style, bool* isFixedPitch, - AxisDefinitions* axes) const = 0; + AxisDefinitions* axes, + VariationPosition* position) const = 0; virtual sk_sp MakeFromStream(std::unique_ptr stream, const SkFontArguments& args) const = 0; virtual SkFourByteTag getFactoryId() const = 0; diff --git a/gfx/skia/skia/include/core/SkFontStyle.h b/gfx/skia/skia/include/core/SkFontStyle.h index be46b53bb285..078bce9f7a75 100644 --- a/gfx/skia/skia/include/core/SkFontStyle.h +++ b/gfx/skia/skia/include/core/SkFontStyle.h @@ -41,7 +41,7 @@ public: kUltraExpanded_Width = 9, }; - enum Slant { + enum Slant : uint8_t { kUpright_Slant, kItalic_Slant, kOblique_Slant, diff --git a/gfx/skia/skia/include/core/SkMaskFilter.h b/gfx/skia/skia/include/core/SkMaskFilter.h index 9d03e98c0c5f..2dac989e44d9 100644 --- a/gfx/skia/skia/include/core/SkMaskFilter.h +++ b/gfx/skia/skia/include/core/SkMaskFilter.h @@ -17,7 +17,6 @@ enum SkBlurStyle : int; struct SkDeserialProcs; -struct SkRect; /** \class SkMaskFilter @@ -35,13 +34,6 @@ public: 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); diff --git a/gfx/skia/skia/include/core/SkMilestone.h b/gfx/skia/skia/include/core/SkMilestone.h index 4a18d44f1d1c..ae5bae1f0513 100644 --- a/gfx/skia/skia/include/core/SkMilestone.h +++ b/gfx/skia/skia/include/core/SkMilestone.h @@ -5,5 +5,5 @@ * found in the LICENSE file. */ #ifndef SK_MILESTONE -#define SK_MILESTONE 132 +#define SK_MILESTONE 136 #endif diff --git a/gfx/skia/skia/include/core/SkPaint.h b/gfx/skia/skia/include/core/SkPaint.h index 98451ba7a1e5..a97ba0d05bce 100644 --- a/gfx/skia/skia/include/core/SkPaint.h +++ b/gfx/skia/skia/include/core/SkPaint.h @@ -317,15 +317,18 @@ public: */ SkScalar getStrokeMiter() const { return fMiterLimit; } - /** Sets the limit at which a sharp corner is drawn beveled. - Valid values are zero and greater. - Has no effect if miter is less than zero. + /** When stroking a small joinAngle with miter, the miterLength may be very long. + When miterLength > maxMiterLength (or joinAngle < minJoinAngle) the join will become bevel. + miterLimit = maxMiterLength / strokeWidth or miterLimit = 1 / sin(minJoinAngle / 2). - @param miter zero and greater miter limit + This call has no effect if the miterLimit passed is less than zero. + Values less than one will be treated as bevel. + + @param miterLimit zero and greater miter limit example: https://fiddle.skia.org/c/@Paint_setStrokeMiter */ - void setStrokeMiter(SkScalar miter); + void setStrokeMiter(SkScalar miterLimit); /** \enum SkPaint::Cap Cap draws at the beginning and end of an open path contour. diff --git a/gfx/skia/skia/include/core/SkPath.h b/gfx/skia/skia/include/core/SkPath.h index 353ed974cd37..0e941c6fe95c 100644 --- a/gfx/skia/skia/include/core/SkPath.h +++ b/gfx/skia/skia/include/core/SkPath.h @@ -1909,7 +1909,7 @@ private: void shrinkToFit(); // Creates a new Path after the supplied arguments have been validated by - // sk_path_analyze_verbs(). + // SkPathPriv::AnalyzeVerbs(). static SkPath MakeInternal(const SkPathVerbAnalysis& analsis, const SkPoint points[], const uint8_t verbs[], diff --git a/gfx/skia/skia/include/core/SkRRect.h b/gfx/skia/skia/include/core/SkRRect.h index 348a4417946d..48bb64bb419a 100644 --- a/gfx/skia/skia/include/core/SkRRect.h +++ b/gfx/skia/skia/include/core/SkRRect.h @@ -13,6 +13,7 @@ #include "include/core/SkScalar.h" #include "include/core/SkSpan.h" #include "include/core/SkTypes.h" +#include "include/private/base/SkMacros.h" #include #include @@ -33,6 +34,7 @@ class SkString; If either axis radii is zero or less: radii are stored as zero; corner is square. If corner curves overlap, radii are proportionally reduced to fit within bounds. */ +SK_BEGIN_REQUIRE_DENSE class SK_API SkRRect { public: @@ -511,11 +513,11 @@ private: SkVector fRadii[4] = {{0, 0}, {0, 0}, {0,0}, {0,0}}; // use an explicitly sized type so we're sure the class is dense (no uninitialized bytes) int32_t fType = kEmpty_Type; - // TODO: add padding so we can use memcpy for flattening and not copy uninitialized data // to access fRadii directly friend class SkPath; friend class SkRRectPriv; }; +SK_END_REQUIRE_DENSE #endif diff --git a/gfx/skia/skia/include/core/SkShader.h b/gfx/skia/skia/include/core/SkShader.h index b650a03d7b04..34b58d98addf 100644 --- a/gfx/skia/skia/include/core/SkShader.h +++ b/gfx/skia/skia/include/core/SkShader.h @@ -25,9 +25,9 @@ struct SkSamplingOptions; /** \class SkShader * - * Shaders specify the source color(s) for what is being drawn. If a paint - * has no shader, then the paint's color is used. If the paint has a - * shader, then the shader's color(s) are use instead, but they are + * Shaders specify the premultiplied source color(s) for what is being drawn. + * If a paint has no shader, then the paint's color is used. If the paint has a + * shader, then the shader's color(s) are used instead, but they are * modulated by the paint's alpha. This makes it easy to create a shader * once (e.g. bitmap tiling or gradient) and then change its transparency * w/o having to modify the original shader... only the paint's alpha needs diff --git a/gfx/skia/skia/include/core/SkStream.h b/gfx/skia/skia/include/core/SkStream.h index 208bfe2903ca..d12a26807f40 100644 --- a/gfx/skia/skia/include/core/SkStream.h +++ b/gfx/skia/skia/include/core/SkStream.h @@ -79,10 +79,12 @@ public: [[nodiscard]] bool readS8(int8_t*); [[nodiscard]] bool readS16(int16_t*); [[nodiscard]] bool readS32(int32_t*); + [[nodiscard]] bool readS64(int64_t*); - [[nodiscard]] bool readU8(uint8_t* i) { return this->readS8((int8_t*)i); } + [[nodiscard]] bool readU8(uint8_t* i) { return this->readS8((int8_t*)i); } [[nodiscard]] bool readU16(uint16_t* i) { return this->readS16((int16_t*)i); } [[nodiscard]] bool readU32(uint32_t* i) { return this->readS32((int32_t*)i); } + [[nodiscard]] bool readU64(uint64_t* i) { return this->readS64((int64_t*)i); } [[nodiscard]] bool readBool(bool* b) { uint8_t i; @@ -240,8 +242,11 @@ public: uint16_t v = SkToU16(value); return this->write(&v, 2); } - bool write32(uint32_t v) { - return this->write(&v, 4); + bool write32(uint32_t value) { + return this->write(&value, 4); + } + bool write64(uint64_t value) { + return this->write(&value, 8); } bool writeText(const char text[]) { diff --git a/gfx/skia/skia/include/core/SkString.h b/gfx/skia/skia/include/core/SkString.h index 6ff1f8b0f10d..a67fc40d2e87 100644 --- a/gfx/skia/skia/include/core/SkString.h +++ b/gfx/skia/skia/include/core/SkString.h @@ -28,13 +28,13 @@ static inline bool SkStrStartsWith(const char string[], const char prefixStr[]) SkASSERT(prefixStr); return !strncmp(string, prefixStr, strlen(prefixStr)); } -static inline bool SkStrStartsWith(const char string[], const char prefixChar) { +static inline bool SkStrStartsWith(const char string[], char prefixChar) { SkASSERT(string); return (prefixChar == *string); } bool SkStrEndsWith(const char string[], const char suffixStr[]); -bool SkStrEndsWith(const char string[], const char suffixChar); +bool SkStrEndsWith(const char string[], char suffixChar); int SkStrStartsWithOneOf(const char string[], const char prefixes[]); @@ -44,7 +44,7 @@ static inline int SkStrFind(const char string[], const char substring[]) { return SkToInt(first - &string[0]); } -static inline int SkStrFindLastOf(const char string[], const char subchar) { +static inline int SkStrFindLastOf(const char string[], char subchar) { const char* last = strrchr(string, subchar); if (nullptr == last) return -1; return SkToInt(last - &string[0]); @@ -55,7 +55,7 @@ static inline bool SkStrContains(const char string[], const char substring[]) { SkASSERT(substring); return (-1 != SkStrFind(string, substring)); } -static inline bool SkStrContains(const char string[], const char subchar) { +static inline bool SkStrContains(const char string[], char subchar) { SkASSERT(string); char tmp[2]; tmp[0] = subchar; @@ -142,25 +142,25 @@ public: bool startsWith(const char prefixStr[]) const { return SkStrStartsWith(fRec->data(), prefixStr); } - bool startsWith(const char prefixChar) const { + bool startsWith(char prefixChar) const { return SkStrStartsWith(fRec->data(), prefixChar); } bool endsWith(const char suffixStr[]) const { return SkStrEndsWith(fRec->data(), suffixStr); } - bool endsWith(const char suffixChar) const { + bool endsWith(char suffixChar) const { return SkStrEndsWith(fRec->data(), suffixChar); } bool contains(const char substring[]) const { return SkStrContains(fRec->data(), substring); } - bool contains(const char subchar) const { + bool contains(char subchar) const { return SkStrContains(fRec->data(), subchar); } int find(const char substring[]) const { return SkStrFind(fRec->data(), substring); } - int findLastOf(const char subchar) const { + int findLastOf(char subchar) const { return SkStrFindLastOf(fRec->data(), subchar); } diff --git a/gfx/skia/skia/include/core/SkSurface.h b/gfx/skia/skia/include/core/SkSurface.h index 2e2460228fcb..8763eb8be1e5 100644 --- a/gfx/skia/skia/include/core/SkSurface.h +++ b/gfx/skia/skia/include/core/SkSurface.h @@ -330,6 +330,20 @@ public: */ sk_sp makeImageSnapshot(const SkIRect& bounds); + /** Returns an SkImage capturing the current SkSurface contents. However, the contents of the + SkImage are only valid as long as no other writes to the SkSurface occur. If writes to the + original SkSurface happen then contents of the SkImage are undefined. However, continued use + of the SkImage should not cause crashes or similar fatal behavior. + + This API is useful for cases where the client either immediately destroys the SkSurface + after the SkImage is created or knows they will destroy the SkImage before writing to the + SkSurface again. + + This API can be more performant than makeImageSnapshot as it never does an internal copy + of the data assuming the user frees either the SkImage or SkSurface as described above. + */ + sk_sp makeTemporaryImage(); + /** Draws SkSurface contents to canvas, with its top-left corner at (x, y). If SkPaint paint is not nullptr, apply SkColorFilter, alpha, SkImageFilter, and SkBlendMode. diff --git a/gfx/skia/skia/include/core/SkTextBlob.h b/gfx/skia/skia/include/core/SkTextBlob.h index 23e2d9bf2a60..3dc5975a1df0 100644 --- a/gfx/skia/skia/include/core/SkTextBlob.h +++ b/gfx/skia/skia/include/core/SkTextBlob.h @@ -209,13 +209,13 @@ public: class SK_API Iter { public: struct Run { - SkTypeface* fTypeface; - int fGlyphCount; - const uint16_t* fGlyphIndices; + SkTypeface* fTypeface; + int fGlyphCount; + const SkGlyphID* fGlyphIndices; #ifdef SK_UNTIL_CRBUG_1187654_IS_FIXED const uint32_t* fClusterIndex_forTest; - int fUtf8Size_forTest; - const char* fUtf8_forTest; + int fUtf8Size_forTest; + const char* fUtf8_forTest; #endif }; @@ -229,10 +229,10 @@ public: // Experimental, DO NO USE, will change/go-away struct ExperimentalRun { - SkFont font; - int count; - const uint16_t* glyphs; - const SkPoint* positions; + SkFont font; + int count; + const SkGlyphID* glyphs; + const SkPoint* positions; }; bool experimentalNext(ExperimentalRun*); diff --git a/gfx/skia/skia/include/core/SkTypeface.h b/gfx/skia/skia/include/core/SkTypeface.h index 097cf8f901fb..40d012955f9e 100644 --- a/gfx/skia/skia/include/core/SkTypeface.h +++ b/gfx/skia/skia/include/core/SkTypeface.h @@ -53,20 +53,18 @@ typedef uint32_t SkFontTableTag; class SK_API SkTypeface : public SkWeakRefCnt { public: /** Returns the typeface's intrinsic style attributes. */ - SkFontStyle fontStyle() const { - return fStyle; - } + SkFontStyle fontStyle() const; /** Returns true if style() has the kBold bit set. */ - bool isBold() const { return fStyle.weight() >= SkFontStyle::kSemiBold_Weight; } + bool isBold() const; /** Returns true if style() has the kItalic bit set. */ - bool isItalic() const { return fStyle.slant() != SkFontStyle::kUpright_Slant; } + bool isItalic() const; /** Returns true if the typeface claims to be fixed-pitch. * This is a style bit, advance widths may vary even if this returns true. */ - bool isFixedPitch() const { return fIsFixedPitch; } + bool isFixedPitch() const; /** Copy into 'coordinates' (allocated by the caller) the design variation coordinates. * @@ -287,6 +285,20 @@ public: */ bool getPostScriptName(SkString* name) const; + /** + * If the primary resource backing this typeface has a name (like a file + * path or URL) representable by unicode code points, the `resourceName` + * will be set. The primary purpose is as a user facing indication about + * where the data was obtained (which font file was used). + * + * Returns the number of resources backing this typeface. + * + * For local font collections resource name will often be a file path. The + * file path may or may not exist. If it does exist, using it to create an + * SkTypeface may or may not create a similar SkTypeface to this one. + */ + int getResourceName(SkString* resourceName) const; + /** * Return a stream for the contents of the font data, or NULL on failure. * If ttcIndex is not null, it is set to the TrueTypeCollection index @@ -355,13 +367,15 @@ protected: /** Sets the font style. If used, must be called in the constructor. */ void setFontStyle(SkFontStyle style) { fStyle = style; } + virtual SkFontStyle onGetFontStyle() const; // TODO: = 0; + + virtual bool onGetFixedPitch() const; // TODO: = 0; + // Must return a valid scaler context. It can not return nullptr. - virtual std::unique_ptr onCreateScalerContext(const SkScalerContextEffects&, - const SkDescriptor*) const = 0; + virtual std::unique_ptr onCreateScalerContext( + const SkScalerContextEffects&, const SkDescriptor*) const = 0; virtual std::unique_ptr onCreateScalerContextAsProxyTypeface - (const SkScalerContextEffects&, - const SkDescriptor*, - sk_sp) const; + (const SkScalerContextEffects&, const SkDescriptor*, SkTypeface* proxyTypeface) const; virtual void onFilterRec(SkScalerContextRec*) const = 0; friend class SkScalerContext; // onFilterRec @@ -404,6 +418,7 @@ protected: */ virtual void onGetFamilyName(SkString* familyName) const = 0; virtual bool onGetPostScriptName(SkString*) const = 0; + virtual int onGetResourceName(SkString* resourceName) const; // TODO: = 0; /** Returns an iterator over the family names in the font. */ virtual LocalizedStrings* onCreateFamilyNameIterator() const = 0; @@ -430,9 +445,9 @@ private: std::unique_ptr getAdvancedMetrics() const; friend class SkRandomTypeface; // getAdvancedMetrics friend class SkPDFFont; // getAdvancedMetrics - friend class SkTypeface_fontconfig; - + friend class SkTypeface_proxy; friend class SkFontPriv; // getGlyphToUnicodeMap + friend void TestSkTypefaceGlyphToUnicodeMap(SkTypeface&, SkUnichar*); private: SkTypefaceID fUniqueID; diff --git a/gfx/skia/skia/include/core/SkTypes.h b/gfx/skia/skia/include/core/SkTypes.h index 60d1a0d97957..7680470bdd75 100644 --- a/gfx/skia/skia/include/core/SkTypes.h +++ b/gfx/skia/skia/include/core/SkTypes.h @@ -97,6 +97,7 @@ defined(SK_HISTOGRAM_BOOLEAN) || \ defined(SK_HISTOGRAM_EXACT_LINEAR) || \ defined(SK_HISTOGRAM_MEMORY_KB) || \ + defined(SK_HISTOGRAM_CUSTOM_COUNTS)|| \ defined(SK_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES) # define SK_HISTOGRAMS_ENABLED 1 #else @@ -119,6 +120,10 @@ # define SK_HISTOGRAM_MEMORY_KB(name, sample) #endif +#ifndef SK_HISTOGRAM_CUSTOM_COUNTS +# define SK_HISTOGRAM_CUSTOM_COUNTS(name, sample, countMin, countMax, bucketCount) +#endif + #ifndef SK_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES # define SK_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(name, sampleUSec, minUSec, maxUSec, bucketCount) #endif diff --git a/gfx/skia/skia/include/docs/SkPDFDocument.h b/gfx/skia/skia/include/docs/SkPDFDocument.h index 6757966ff4b9..ff48f4116889 100644 --- a/gfx/skia/skia/include/docs/SkPDFDocument.h +++ b/gfx/skia/skia/include/docs/SkPDFDocument.h @@ -16,9 +16,12 @@ #include class SkCanvas; +class SkCodec; +class SkData; class SkExecutor; class SkPDFArray; class SkPDFStructTree; +class SkPixmap; class SkWStream; #define SKPDF_STRING(X) SKPDF_STRING_IMPL(X) @@ -80,6 +83,9 @@ struct DateTime { void toISO8601(SkString* dst) const; }; +using DecodeJpegCallback = std::unique_ptr (*)(sk_sp); +using EncodeJpegCallback = bool (*)(SkWStream* dst, const SkPixmap& src, int quality); + /** Optional metadata to be passed into the PDF factory function. */ struct Metadata { @@ -157,6 +163,7 @@ struct Metadata { enum class Outline : int { None = 0, StructureElementHeaders = 1, + StructureElements = 2, } fOutline = Outline::None; /** Executor to handle threaded work within PDF Backend. If this is nullptr, @@ -186,14 +193,50 @@ struct Metadata { enum Subsetter { kHarfbuzz_Subsetter, } fSubsetter = kHarfbuzz_Subsetter; + + /** Clients can provide a way to decode jpeg. To use Skia's JPEG decoder, pass in + SkJpegDecoder::Decode. If not supplied, all images will need to be re-encoded + as jpegs or deflated images before embedding. If supplied, Skia may be able to + skip the re-encoding step. + Skia's JPEG decoder can be used here. + */ + SkPDF::DecodeJpegCallback jpegDecoder = nullptr; + + /** Clients can provide a way to encode jpeg. If not supplied, images will be embedded + as a deflated image, potentially making them much larger. If clients provide + their own implementation, JPEGs should be encoded to RGB (not YUV) otherwise they + will have the wrong surrounding metadata provided by Skia. + Skia's JPEG encoder can be used here. + */ + SkPDF::EncodeJpegCallback jpegEncoder = nullptr; + + // Skia's PDF support depends on having both a jpeg encoder and decoder for writing + // compact PDFs. It will technically work, but produce larger than optimal PDFs + // if either the decoder or encoder are left as nullptr. If clients will be creating + // PDFs that don't use images or otherwise want to incur this cost (with the upside + // of not having a jpeg library), they should set this to true to avoid an internal + // assert from firing. + bool allowNoJpegs = false; }; +namespace NodeID { +static const constexpr int Nothing = 0; +static const constexpr int OtherArtifact = -1; +static const constexpr int PaginationArtifact = -2; +static const constexpr int PaginationHeaderArtifact = -3; +static const constexpr int PaginationFooterArtifact = -4; +static const constexpr int PaginationWatermarkArtifact = -5; +static const constexpr int LayoutArtifact = -6; +static const constexpr int PageArtifact = -7; +static const constexpr int BackgroundArtifact = -8; +} // namespace NodeID + /** Associate a node ID with subsequent drawing commands in an SkCanvas. The same node ID can appear in a StructureElementNode in order to associate a document's structure element tree with its content. - A node ID of zero indicates no node ID. + A node ID of zero indicates no node ID. Negative node IDs are reserved. @param canvas The canvas used to draw to the PDF. @param nodeId The node ID for subsequent drawing commands. @@ -207,15 +250,17 @@ SK_API void SetNodeId(SkCanvas* dst, int nodeID); @param stream A PDF document will be written to this stream. The document may write to the stream at anytime during its lifetime, until either close() is called or the document is deleted. - @param metadata a PDFmetadata object. Any fields may be left empty. + @param metadata a PDFmetadata object. Some fields may be left empty. @returns NULL if there is an error, otherwise a newly created PDF-backed SkDocument. */ SK_API sk_sp MakeDocument(SkWStream* stream, const Metadata& metadata); +#if !defined(SK_DISABLE_LEGACY_PDF_JPEG) static inline sk_sp MakeDocument(SkWStream* stream) { return MakeDocument(stream, Metadata()); } +#endif } // namespace SkPDF diff --git a/gfx/skia/skia/include/docs/SkPDFJpegHelpers.h b/gfx/skia/skia/include/docs/SkPDFJpegHelpers.h new file mode 100644 index 000000000000..423de8b387a7 --- /dev/null +++ b/gfx/skia/skia/include/docs/SkPDFJpegHelpers.h @@ -0,0 +1,41 @@ +/* + * Copyright 2024 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SkPDFJPEGHelpers_DEFINED +#define SkPDFJPEGHelpers_DEFINED + +#include "include/codec/SkJpegDecoder.h" +#include "include/core/SkData.h" +#include "include/core/SkRefCnt.h" +#include "include/docs/SkPDFDocument.h" +#include "include/encode/SkJpegEncoder.h" + +class SkPixmap; +class SkWStream; + +#include + +namespace SkPDF::JPEG { +inline std::unique_ptr Decode(sk_sp data) { + return SkJpegDecoder::Decode(data, nullptr, nullptr); +} + +inline bool Encode(SkWStream* dst, const SkPixmap& src, int quality) { + SkJpegEncoder::Options jOpts; + jOpts.fQuality = quality; + return SkJpegEncoder::Encode(dst, src, jOpts); +} + +inline SkPDF::Metadata MetadataWithCallbacks() { + SkPDF::Metadata m; + m.jpegDecoder = SkPDF::JPEG::Decode; + m.jpegEncoder = SkPDF::JPEG::Encode; + return m; +} + +} // namespace SkPDF::JPEG + +#endif diff --git a/gfx/skia/skia/include/effects/SkGradientShader.h b/gfx/skia/skia/include/effects/SkGradientShader.h index d725c2d8b12b..51aa291cc324 100644 --- a/gfx/skia/skia/include/effects/SkGradientShader.h +++ b/gfx/skia/skia/include/effects/SkGradientShader.h @@ -94,7 +94,12 @@ public: kHSL, kHWB, - kLastColorSpace = kHWB, + kDisplayP3, + kRec2020, + kProphotoRGB, + kA98RGB, + + kLastColorSpace = kA98RGB, }; static constexpr int kColorSpaceCount = static_cast(ColorSpace::kLastColorSpace) + 1; diff --git a/gfx/skia/skia/include/effects/SkRuntimeEffect.h b/gfx/skia/skia/include/effects/SkRuntimeEffect.h index 11a2b283b3a6..0bee8a7f6bbf 100644 --- a/gfx/skia/skia/include/effects/SkRuntimeEffect.h +++ b/gfx/skia/skia/include/effects/SkRuntimeEffect.h @@ -126,6 +126,8 @@ public: // don't run the inliner directly, but they still get an inlining pass once they are // painted.) bool forceUnoptimized = false; + // When possible this name will be used to identify the created runtime effect. + std::string_view fName; private: friend class SkRuntimeEffect; @@ -170,6 +172,7 @@ public: // Shader SkSL requires an entry point that looks like: // vec4 main(vec2 inCoords) { ... } + // The color that is returned should be premultiplied. static Result MakeForShader(SkString sksl, const Options&); static Result MakeForShader(SkString sksl) { return MakeForShader(std::move(sksl), Options{}); @@ -316,7 +319,9 @@ private: friend class SkRuntimeEffectPriv; uint32_t fHash; - uint32_t fStableKey; + // When not 0, this field holds a StableKey value or a user-defined stable key + uint32_t fStableKey = 0; + SkString fName; std::unique_ptr fBaseProgram; std::unique_ptr fRPProgram; diff --git a/gfx/skia/skia/include/effects/SkTableMaskFilter.h b/gfx/skia/skia/include/effects/SkTableMaskFilter.h index 937037afa80d..2cc439c1c518 100644 --- a/gfx/skia/skia/include/effects/SkTableMaskFilter.h +++ b/gfx/skia/skia/include/effects/SkTableMaskFilter.h @@ -38,6 +38,10 @@ public: static SkMaskFilter* CreateClip(uint8_t min, uint8_t max); SkTableMaskFilter() = delete; + +private: + static void RegisterFlattenables(); + friend class SkFlattenable; }; #endif diff --git a/gfx/skia/skia/include/encode/SkPngEncoder.h b/gfx/skia/skia/include/encode/SkPngEncoder.h index b26befa323ef..909f5e773f66 100644 --- a/gfx/skia/skia/include/encode/SkPngEncoder.h +++ b/gfx/skia/skia/include/encode/SkPngEncoder.h @@ -23,6 +23,7 @@ class SkImage; class SkPixmap; class SkWStream; struct skcms_ICCProfile; +struct SkGainmapInfo; namespace SkPngEncoder { @@ -80,6 +81,19 @@ struct Options { */ const skcms_ICCProfile* fICCProfile = nullptr; const char* fICCProfileDescription = nullptr; + + /** + * If non-null, then a gainmap and its metadata will be encoded as png chunks. + * The gainmap will be encoded in a gmAP chunk as a full PNG container. The + * gainmap info will be encoded in a gdAT chunk inside of the gmAP chunk. + * This effectively is Option B proposed in this discussion for adding gainmaps + * into PNG: https://github.com/w3c/png/issues/380#issuecomment-2325163149. + * + * Note that if fGainmapInfo is null, then fGainmap will fail to encode, as the + * gainmap metadata is required to correctly interpret the encoded gainmap. + */ + const SkPixmap* fGainmap = nullptr; + const SkGainmapInfo* fGainmapInfo = nullptr; }; /** diff --git a/gfx/skia/skia/include/gpu/GpuTypes.h b/gfx/skia/skia/include/gpu/GpuTypes.h index 9fd7e7bc6ff6..5d311da8d1a8 100644 --- a/gfx/skia/skia/include/gpu/GpuTypes.h +++ b/gfx/skia/skia/include/gpu/GpuTypes.h @@ -79,6 +79,15 @@ enum class Origin : unsigned { kBottomLeft, }; +enum class GpuStatsFlags : uint32_t { + kNone = 0b00, + kElapsedTime = 0b01, +}; + +struct GpuStats { + uint64_t elapsedTime = 0; +}; + } // namespace skgpu diff --git a/gfx/skia/skia/include/gpu/ganesh/GrContextOptions.h b/gfx/skia/skia/include/gpu/ganesh/GrContextOptions.h index 1c9a845b8d8c..7da06e5645c5 100644 --- a/gfx/skia/skia/include/gpu/ganesh/GrContextOptions.h +++ b/gfx/skia/skia/include/gpu/ganesh/GrContextOptions.h @@ -75,8 +75,55 @@ struct SK_API GrContextOptions { GrContextOptions() {} - // Suppress prints for the GrContext. - bool fSuppressPrints = false; + /** + * If Skia is creating a default VMA allocator for the Vulkan backend this value will be used + * for the preferredLargeHeapBlockSize. If the value is not set, then Skia will use an + * inernally defined default size. + * + * However, it is highly discouraged to have Skia make a default allocator (and support for + * doing so will be removed soon, b/321962001). Instead clients should create their own + * allocator to pass into Skia where they can fine tune this value themeselves. + */ + std::optional fVulkanVMALargeHeapBlockSize; + + /** + * Optional callback that can be passed into the GrDirectContext which will be called when the + * GrDirectContext is about to be destroyed. When this call is made, it will be safe for the + * client to delete the GPU backend context that is backing the GrDirectContext. The + * GrDirectContextDestroyedContext will be passed back to the client in the callback. + */ + GrDirectContextDestroyedContext fContextDeleteContext = nullptr; + GrDirectContextDestroyedProc fContextDeleteProc = nullptr; + + /** + * Executor to handle threaded work within Ganesh. If this is nullptr, then all work will be + * done serially on the main thread. To have worker threads assist with various tasks, set this + * to a valid SkExecutor instance. Currently, used for software path rendering, but may be used + * for other tasks. + */ + SkExecutor* fExecutor = nullptr; + + /** + * Cache in which to store compiled shader binaries between runs. + */ + PersistentCache* fPersistentCache = nullptr; + + /** + * If present, use this object to report shader compilation failures. If not, report failures + * via SkDebugf and assert. + */ + ShaderErrorHandler* fShaderErrorHandler = nullptr; + + /** Default minimum size to use when allocating buffers for uploading data to textures. The + larger the value the more uploads can be packed into one buffer, but at the cost of + more gpu memory allocated that may not be used. Uploads larger than the minimum will still + work by allocating a dedicated buffer. */ + size_t fMinimumStagingBufferSize = 64 * 1024; + + /** + * The maximum size of cache textures used for Skia's Glyph cache. + */ + size_t fGlyphCacheTextureMaximumBytes = 2048 * 1024 * 4; /** * Controls whether we check for GL errors after functions that allocate resources (e.g. @@ -86,6 +133,33 @@ struct SK_API GrContextOptions { */ Enable fSkipGLErrorChecks = Enable::kDefault; + /** + * Can the glyph atlas use multiple textures. If allowed, the each texture's size is bound by + * fGlypheCacheTextureMaximumBytes. + */ + Enable fAllowMultipleGlyphCacheTextures = Enable::kDefault; + + /** + * Enables driver workaround to use draws instead of HW clears, e.g. glClear on the GL backend. + */ + Enable fUseDrawInsteadOfClear = Enable::kDefault; + + /** + * Allow Ganesh to more aggressively reorder operations to reduce the number of render passes. + * Offscreen draws will be done upfront instead of interrupting the main render pass when + * possible. May increase VRAM usage, but still observes the resource cache limit. + * Enabled by default. + */ + Enable fReduceOpsTaskSplitting = Enable::kDefault; + + /** + * This affects the usage of the PersistentCache. We can cache SkSL, backend source (GLSL), or + * backend binaries (GL program binaries). By default we cache binaries, but if the driver's + * binary loading/storing is believed to have bugs, this can be limited to caching GLSL. + * Caching GLSL strings still saves CPU work when a GL program is created. + */ + ShaderCacheStrategy fShaderCacheStrategy = ShaderCacheStrategy::kBackendBinary; + /** Overrides: These options override feature detection using backend API queries. These overrides can only reduce the feature set or limits, never increase them beyond the detected values. */ @@ -97,19 +171,48 @@ struct SK_API GrContextOptions { deduce the optimal value for this platform. */ int fBufferMapThreshold = -1; - /** Default minimum size to use when allocating buffers for uploading data to textures. The - larger the value the more uploads can be packed into one buffer, but at the cost of - more gpu memory allocated that may not be used. Uploads larger than the minimum will still - work by allocating a dedicated buffer. */ - size_t fMinimumStagingBufferSize = 64 * 1024; + /** + * Maximum number of GPU programs or pipelines to keep active in the runtime cache. + */ + int fRuntimeProgramCacheSize = 256; /** - * Executor to handle threaded work within Ganesh. If this is nullptr, then all work will be - * done serially on the main thread. To have worker threads assist with various tasks, set this - * to a valid SkExecutor instance. Currently, used for software path rendering, but may be used - * for other tasks. + * Specifies the number of samples Ganesh should use when performing internal draws with MSAA + * (hardware capabilities permitting). + * + * If 0, Ganesh will disable internal code paths that use multisampling. */ - SkExecutor* fExecutor = nullptr; + int fInternalMultisampleCount = 4; + + /** + * In Skia's vulkan backend a single GrContext submit equates to the submission of a single + * primary command buffer to the VkQueue. This value specifies how many vulkan secondary command + * buffers we will cache for reuse on a given primary command buffer. A single submit may use + * more than this many secondary command buffers, but after the primary command buffer is + * finished on the GPU it will only hold on to this many secondary command buffers for reuse. + * + * A value of -1 means we will pick a limit value internally. + */ + int fMaxCachedVulkanSecondaryCommandBuffers = -1; + + /** + * Below this threshold size in device space distance field fonts won't be used. Distance field + * fonts don't support hinting which is more important at smaller sizes. + */ + float fMinDistanceFieldFontSize = 18; + + /** + * Above this threshold size in device space glyphs are drawn as individual paths. + */ +#if defined(SK_BUILD_FOR_ANDROID) + float fGlyphsAsPathsFontSize = 384; +#elif defined(SK_BUILD_FOR_MAC) + float fGlyphsAsPathsFontSize = 256; +#else + float fGlyphsAsPathsFontSize = 324; +#endif + + GrDriverBugWorkarounds fDriverBugWorkarounds; /** Construct mipmaps manually, via repeated downsampling draw-calls. This is used when the driver's implementation (glGenerateMipmap) contains bugs. This requires mipmap @@ -142,34 +245,6 @@ struct SK_API GrContextOptions { */ bool fDisableGpuYUVConversion = false; - /** - * The maximum size of cache textures used for Skia's Glyph cache. - */ - size_t fGlyphCacheTextureMaximumBytes = 2048 * 1024 * 4; - - /** - * Below this threshold size in device space distance field fonts won't be used. Distance field - * fonts don't support hinting which is more important at smaller sizes. - */ - float fMinDistanceFieldFontSize = 18; - - /** - * Above this threshold size in device space glyphs are drawn as individual paths. - */ -#if defined(SK_BUILD_FOR_ANDROID) - float fGlyphsAsPathsFontSize = 384; -#elif defined(SK_BUILD_FOR_MAC) - float fGlyphsAsPathsFontSize = 256; -#else - float fGlyphsAsPathsFontSize = 324; -#endif - - /** - * Can the glyph atlas use multiple textures. If allowed, the each texture's size is bound by - * fGlypheCacheTextureMaximumBytes. - */ - Enable fAllowMultipleGlyphCacheTextures = Enable::kDefault; - /** * Bugs on certain drivers cause stencil buffers to leak. This flag causes Skia to avoid * allocating stencil buffers and use alternate rasterization paths, avoiding the leak. @@ -183,19 +258,6 @@ struct SK_API GrContextOptions { */ bool fSharpenMipmappedTextures = true; - /** - * Enables driver workaround to use draws instead of HW clears, e.g. glClear on the GL backend. - */ - Enable fUseDrawInsteadOfClear = Enable::kDefault; - - /** - * Allow Ganesh to more aggressively reorder operations to reduce the number of render passes. - * Offscreen draws will be done upfront instead of interrupting the main render pass when - * possible. May increase VRAM usage, but still observes the resource cache limit. - * Enabled by default. - */ - Enable fReduceOpsTaskSplitting = Enable::kDefault; - /** * Some ES3 contexts report the ES2 external image extension, but not the ES3 version. * If support for external images is critical, enabling this option will cause Ganesh to limit @@ -210,60 +272,6 @@ struct SK_API GrContextOptions { */ bool fDisableDriverCorrectnessWorkarounds = false; - /** - * Maximum number of GPU programs or pipelines to keep active in the runtime cache. - */ - int fRuntimeProgramCacheSize = 256; - - /** - * Cache in which to store compiled shader binaries between runs. - */ - PersistentCache* fPersistentCache = nullptr; - - /** - * This affects the usage of the PersistentCache. We can cache SkSL, backend source (GLSL), or - * backend binaries (GL program binaries). By default we cache binaries, but if the driver's - * binary loading/storing is believed to have bugs, this can be limited to caching GLSL. - * Caching GLSL strings still saves CPU work when a GL program is created. - */ - ShaderCacheStrategy fShaderCacheStrategy = ShaderCacheStrategy::kBackendBinary; - - /** - * If present, use this object to report shader compilation failures. If not, report failures - * via SkDebugf and assert. - */ - ShaderErrorHandler* fShaderErrorHandler = nullptr; - - /** - * Specifies the number of samples Ganesh should use when performing internal draws with MSAA - * (hardware capabilities permitting). - * - * If 0, Ganesh will disable internal code paths that use multisampling. - */ - int fInternalMultisampleCount = 4; - - /** - * In Skia's vulkan backend a single GrContext submit equates to the submission of a single - * primary command buffer to the VkQueue. This value specifies how many vulkan secondary command - * buffers we will cache for reuse on a given primary command buffer. A single submit may use - * more than this many secondary command buffers, but after the primary command buffer is - * finished on the GPU it will only hold on to this many secondary command buffers for reuse. - * - * A value of -1 means we will pick a limit value internally. - */ - int fMaxCachedVulkanSecondaryCommandBuffers = -1; - - /** - * If Skia is creating a default VMA allocator for the Vulkan backend this value will be used - * for the preferredLargeHeapBlockSize. If the value is not set, then Skia will use an - * inernally defined default size. - * - * However, it is highly discouraged to have Skia make a default allocator (and support for - * doing so will be removed soon, b/321962001). Instead clients should create their own - * allocator to pass into Skia where they can fine tune this value themeselves. - */ - std::optional fVulkanVMALargeHeapBlockSize; - /** * If true, the caps will never support mipmaps. */ @@ -307,20 +315,32 @@ struct SK_API GrContextOptions { */ bool fAlwaysUseTexStorageWhenAvailable = false; - /** - * Optional callback that can be passed into the GrDirectContext which will be called when the - * GrDirectContext is about to be destroyed. When this call is made, it will be safe for the - * client to delete the GPU backend context that is backing the GrDirectContext. The - * GrDirectContextDestroyedContext will be passed back to the client in the callback. - */ - GrDirectContextDestroyedContext fContextDeleteContext = nullptr; - GrDirectContextDestroyedProc fContextDeleteProc = nullptr; + // Suppress prints for the GrContext. + bool fSuppressPrints = false; #if defined(GPU_TEST_UTILS) /** * Private options that are only meant for testing within Skia's tools. */ + /** + * Include or exclude specific GPU path renderers. + */ + GpuPathRenderers fGpuPathRenderers = GpuPathRenderers::kDefault; + + /** + * Specify the GPU resource cache limit. Equivalent to calling `setResourceCacheLimit` on the + * context at construction time. + * + * A value of -1 means use the default limit value. + */ + int fResourceCacheLimitOverride = -1; + + /** + * Maximum width and height of internal texture atlases. + */ + int fMaxTextureAtlasSize = 2048; + /** * Testing-only mode to exercise allocation failures in the flush-time callback objects. * For now it only simulates allocation failure during the preFlush callback. @@ -368,26 +388,8 @@ struct SK_API GrContextOptions { */ bool fDisallowWriteAndTransferPixelRowBytes = false; - /** - * Include or exclude specific GPU path renderers. - */ - GpuPathRenderers fGpuPathRenderers = GpuPathRenderers::kDefault; - - /** - * Specify the GPU resource cache limit. Equivalent to calling `setResourceCacheLimit` on the - * context at construction time. - * - * A value of -1 means use the default limit value. - */ - int fResourceCacheLimitOverride = -1; - - /** - * Maximum width and height of internal texture atlases. - */ - int fMaxTextureAtlasSize = 2048; #endif - GrDriverBugWorkarounds fDriverBugWorkarounds; }; #endif diff --git a/gfx/skia/skia/include/gpu/ganesh/GrDirectContext.h b/gfx/skia/skia/include/gpu/ganesh/GrDirectContext.h index c60bb4d2989c..cc4d527857e3 100644 --- a/gfx/skia/skia/include/gpu/ganesh/GrDirectContext.h +++ b/gfx/skia/skia/include/gpu/ganesh/GrDirectContext.h @@ -271,6 +271,11 @@ public: */ void purgeUnlockedResources(GrPurgeResourceOptions opts); + /* + * Gets the types of GPU stats supported by this Context. + */ + skgpu::GpuStatsFlags supportedGpuStats() const; + /** * Gets the maximum supported texture size. */ diff --git a/gfx/skia/skia/include/gpu/ganesh/GrDriverBugWorkarounds.h b/gfx/skia/skia/include/gpu/ganesh/GrDriverBugWorkarounds.h index d7d4509e2707..f44bc066be2d 100644 --- a/gfx/skia/skia/include/gpu/ganesh/GrDriverBugWorkarounds.h +++ b/gfx/skia/skia/include/gpu/ganesh/GrDriverBugWorkarounds.h @@ -34,8 +34,10 @@ enum GrDriverBugWorkaroundType { class SK_API GrDriverBugWorkarounds { public: - GrDriverBugWorkarounds(); + GrDriverBugWorkarounds() = default; GrDriverBugWorkarounds(const GrDriverBugWorkarounds&) = default; + ~GrDriverBugWorkarounds() = default; + explicit GrDriverBugWorkarounds(const std::vector& workarounds); GrDriverBugWorkarounds& operator=(const GrDriverBugWorkarounds&) = default; @@ -43,8 +45,6 @@ class SK_API GrDriverBugWorkarounds { // Turn on any workarounds listed in |workarounds| (but don't turn any off). void applyOverrides(const GrDriverBugWorkarounds& workarounds); - ~GrDriverBugWorkarounds(); - #define GPU_OP(type, name) bool name = false; GPU_DRIVER_BUG_WORKAROUNDS(GPU_OP) #undef GPU_OP diff --git a/gfx/skia/skia/include/gpu/ganesh/GrTypes.h b/gfx/skia/skia/include/gpu/ganesh/GrTypes.h index 0b23a721f3f9..129092ad95af 100644 --- a/gfx/skia/skia/include/gpu/ganesh/GrTypes.h +++ b/gfx/skia/skia/include/gpu/ganesh/GrTypes.h @@ -9,17 +9,13 @@ #define GrTypes_DEFINED #include "include/core/SkTypes.h" +#include "include/gpu/GpuTypes.h" #include "include/private/base/SkTo.h" // IWYU pragma: keep #include #include class GrBackendSemaphore; -namespace skgpu { -enum class Protected : bool; -enum class Renderable : bool; -} - /////////////////////////////////////////////////////////////////////////////// /** @@ -110,6 +106,8 @@ static const uint32_t kAll_GrBackendState = 0xffffffff; typedef void* GrGpuFinishedContext; typedef void (*GrGpuFinishedProc)(GrGpuFinishedContext finishedContext); +typedef void (*GrGpuFinishedWithStatsProc)(GrGpuFinishedContext finishedContext, + const skgpu::GpuStats&); typedef void* GrGpuSubmittedContext; typedef void (*GrGpuSubmittedProc)(GrGpuSubmittedContext submittedContext, bool success); @@ -131,10 +129,15 @@ typedef void (*GrDirectContextDestroyedProc)(GrDirectContextDestroyedContext des * and returned in initialized GrBackendSemaphore objects. The GrBackendSemaphore objects * themselves can be deleted as soon as this function returns. * - * If a finishedProc is provided, the finishedProc will be called when all work submitted to the gpu - * from this flush call and all previous flush calls has finished on the GPU. If the flush call - * fails due to an error and nothing ends up getting sent to the GPU, the finished proc is called - * immediately. + * If a finishedProc or finishedWithStatsProc is provided, the proc will be called when all work + * submitted to the gpu from this flush call and all previous flush calls has finished on the GPU. + * If the flush call fails due to an error and nothing ends up getting sent to the GPU, the finished + * proc is called immediately. If both types of proc are provided then finishedWithStatsProc is + * preferred. + * + * When finishedWithStatsProc is called the GpuStats passed will contain valid values for stats + * by requested by gpuStatsFlags, assuming the stats are supported by the underlying backend GPU + * context and the GPU work completed successfully. * * If a submittedProc is provided, the submittedProc will be called when all work from this flush * call is submitted to the GPU. If the flush call fails due to an error and nothing will get sent @@ -148,8 +151,10 @@ typedef void (*GrDirectContextDestroyedProc)(GrDirectContextDestroyedContext des */ struct GrFlushInfo { size_t fNumSemaphores = 0; + skgpu::GpuStatsFlags fGpuStatsFlags = skgpu::GpuStatsFlags::kNone; GrBackendSemaphore* fSignalSemaphores = nullptr; GrGpuFinishedProc fFinishedProc = nullptr; + GrGpuFinishedWithStatsProc fFinishedWithStatsProc = nullptr; GrGpuFinishedContext fFinishedContext = nullptr; GrGpuSubmittedProc fSubmittedProc = nullptr; GrGpuSubmittedContext fSubmittedContext = nullptr; diff --git a/gfx/skia/skia/include/gpu/graphite/BackendTexture.h b/gfx/skia/skia/include/gpu/graphite/BackendTexture.h index 4bc63a358958..c35e6cd8c5b0 100644 --- a/gfx/skia/skia/include/gpu/graphite/BackendTexture.h +++ b/gfx/skia/skia/include/gpu/graphite/BackendTexture.h @@ -17,7 +17,6 @@ namespace skgpu::graphite { class BackendTextureData; -struct VulkanTextureInfo; class SK_API BackendTexture { public: @@ -61,4 +60,3 @@ private: } // namespace skgpu::graphite #endif // skgpu_graphite_BackendTexture_DEFINED - diff --git a/gfx/skia/skia/include/gpu/graphite/Context.h b/gfx/skia/skia/include/gpu/graphite/Context.h index 2112412aa26d..ac2d0208d648 100644 --- a/gfx/skia/skia/include/gpu/graphite/Context.h +++ b/gfx/skia/skia/include/gpu/graphite/Context.h @@ -16,6 +16,10 @@ #include "include/gpu/graphite/Recorder.h" #include "include/private/base/SingleOwner.h" +#if defined(GPU_TEST_UTILS) +#include "include/private/base/SkMutex.h" +#endif + #include #include #include @@ -33,7 +37,7 @@ class Context; class ContextPriv; class GlobalCache; class PaintOptions; -class PlotUploadTracker; +class PrecompileContext; class QueueManager; class Recording; class ResourceProvider; @@ -53,6 +57,11 @@ public: std::unique_ptr makeRecorder(const RecorderOptions& = {}); + /** Creates a helper object that can be moved to a different thread and used + * for precompilation. + */ + std::unique_ptr makePrecompileContext(); + bool insertRecording(const InsertRecordingInfo&); bool submit(SyncToCpu = SyncToCpu::kNo); @@ -218,6 +227,12 @@ public: */ size_t maxBudgetedBytes() const; + /** + * Sets the size of Context's gpu memory cache budget in bytes. If the new budget is lower than + * the current budget, the cache will try to free resources to get under the new budget. + */ + void setMaxBudgetedBytes(size_t bytes); + /** * Enumerates all cached GPU resources owned by the Context and dumps their memory to * traceMemoryDump. @@ -240,6 +255,11 @@ public: */ bool supportsProtectedContent() const; + /* + * Gets the types of GPU stats supported by this Context. + */ + GpuStatsFlags supportedGpuStats() const; + // Provides access to functions that aren't part of the public API. ContextPriv priv(); const ContextPriv priv() const; // NOLINT(readability-const-return-type) @@ -343,11 +363,14 @@ private: mutable SingleOwner fSingleOwner; #if defined(GPU_TEST_UTILS) + void deregisterRecorder(const Recorder*) SK_EXCLUDES(fTestingLock); + // In test builds a Recorder may track the Context that was used to create it. bool fStoreContextRefInRecorder = false; // If this tracking is on, to allow the client to safely delete this Context or its Recorders // in any order we must also track the Recorders created here. - std::vector fTrackedRecorders; + SkMutex fTestingLock; + std::vector fTrackedRecorders SK_GUARDED_BY(fTestingLock); #endif // Needed for MessageBox handling diff --git a/gfx/skia/skia/include/gpu/graphite/ContextOptions.h b/gfx/skia/skia/include/gpu/graphite/ContextOptions.h index d377eb951547..e139b1f77674 100644 --- a/gfx/skia/skia/include/gpu/graphite/ContextOptions.h +++ b/gfx/skia/skia/include/gpu/graphite/ContextOptions.h @@ -8,11 +8,15 @@ #ifndef skgpu_graphite_ContextOptions_DEFINED #define skgpu_graphite_ContextOptions_DEFINED +#include "include/core/SkRefCnt.h" +#include "include/core/SkSpan.h" #include "include/private/base/SkAPI.h" #include "include/private/base/SkMath.h" #include +class SkData; +class SkRuntimeEffect; namespace skgpu { class ShaderErrorHandler; } namespace skgpu::graphite { @@ -85,12 +89,12 @@ struct SK_API ContextOptions { bool fSupportBilerpFromGlyphAtlas = false; /** - * Disable caching of glyph uploads at the start of each Recording. These can add additional - * overhead and are only necessary if Recordings are replayed or played out of order. - * - * Deprecated, now only used to set requireOrderedRecordings Caps. + * For the moment, if Recordings are replayed in the order they are recorded, then + * Graphite can make certain assumptions that allow for better performance. Otherwise + * we have to flush some caches at the start of each Recording to ensure that they can + * be played back properly. */ - bool fDisableCachedGlyphUploads = false; + bool fRequireOrderedRecordings = false; static constexpr size_t kDefaultContextBudget = 256 * (1 << 20); /** @@ -118,6 +122,37 @@ struct SK_API ContextOptions { */ std::optional fVulkanVMALargeHeapBlockSize; + /** Client-provided context that is passed to client-provided PipelineCallback. */ + using PipelineCallbackContext = void*; + /** Client-provided callback that is called whenever Graphite encounters a new Pipeline. */ + using PipelineCallback = void (*)(PipelineCallbackContext context, sk_sp pipelineData); + + /** + * These two members allow a client to register a callback that will be invoked + * whenever Graphite encounters a new Pipeline. The callback will be passed an + * sk_sp that a client can take ownership of and serialize. The SkData + * contains all the information Graphite requires to recreate the Pipeline at + * a later date. The SkData is versioned however, so must be regenerated and + * re-serialized when it becomes out of date. + */ + PipelineCallbackContext fPipelineCallbackContext = nullptr; + PipelineCallback fPipelineCallback = nullptr; + + /** + * The runtime effects provided here will be registered as user-defined *known* runtime + * effects and will be given a stable key. Such runtime effects can then be used in + * serialized pipeline keys (c.f. PrecompileContext::precompile). + * + * Graphite will take a ref on the provided runtime effects and they will persist for as long + * as the Context exists. Rather than recreating new SkRuntimeEffects using the same SkSL, + * clients should use the existing SkRuntimeEffects provided here. + * + * Warning: Registering runtime effects here does obligate users to clear out their caches + * of serialized pipeline keys if the provided runtime effects ever change in a meaningful way. + * This includes adding, removing or reordering the effects provided here. + */ + SkSpan> fUserDefinedKnownRuntimeEffects; + /** * Private options that are only meant for testing within Skia's tools. */ diff --git a/gfx/skia/skia/include/gpu/graphite/GraphiteTypes.h b/gfx/skia/skia/include/gpu/graphite/GraphiteTypes.h index cabf9eccc13e..bcb26b9672f2 100644 --- a/gfx/skia/skia/include/gpu/graphite/GraphiteTypes.h +++ b/gfx/skia/skia/include/gpu/graphite/GraphiteTypes.h @@ -30,13 +30,23 @@ class Task; using GpuFinishedContext = void*; using GpuFinishedProc = void (*)(GpuFinishedContext finishedContext, CallbackResult); +using GpuFinishedWithStatsProc = void (*)(GpuFinishedContext finishedContext, + CallbackResult, + const GpuStats&); + /** * The fFinishedProc is called when the Recording has been submitted and finished on the GPU, or * when there is a failure that caused it not to be submitted. The callback will always be called * and the caller can use the callback to know it is safe to free any resources associated with * the Recording that they may be holding onto. If the Recording is successfully submitted to the * GPU the callback will be called with CallbackResult::kSuccess once the GPU has finished. All - * other cases where some failure occured it will be called with CallbackResult::kFailed. + * other cases where some failure occurred it will be called with CallbackResult::kFailed. + * + * Alternatively, the client can provide fFinishedProcWithStats. This provides additional + * information about execution of the recording on the GPU. Only the stats requested using + * fStatsFlags will be valid and only if CallbackResult is kSuccess. If both fFinishedProc + * and fFinishedProcWithStats are provided the latter is preferred and the former won't be + * called. * * The fTargetSurface, if provided, is used as a target for any draws recorded onto a deferred * canvas returned from Recorder::makeDeferredCanvas. This target surface must be provided iff @@ -74,8 +84,10 @@ struct InsertRecordingInfo { size_t fNumSignalSemaphores = 0; BackendSemaphore* fSignalSemaphores = nullptr; + GpuStatsFlags fGpuStatsFlags = GpuStatsFlags::kNone; GpuFinishedContext fFinishedContext = nullptr; GpuFinishedProc fFinishedProc = nullptr; + GpuFinishedWithStatsProc fFinishedWithStatsProc = nullptr; }; /** @@ -87,8 +99,15 @@ struct InsertRecordingInfo { * other cases where some failure occured it will be called with CallbackResult::kFailed. */ struct InsertFinishInfo { + InsertFinishInfo() = default; + InsertFinishInfo(GpuFinishedContext context, GpuFinishedProc proc) + : fFinishedContext{context}, fFinishedProc{proc} {} + InsertFinishInfo(GpuFinishedContext context, GpuFinishedWithStatsProc proc) + : fFinishedContext{context}, fFinishedWithStatsProc{proc} {} GpuFinishedContext fFinishedContext = nullptr; GpuFinishedProc fFinishedProc = nullptr; + GpuFinishedWithStatsProc fFinishedWithStatsProc = nullptr; + GpuStatsFlags fGpuStatsFlags = GpuStatsFlags::kNone; }; /** @@ -121,44 +140,47 @@ enum class DepthStencilFlags : int { */ enum DrawTypeFlags : uint16_t { - kNone = 0b000000000, + kNone = 0, // kBitmapText_Mask should be used for the BitmapTextRenderStep[mask] RenderStep - kBitmapText_Mask = 0b00000001, + kBitmapText_Mask = 1 << 0, // kBitmapText_LCD should be used for the BitmapTextRenderStep[LCD] RenderStep - kBitmapText_LCD = 0b00000010, + kBitmapText_LCD = 1 << 1, // kBitmapText_Color should be used for the BitmapTextRenderStep[color] RenderStep - kBitmapText_Color = 0b00000100, + kBitmapText_Color = 1 << 2, // kSDFText should be used for the SDFTextRenderStep RenderStep - kSDFText = 0b00001000, + kSDFText = 1 << 3, // kSDFText_LCD should be used for the SDFTextLCDRenderStep RenderStep - kSDFText_LCD = 0b00010000, + kSDFText_LCD = 1 << 4, // kDrawVertices should be used to generate Pipelines that use the following RenderSteps: // VerticesRenderStep[*] for: - // [tris], [tris-texCoords], [tris-color], [tris-color-texCoords], - // [tristrips], [tristrips-texCoords], [tristrips-color], [tristrips-color-texCoords] - kDrawVertices = 0b00100000, + // [Tris], [TrisTexCoords], [TrisColor], [TrisColorTexCoords], + // [Tristrips], [TristripsTexCoords], [TristripsColor], [TristripsColorTexCoords] + kDrawVertices = 1 << 5, + + // kCircularArc renders filled circular arcs, with or without the center included, and + // stroked circular arcs with butt or round caps that don't include the center point. + // It corresponds to the CircularArcRenderStep. + kCircularArc = 1 << 6, // kSimpleShape should be used to generate Pipelines that use the following RenderSteps: - // AnalyticBlurRenderStep // AnalyticRRectRenderStep // PerEdgeAAQuadRenderStep - // CoverBoundsRenderStep[non-aa-fill] - kSimpleShape = 0b01000000, + // CoverBoundsRenderStep[NonAAFill] + kSimpleShape = 1 << 7, // kNonSimpleShape should be used to generate Pipelines that use the following RenderSteps: // CoverageMaskRenderStep - // CoverBoundsRenderStep[*] for [inverse-cover], [regular-cover] + // CoverBoundsRenderStep[*] for [InverseCover], [RegularCover] // TessellateStrokeRenderStep - // TessellateWedgesRenderStep[*] for [convex], [evenodd], [winding] - // TessellateCurvesRenderStep[*] for [even-odd], [winding] - // MiddleOutFanRenderStep[*] for [even-odd], [winding] - kNonSimpleShape = 0b10000000, + // TessellateWedgesRenderStep[*] for [Convex], [EvenOdd], [Winding] + // TessellateCurvesRenderStep[*] for [EvenOdd], [Winding] + // MiddleOutFanRenderStep[*] for [EvenOdd], [Winding] + kNonSimpleShape = 1 << 8, kLast = kNonSimpleShape, }; -static constexpr int kDrawTypeFlagsCnt = static_cast(DrawTypeFlags::kLast) + 1; } // namespace skgpu::graphite diff --git a/gfx/skia/skia/include/gpu/graphite/Image.h b/gfx/skia/skia/include/gpu/graphite/Image.h index ccc69857acd2..cf95ea7ffbce 100644 --- a/gfx/skia/skia/include/gpu/graphite/Image.h +++ b/gfx/skia/skia/include/gpu/graphite/Image.h @@ -15,6 +15,7 @@ #include "include/gpu/GpuTypes.h" #include +#include class SkYUVAInfo; class SkYUVAPixmaps; diff --git a/gfx/skia/skia/include/gpu/graphite/LogPriority.h b/gfx/skia/skia/include/gpu/graphite/LogPriority.h new file mode 100644 index 000000000000..d149fd1b3745 --- /dev/null +++ b/gfx/skia/skia/include/gpu/graphite/LogPriority.h @@ -0,0 +1,36 @@ +/* + * Copyright 2024 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef skgpu_graphite_LogPriority_DEFINED +#define skgpu_graphite_LogPriority_DEFINED + +/** + * Note: this file may be included in clients' SkUserConfig.h files, so including any other headers + * in this file should be avoided. + */ + +namespace skgpu::graphite { +/** + * SKGPU_GRAPHITE_LOWEST_ACTIVE_LOG_PRIORITY can be defined to one of these values (in + * SkUserConfig.h) to control Graphite's logging behavior. + * + * For example: + * ``` + * #define SKGPU_GRAPHITE_LOWEST_ACTIVE_LOG_PRIORITY skgpu::graphite::LogPriority::kWarning + * ``` + * Would cause Graphite to log warnings, non-fatal errors, and fatal errors. + * However, debug logs would be omitted. + */ +enum class LogPriority : int { + kFatal = 0, + kError = 1, + kWarning = 2, + kDebug = 3, +}; +}; // namespace skgpu::graphite + +#endif // skgpu_graphite_LogPriority_DEFINED diff --git a/gfx/skia/skia/include/gpu/graphite/PrecompileContext.h b/gfx/skia/skia/include/gpu/graphite/PrecompileContext.h new file mode 100644 index 000000000000..18bbe39e6a17 --- /dev/null +++ b/gfx/skia/skia/include/gpu/graphite/PrecompileContext.h @@ -0,0 +1,76 @@ +/* + * Copyright 2024 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef skgpu_graphite_PrecompileContext_DEFINED +#define skgpu_graphite_PrecompileContext_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/private/base/SingleOwner.h" + +#include +#include + +class SkData; + +namespace skgpu::graphite { + +class SharedContext; +class PrecompileContextPriv; +class ResourceProvider; + +class SK_API PrecompileContext { +public: + ~PrecompileContext(); + + /** + * Purge Pipelines that haven't been used in the past 'msNotUsed' milliseconds + * regardless of whether the pipeline cache is under budget. + * + * @param msNotUsed Pipelines not used in these last milliseconds will be cleaned up. + */ + void purgePipelinesNotUsedInMs(std::chrono::milliseconds msNotUsed); + + /** + * Emit histograms (using the SK_HISTOGRAM* macros) for Skia's Pipeline usage. + */ + void reportPipelineStats(); + + /** + * Precompile one specific Pipeline that has been previously serialized. Serialized pipeline + * keys can be acquired via the ContextOptions::PipelineCallback. + * + * @param serializedPipelineKey serialized Pipeline key. + * @return true if a Pipeline was created from the key; false otherwise + */ + bool precompile(sk_sp serializedPipelineKey); + + /** + * Get a human-readable version of a serialized pipeline key. + * + * @param serializedPipelineKey serialized Pipeline key. + * @return A human-readable version of the provided key; "" on failure. + */ + std::string getPipelineLabel(sk_sp serializedPipelineKey); + + // Provides access to functions that aren't part of the public API. + PrecompileContextPriv priv(); + const PrecompileContextPriv priv() const; // NOLINT(readability-const-return-type) + +private: + friend class PrecompileContextPriv; + friend class Context; // for ctor + + PrecompileContext(sk_sp); + + mutable SingleOwner fSingleOwner; + sk_sp fSharedContext; + std::unique_ptr fResourceProvider; +}; + +} // namespace skgpu::graphite + +#endif // skgpu_graphite_PrecompileContext_DEFINED diff --git a/gfx/skia/skia/include/gpu/graphite/Recorder.h b/gfx/skia/skia/include/gpu/graphite/Recorder.h index cd1917104b60..24f1483f46c2 100644 --- a/gfx/skia/skia/include/gpu/graphite/Recorder.h +++ b/gfx/skia/skia/include/gpu/graphite/Recorder.h @@ -209,6 +209,12 @@ public: */ size_t maxBudgetedBytes() const; + /** + * Sets the size of Recorders's gpu memory cache budget in bytes. If the new budget is lower + * than the current budget, the cache will try to free resources to get under the new budget. + */ + void setMaxBudgetedBytes(size_t bytes); + /** * Enumerates all cached GPU resources owned by the Recorder and dumps their memory to * traceMemoryDump. diff --git a/gfx/skia/skia/include/gpu/graphite/Recording.h b/gfx/skia/skia/include/gpu/graphite/Recording.h index 5a7827b88b4f..86bbe1636fc3 100644 --- a/gfx/skia/skia/include/gpu/graphite/Recording.h +++ b/gfx/skia/skia/include/gpu/graphite/Recording.h @@ -9,6 +9,7 @@ #define skgpu_graphite_Recording_DEFINED #include "include/core/SkRefCnt.h" +#include "include/core/SkSize.h" #include "include/private/base/SkTArray.h" #include @@ -21,6 +22,7 @@ class RefCntedCallback; namespace skgpu::graphite { +class Caps; class CommandBuffer; class RecordingPriv; class Resource; @@ -44,7 +46,7 @@ private: // replay, and it handles the target proxy's instantiation with the provided target. class LazyProxyData { public: - LazyProxyData(const TextureInfo&); + LazyProxyData(const Caps*, SkISize dimensions, const TextureInfo&); TextureProxy* lazyProxy(); sk_sp refLazyProxy(); diff --git a/gfx/skia/skia/include/gpu/graphite/TextureInfo.h b/gfx/skia/skia/include/gpu/graphite/TextureInfo.h index 90400e101659..98f80a9978aa 100644 --- a/gfx/skia/skia/include/gpu/graphite/TextureInfo.h +++ b/gfx/skia/skia/include/gpu/graphite/TextureInfo.h @@ -18,70 +18,115 @@ struct SkISize; namespace skgpu::graphite { -class TextureInfoData; +enum class TextureFormat : uint8_t; +/** + * TextureInfo is a backend-agnostic wrapper around the properties of a texture, sans dimensions. + * It is designed this way to be compilable w/o bringing in a specific backend's build files, and + * without requiring heap allocations of virtual types. + */ class SK_API TextureInfo { +private: + class Data; + friend class MtlTextureInfo; + friend class DawnTextureInfo; + friend class VulkanTextureInfo; + + // Size is the largest of the Data subclasses assuming a 64-bit compiler. + inline constexpr static size_t kMaxSubclassSize = 112; + using AnyTextureInfoData = SkAnySubclass; + + // Base properties for all backend-specific properties. Clients managing textures directly + // should use the public subclasses of Data directly, e.g. MtlTextureInfo/DawnTextureInfo. + // + // Each backend subclass must expose to TextureInfo[Priv]: + // static constexpr BackendApi kBackend; + // Protected isProtected() const; + // TextureFormat viewFormat() const; + // bool serialize(SkWStream*) const; + // bool deserialize(SkStream*); + class Data { + public: + virtual ~Data() = default; + + Data(uint32_t sampleCount, skgpu::Mipmapped mipmapped) + : fSampleCount(sampleCount) + , fMipmapped(mipmapped) {} + + Data() = default; + Data(const Data&) = default; + + Data& operator=(const Data&) = default; + + // NOTE: These fields are accessible via the backend-specific subclasses. + uint32_t fSampleCount = 1; + Mipmapped fMipmapped = Mipmapped::kNo; + + private: + friend class TextureInfo; + friend class TextureInfoPriv; + + virtual SkString toBackendString() const = 0; + + virtual void copyTo(AnyTextureInfoData&) const = 0; + // Passed in TextureInfo will have data of the same backend type and subclass, and + // base properties of Data have already been checked for equality/compatibility. + virtual bool isCompatible(const TextureInfo& that, bool requireExact) const = 0; + }; + public: - TextureInfo(); - ~TextureInfo(); + TextureInfo() = default; + ~TextureInfo() = default; + TextureInfo(const TextureInfo&); TextureInfo& operator=(const TextureInfo&); - bool operator==(const TextureInfo&) const; + bool operator==(const TextureInfo& that) const { + return this->isCompatible(that, /*requireExact=*/true); + } bool operator!=(const TextureInfo& that) const { return !(*this == that); } - bool isValid() const { return fValid; } - BackendApi backend() const { return fBackend; } - - uint32_t numSamples() const { return fSampleCount; } - Mipmapped mipmapped() const { return fMipmapped; } - Protected isProtected() const { return fProtected; } - SkTextureCompressionType compressionType() const; - bool isMemoryless() const; - - bool isCompatible(const TextureInfo& that) const; - // Return a string containing the full description of this TextureInfo. - SkString toString() const; - // Return a string containing only the info relevant for its use as a RenderPass attachment. - SkString toRPAttachmentString() const; - -private: - friend class TextureInfoData; - friend class TextureInfoPriv; - - // Size determined by looking at the TextureInfoData subclasses, then guessing-and-checking. - // Compiler will complain if this is too small - in that case, just increase the number. - inline constexpr static size_t kMaxSubclassSize = 112; - using AnyTextureInfoData = SkAnySubclass; - - template - TextureInfo(BackendApi backend, - uint32_t sampleCount, - skgpu::Mipmapped mipped, - skgpu::Protected isProtected, - const SomeTextureInfoData& textureInfoData) - : fBackend(backend) - , fValid(true) - , fSampleCount(sampleCount) - , fMipmapped(mipped) - , fProtected(isProtected) { - fTextureInfoData.emplace(textureInfoData); + bool isValid() const { return fData.has_value(); } + BackendApi backend() const { + SkASSERT(fData.has_value() || fBackend == BackendApi::kUnsupported); + return fBackend; } - friend size_t ComputeSize(SkISize dimensions, const TextureInfo&); // for bytesPerPixel + uint32_t numSamples() const { return fData.has_value() ? fData->fSampleCount : 1; } + Mipmapped mipmapped() const { return fData.has_value() ? fData->fMipmapped : Mipmapped::kNo; } + Protected isProtected() const { return fProtected; } - size_t bytesPerPixel() const; + // Return true if `that` describes a texture that is compatible with this info and can validly + // be used to fulfill a promise image that was created with this TextureInfo. + bool canBeFulfilledBy(const TextureInfo& that) const { + return this->isCompatible(that, /*requireExact=*/false); + } - BackendApi fBackend = BackendApi::kMock; - bool fValid = false; + // Return a string containing the full description of this TextureInfo. + SkString toString() const; - uint32_t fSampleCount = 1; - Mipmapped fMipmapped = Mipmapped::kNo; +private: + friend class TextureInfoPriv; + + template , bool> = true> + explicit TextureInfo(const BackendTextureData& data) + : fBackend(BackendTextureData::kBackend) + , fViewFormat(data.viewFormat()) + , fProtected(data.isProtected()) { + fData.emplace(data); + } + + bool isCompatible(const TextureInfo& that, bool requireExact) const; + + skgpu::BackendApi fBackend = BackendApi::kUnsupported; + AnyTextureInfoData fData; + + // Derived properties from the backend data, cached to avoid a virtual function call + TextureFormat fViewFormat; Protected fProtected = Protected::kNo; - - AnyTextureInfoData fTextureInfoData; }; -} // namespace skgpu::graphite +} // namespace skgpu::graphite -#endif //skgpu_graphite_TextureInfo_DEFINED +#endif // skgpu_graphite_TextureInfo_DEFINED diff --git a/gfx/skia/skia/include/gpu/graphite/YUVABackendTextures.h b/gfx/skia/skia/include/gpu/graphite/YUVABackendTextures.h index 0298af3602d9..f6d3cceb9dd5 100644 --- a/gfx/skia/skia/include/gpu/graphite/YUVABackendTextures.h +++ b/gfx/skia/skia/include/gpu/graphite/YUVABackendTextures.h @@ -37,10 +37,15 @@ public: * by Mipmapped. This will produce an invalid result (return false from isValid()) if the * passed formats' channels don't agree with SkYUVAInfo. */ - YUVABackendTextureInfo(const Recorder*, - const SkYUVAInfo&, + YUVABackendTextureInfo(const SkYUVAInfo&, SkSpan, Mipmapped); + // DEPRECATED: No more need for a Recorder to construct YUVABackendTextureInfo + YUVABackendTextureInfo(Recorder*, + const SkYUVAInfo& yuvaInfo, + SkSpan textures, + Mipmapped mipmapped) + : YUVABackendTextureInfo(yuvaInfo, textures, mipmapped) {} bool operator==(const YUVABackendTextureInfo&) const; bool operator!=(const YUVABackendTextureInfo& that) const { return !(*this == that); } @@ -95,9 +100,13 @@ public: * indicated by the SkYUVAInfo. This will produce an invalid result (return false from * isValid()) if the passed texture formats' channels don't agree with SkYUVAInfo. */ - YUVABackendTextures(const Recorder*, - const SkYUVAInfo&, + YUVABackendTextures(const SkYUVAInfo&, SkSpan); + // DEPRECATED: No more need for a Recorder to construct YUVABackendTextureInfo + YUVABackendTextures(Recorder*, + const SkYUVAInfo& yuvaInfo, + SkSpan textures) + : YUVABackendTextures(yuvaInfo, textures) {} SkSpan planeTextures() const { return SkSpan(fPlaneTextures); diff --git a/gfx/skia/skia/include/gpu/graphite/dawn/DawnBackendContext.h b/gfx/skia/skia/include/gpu/graphite/dawn/DawnBackendContext.h index 574ffcd6cf7f..659fcd07c251 100644 --- a/gfx/skia/skia/include/gpu/graphite/dawn/DawnBackendContext.h +++ b/gfx/skia/skia/include/gpu/graphite/dawn/DawnBackendContext.h @@ -11,8 +11,13 @@ #include "include/core/SkTypes.h" #include "webgpu/webgpu_cpp.h" // NO_G3_REWRITE +#include + namespace skgpu::graphite { +class Context; +struct ContextOptions; + /** * WebGPU needs to allow the main thread loop to run to detect GPU progress. Dawn native has a * function wgpu::Instance::ProcessEvents, not (currently) present in WebGPU, that can be used to @@ -63,6 +68,10 @@ struct SK_API DawnBackendContext { #endif }; +namespace ContextFactory { +SK_API std::unique_ptr MakeDawn(const DawnBackendContext&, const ContextOptions&); +} // namespace ContextFactory + } // namespace skgpu::graphite #endif // skgpu_graphite_DawnBackendContext_DEFINED diff --git a/gfx/skia/skia/include/gpu/graphite/dawn/DawnGraphiteTypes.h b/gfx/skia/skia/include/gpu/graphite/dawn/DawnGraphiteTypes.h new file mode 100644 index 000000000000..a12769b5bd4d --- /dev/null +++ b/gfx/skia/skia/include/gpu/graphite/dawn/DawnGraphiteTypes.h @@ -0,0 +1,166 @@ +/* + * Copyright 2025 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef skgpu_graphite_DawnTypes_DEFINED +#define skgpu_graphite_DawnTypes_DEFINED + +#include "include/core/SkSize.h" +#include "include/gpu/graphite/GraphiteTypes.h" +#include "include/gpu/graphite/TextureInfo.h" +#include "include/private/base/SkAPI.h" + +#include "webgpu/webgpu_cpp.h" // NO_G3_REWRITE + +class SkStream; +class SkWStream; + +namespace skgpu::graphite { +class BackendTexture; + +class SK_API DawnTextureInfo final : public TextureInfo::Data { +public: + // wgpu::TextureDescriptor properties + wgpu::TextureFormat fFormat = wgpu::TextureFormat::Undefined; + // `fViewFormat` for multiplanar formats corresponds to the plane TextureView's format. + wgpu::TextureFormat fViewFormat = wgpu::TextureFormat::Undefined; + wgpu::TextureUsage fUsage = wgpu::TextureUsage::None; + // TODO(b/308944094): Migrate aspect information to BackendTextureViews. + wgpu::TextureAspect fAspect = wgpu::TextureAspect::All; + uint32_t fSlice = 0; + +#if !defined(__EMSCRIPTEN__) + // The descriptor of the YCbCr info (if any) for this texture. Dawn's YCbCr + // sampling will be used for this texture if this info is set. Setting the + // info is supported only on Android and only if using Vulkan as the + // underlying GPU driver. + wgpu::YCbCrVkDescriptor fYcbcrVkDescriptor = {}; +#endif + + wgpu::TextureFormat getViewFormat() const { + return fViewFormat != wgpu::TextureFormat::Undefined ? fViewFormat : fFormat; + } + + DawnTextureInfo() = default; + + DawnTextureInfo(WGPUTexture texture); + + DawnTextureInfo(uint32_t sampleCount, + Mipmapped mipmapped, + wgpu::TextureFormat format, + wgpu::TextureUsage usage, + wgpu::TextureAspect aspect) + : DawnTextureInfo(sampleCount, + mipmapped, + /*format=*/format, + /*viewFormat=*/format, + usage, + aspect, + /*slice=*/0) {} + + DawnTextureInfo(uint32_t sampleCount, + Mipmapped mipmapped, + wgpu::TextureFormat format, + wgpu::TextureFormat viewFormat, + wgpu::TextureUsage usage, + wgpu::TextureAspect aspect, + uint32_t slice) + : Data(sampleCount, mipmapped) + , fFormat(format) + , fViewFormat(viewFormat) + , fUsage(usage) + , fAspect(aspect) + , fSlice(slice) {} + +#if !defined(__EMSCRIPTEN__) + DawnTextureInfo(uint32_t sampleCount, + Mipmapped mipmapped, + wgpu::TextureFormat format, + wgpu::TextureFormat viewFormat, + wgpu::TextureUsage usage, + wgpu::TextureAspect aspect, + uint32_t slice, + wgpu::YCbCrVkDescriptor ycbcrVkDescriptor) + : Data(sampleCount, mipmapped) + , fFormat(format) + , fViewFormat(viewFormat) + , fUsage(usage) + , fAspect(aspect) + , fSlice(slice) + , fYcbcrVkDescriptor(ycbcrVkDescriptor) {} +#endif + +private: + friend class TextureInfo; + friend class TextureInfoPriv; + + // Non-virtual template API for TextureInfo::Data accessed directly when backend type is known. + static constexpr skgpu::BackendApi kBackend = skgpu::BackendApi::kDawn; + + Protected isProtected() const { return Protected::kNo; } + TextureFormat viewFormat() const; + + bool serialize(SkWStream*) const; + bool deserialize(SkStream*); + + // Virtual API when the specific backend type is not available. + SkString toBackendString() const override; + + void copyTo(TextureInfo::AnyTextureInfoData& dstData) const override { + dstData.emplace(*this); + } + bool isCompatible(const TextureInfo& that, bool requireExact) const override; +}; + +namespace TextureInfos { +SK_API TextureInfo MakeDawn(const DawnTextureInfo& dawnInfo); + +SK_API bool GetDawnTextureInfo(const TextureInfo&, DawnTextureInfo*); +} // namespace TextureInfos + +namespace BackendTextures { +// Create a BackendTexture from a WGPUTexture. Texture info will be queried from the texture. +// +// This is the recommended way of specifying a BackendTexture for Dawn. See the note below on +// the constructor that takes a WGPUTextureView for a fuller explanation. +// +// The BackendTexture will not call retain or release on the passed in WGPUTexture. Thus, the +// client must keep the WGPUTexture valid until they are no longer using the BackendTexture. +// However, any SkImage or SkSurface that wraps the BackendTexture *will* retain and release +// the WGPUTexture. +SK_API BackendTexture MakeDawn(WGPUTexture); + +// Create a BackendTexture from a WGPUTexture. Texture planeDimensions, plane aspect and +// info have to be provided. This is intended to be used only when accessing a plane +// of a WGPUTexture. +// +// The BackendTexture will not call retain or release on the passed in WGPUTexture. Thus, the +// client must keep the WGPUTexture valid until they are no longer using the BackendTexture. +// However, any SkImage or SkSurface that wraps the BackendTexture *will* retain and release +// the WGPUTexture. +SK_API BackendTexture MakeDawn(SkISize planeDimensions, const DawnTextureInfo&, WGPUTexture); + +// Create a BackendTexture from a WGPUTextureView. Texture dimensions and +// info have to be provided. +// +// Using a WGPUTextureView rather than a WGPUTexture is less effecient for operations that +// require buffer transfers to or from the texture (e.g. methods on graphite::Context that read +// pixels or SkSurface::writePixels). In such cases an intermediate copy to or from a +// WGPUTexture is required. Thus, it is recommended to use this functionality only for cases +// where a WGPUTexture is unavailable, in particular when using wgpu::SwapChain. +// +// The BackendTexture will not call retain or release on the passed in WGPUTextureView. Thus, +// the client must keep the WGPUTextureView valid until they are no longer using the +// BackendTexture. However, any SkImage or SkSurface that wraps the BackendTexture *will* retain +// and release the WGPUTextureView. +SK_API BackendTexture MakeDawn(SkISize dimensions, + const DawnTextureInfo& info, + WGPUTextureView textureView); +} // namespace BackendTextures + +} // namespace skgpu::graphite + +#endif // skgpu_graphite_DawnTypes_DEFINED diff --git a/gfx/skia/skia/include/gpu/graphite/dawn/DawnTypes.h b/gfx/skia/skia/include/gpu/graphite/dawn/DawnTypes.h index 005c0da02339..a6208e1e73fd 100644 --- a/gfx/skia/skia/include/gpu/graphite/dawn/DawnTypes.h +++ b/gfx/skia/skia/include/gpu/graphite/dawn/DawnTypes.h @@ -1,147 +1,9 @@ /* - * Copyright 2022 Google LLC. + * Copyright 2022 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ -#ifndef skgpu_graphite_DawnTypes_DEFINED -#define skgpu_graphite_DawnTypes_DEFINED - -#include "include/core/SkSize.h" -#include "include/gpu/graphite/GraphiteTypes.h" -#include "include/private/base/SkAPI.h" - -#include "webgpu/webgpu_cpp.h" // NO_G3_REWRITE - -namespace skgpu::graphite { -class BackendTexture; -class TextureInfo; - -struct DawnTextureInfo { - uint32_t fSampleCount = 1; - Mipmapped fMipmapped = Mipmapped::kNo; - - // wgpu::TextureDescriptor properties - wgpu::TextureFormat fFormat = wgpu::TextureFormat::Undefined; - // `fViewFormat` for multiplanar formats corresponds to the plane TextureView's format. - wgpu::TextureFormat fViewFormat = wgpu::TextureFormat::Undefined; - wgpu::TextureUsage fUsage = wgpu::TextureUsage::None; - // TODO(b/308944094): Migrate aspect information to BackendTextureViews. - wgpu::TextureAspect fAspect = wgpu::TextureAspect::All; - uint32_t fSlice = 0; - -#if !defined(__EMSCRIPTEN__) - // The descriptor of the YCbCr info (if any) for this texture. Dawn's YCbCr - // sampling will be used for this texture if this info is set. Setting the - // info is supported only on Android and only if using Vulkan as the - // underlying GPU driver. - wgpu::YCbCrVkDescriptor fYcbcrVkDescriptor = {}; -#endif - - wgpu::TextureFormat getViewFormat() const { - return fViewFormat != wgpu::TextureFormat::Undefined ? fViewFormat : fFormat; - } - - DawnTextureInfo() = default; - - DawnTextureInfo(uint32_t sampleCount, - Mipmapped mipmapped, - wgpu::TextureFormat format, - wgpu::TextureUsage usage, - wgpu::TextureAspect aspect) - : DawnTextureInfo(sampleCount, - mipmapped, - /*format=*/format, - /*viewFormat=*/format, - usage, - aspect, - /*slice=*/0) {} - - DawnTextureInfo(uint32_t sampleCount, - Mipmapped mipmapped, - wgpu::TextureFormat format, - wgpu::TextureFormat viewFormat, - wgpu::TextureUsage usage, - wgpu::TextureAspect aspect, - uint32_t slice) - : fSampleCount(sampleCount) - , fMipmapped(mipmapped) - , fFormat(format) - , fViewFormat(viewFormat) - , fUsage(usage) - , fAspect(aspect) - , fSlice(slice) {} - -#if !defined(__EMSCRIPTEN__) - DawnTextureInfo(uint32_t sampleCount, - Mipmapped mipmapped, - wgpu::TextureFormat format, - wgpu::TextureFormat viewFormat, - wgpu::TextureUsage usage, - wgpu::TextureAspect aspect, - uint32_t slice, - wgpu::YCbCrVkDescriptor ycbcrVkDescriptor) - : fSampleCount(sampleCount) - , fMipmapped(mipmapped) - , fFormat(format) - , fViewFormat(viewFormat) - , fUsage(usage) - , fAspect(aspect) - , fSlice(slice) - , fYcbcrVkDescriptor(ycbcrVkDescriptor) {} -#endif -}; - -namespace TextureInfos { -SK_API TextureInfo MakeDawn(const DawnTextureInfo& dawnInfo); - -SK_API bool GetDawnTextureInfo(const TextureInfo&, DawnTextureInfo*); -} // namespace TextureInfos - -namespace BackendTextures { -// Create a BackendTexture from a WGPUTexture. Texture info will be queried from the texture. -// -// This is the recommended way of specifying a BackendTexture for Dawn. See the note below on -// the constructor that takes a WGPUTextureView for a fuller explanation. -// -// The BackendTexture will not call retain or release on the passed in WGPUTexture. Thus, the -// client must keep the WGPUTexture valid until they are no longer using the BackendTexture. -// However, any SkImage or SkSurface that wraps the BackendTexture *will* retain and release -// the WGPUTexture. -SK_API BackendTexture MakeDawn(WGPUTexture); - -// Create a BackendTexture from a WGPUTexture. Texture planeDimensions, plane aspect and -// info have to be provided. This is intended to be used only when accessing a plane -// of a WGPUTexture. -// -// The BackendTexture will not call retain or release on the passed in WGPUTexture. Thus, the -// client must keep the WGPUTexture valid until they are no longer using the BackendTexture. -// However, any SkImage or SkSurface that wraps the BackendTexture *will* retain and release -// the WGPUTexture. -SK_API BackendTexture MakeDawn(SkISize planeDimensions, const DawnTextureInfo&, WGPUTexture); - -// Create a BackendTexture from a WGPUTextureView. Texture dimensions and -// info have to be provided. -// -// Using a WGPUTextureView rather than a WGPUTexture is less effecient for operations that -// require buffer transfers to or from the texture (e.g. methods on graphite::Context that read -// pixels or SkSurface::writePixels). In such cases an intermediate copy to or from a -// WGPUTexture is required. Thus, it is recommended to use this functionality only for cases -// where a WGPUTexture is unavailable, in particular when using wgpu::SwapChain. -// -// The BackendTexture will not call retain or release on the passed in WGPUTextureView. Thus, -// the client must keep the WGPUTextureView valid until they are no longer using the -// BackendTexture. However, any SkImage or SkSurface that wraps the BackendTexture *will* retain -// and release the WGPUTextureView. -SK_API BackendTexture MakeDawn(SkISize dimensions, - const DawnTextureInfo& info, - WGPUTextureView textureView); - -} // namespace BackendTextures - -} // namespace skgpu::graphite - -#endif // skgpu_graphite_DawnTypes_DEFINED - - +// DEPRECRATED: DawnTypes.h will be removed in the future, please include DawnGraphiteTypes.h +#include "include/gpu/graphite/dawn/DawnGraphiteTypes.h" diff --git a/gfx/skia/skia/include/gpu/graphite/dawn/DawnUtils.h b/gfx/skia/skia/include/gpu/graphite/dawn/DawnUtils.h index 059d128ef59d..1f58dc6d1c17 100644 --- a/gfx/skia/skia/include/gpu/graphite/dawn/DawnUtils.h +++ b/gfx/skia/skia/include/gpu/graphite/dawn/DawnUtils.h @@ -5,24 +5,5 @@ * found in the LICENSE file. */ -#ifndef skgpu_graphite_DawnUtils_DEFINED -#define skgpu_graphite_DawnUtils_DEFINED - -#include - -#include "include/private/base/SkAPI.h" - -namespace skgpu::graphite { - -class Context; -struct ContextOptions; -struct DawnBackendContext; - -namespace ContextFactory { -SK_API std::unique_ptr MakeDawn(const DawnBackendContext&, const ContextOptions&); -} // namespace ContextFactory - -} // namespace skgpu::graphite - - -#endif // skgpu_graphite_DawnUtils_DEFINED +// DEPRECRATED: DawnUtils.h will be removed in the future, please include DawnBackendContext.h +#include "include/gpu/graphite/dawn/DawnBackendContext.h" diff --git a/gfx/skia/skia/include/gpu/graphite/mtl/MtlBackendContext.h b/gfx/skia/skia/include/gpu/graphite/mtl/MtlBackendContext.h index 4f0bece62a61..1459e9b4430b 100644 --- a/gfx/skia/skia/include/gpu/graphite/mtl/MtlBackendContext.h +++ b/gfx/skia/skia/include/gpu/graphite/mtl/MtlBackendContext.h @@ -23,6 +23,10 @@ struct SK_API MtlBackendContext { sk_cfp fQueue; }; +namespace ContextFactory { +SK_API std::unique_ptr MakeMetal(const MtlBackendContext&, const ContextOptions&); +} // namespace ContextFactory + } // namespace skgpu::graphite #endif // skgpu_graphite_MtlBackendContext_DEFINED diff --git a/gfx/skia/skia/include/gpu/graphite/mtl/MtlGraphiteTypes.h b/gfx/skia/skia/include/gpu/graphite/mtl/MtlGraphiteTypes.h index ebcf1adc54d6..9d542c7cf441 100644 --- a/gfx/skia/skia/include/gpu/graphite/mtl/MtlGraphiteTypes.h +++ b/gfx/skia/skia/include/gpu/graphite/mtl/MtlGraphiteTypes.h @@ -8,31 +8,26 @@ #ifndef skgpu_graphite_MtlGraphiteTypes_DEFINED #define skgpu_graphite_MtlGraphiteTypes_DEFINED +#if __OBJC__ // only works when compiled for Objective C + #include "include/core/SkTypes.h" -#if __OBJC__ // only works when compiled for Objective C -#include "include/gpu/graphite/BackendTexture.h" #include "include/gpu/graphite/GraphiteTypes.h" #include "include/gpu/graphite/TextureInfo.h" -#include "include/ports/SkCFObject.h" +#include "include/gpu/graphite/mtl/MtlGraphiteTypes_cpp.h" #include "include/private/base/SkAPI.h" #import #import #import -#if TARGET_OS_SIMULATOR -#define SK_API_AVAILABLE_CA_METAL_LAYER SK_API_AVAILABLE(macos(10.11), ios(13.0), tvos(13.0)) -#else // TARGET_OS_SIMULATOR -#define SK_API_AVAILABLE_CA_METAL_LAYER SK_API_AVAILABLE(macos(10.11), ios(8.0), tvos(9.0)) -#endif // TARGET_OS_SIMULATOR +class SkStream; +class SkWStream; namespace skgpu::graphite { -struct SK_API MtlTextureInfo { - uint32_t fSampleCount = 1; - skgpu::Mipmapped fMipmapped = skgpu::Mipmapped::kNo; - +class SK_API MtlTextureInfo final : public TextureInfo::Data { +public: MTLPixelFormat fFormat = MTLPixelFormatInvalid; MTLTextureUsage fUsage = MTLTextureUsageUnknown; MTLStorageMode fStorageMode = MTLStorageModeShared; @@ -46,38 +41,35 @@ struct SK_API MtlTextureInfo { MTLTextureUsage usage, MTLStorageMode storageMode, bool framebufferOnly) - : fSampleCount(sampleCount) - , fMipmapped(mipmapped) + : Data(sampleCount, mipmapped) , fFormat(format) , fUsage(usage) , fStorageMode(storageMode) , fFramebufferOnly(framebufferOnly) {} + +private: + friend class TextureInfo; + friend class TextureInfoPriv; + + // Non-virtual template API for TextureInfo::Data accessed directly when backend type is known. + static constexpr skgpu::BackendApi kBackend = skgpu::BackendApi::kMetal; + + Protected isProtected() const { return Protected::kNo; } + TextureFormat viewFormat() const; + + bool serialize(SkWStream*) const; + bool deserialize(SkStream*); + + // Virtual API when the specific backend type is not available. + SkString toBackendString() const override; + + void copyTo(TextureInfo::AnyTextureInfoData& dstData) const override { + dstData.emplace(*this); + } + bool isCompatible(const TextureInfo& that, bool requireExact) const override; }; -namespace TextureInfos { -SK_API TextureInfo MakeMetal(const MtlTextureInfo&); -SK_API TextureInfo MakeMetal(CFTypeRef mtlTexture); - -SK_API bool GetMtlTextureInfo(const TextureInfo&, MtlTextureInfo*); -} // namespace TextureInfos - -namespace BackendTextures { -// The BackendTexture will not call retain or release on the passed in CFTypeRef. Thus the -// client must keep the CFTypeRef valid until they are no longer using the BackendTexture. -SK_API BackendTexture MakeMetal(SkISize dimensions, CFTypeRef mtlTexture); - -SK_API CFTypeRef GetMtlTexture(const BackendTexture&); -} // namespace BackendTextures - -namespace BackendSemaphores { -// TODO(b/286088355) Determine creator's responsibility for setting refcnt. -SK_API BackendSemaphore MakeMetal(CFTypeRef mtlEvent, uint64_t value); - -SK_API CFTypeRef GetMtlEvent(const BackendSemaphore&); -SK_API uint64_t GetMtlValue(const BackendSemaphore&); -} // namespace BackendSemaphores - -} // namespace skgpu::graphite +} // namespace skgpu::graphite #endif // __OBJC__ diff --git a/gfx/skia/skia/include/gpu/graphite/mtl/MtlGraphiteTypesUtils.h b/gfx/skia/skia/include/gpu/graphite/mtl/MtlGraphiteTypesUtils.h new file mode 100644 index 000000000000..5e3bb19b2b21 --- /dev/null +++ b/gfx/skia/skia/include/gpu/graphite/mtl/MtlGraphiteTypesUtils.h @@ -0,0 +1,10 @@ +/* + * Copyright 2022 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +// DEPRECRATED: MtlGraphiteTypesUtils.h will be removed in the future, please include +// MtlGraphiteTypes_cpp.h +#include "include/gpu/graphite/mtl/MtlGraphiteTypes_cpp.h" diff --git a/gfx/skia/skia/include/gpu/graphite/mtl/MtlGraphiteTypes_cpp.h b/gfx/skia/skia/include/gpu/graphite/mtl/MtlGraphiteTypes_cpp.h new file mode 100644 index 000000000000..d7ac5f5535d0 --- /dev/null +++ b/gfx/skia/skia/include/gpu/graphite/mtl/MtlGraphiteTypes_cpp.h @@ -0,0 +1,50 @@ +/* + * Copyright 2024 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef skgpu_graphite_MtlGraphiteTypesUtils_DEFINED +#define skgpu_graphite_MtlGraphiteTypesUtils_DEFINED + +#include "include/core/SkTypes.h" + +#include "include/gpu/graphite/BackendTexture.h" +#include "include/gpu/graphite/TextureInfo.h" +#include "include/private/base/SkAPI.h" + +#import + +namespace skgpu::graphite { + +// MtlTextureInfo requires compiling with Objective-C, so this header is split to allow invoking +// the various backend wrapping APIs from a C++-only compilation unit. +class SK_API MtlTextureInfo; + +namespace TextureInfos { +SK_API TextureInfo MakeMetal(const MtlTextureInfo&); +SK_API TextureInfo MakeMetal(CFTypeRef mtlTexture); + +SK_API bool GetMtlTextureInfo(const TextureInfo&, MtlTextureInfo*); +} // namespace TextureInfos + +namespace BackendTextures { +// The BackendTexture will not call retain or release on the passed in CFTypeRef. Thus the +// client must keep the CFTypeRef valid until they are no longer using the BackendTexture. +SK_API BackendTexture MakeMetal(SkISize dimensions, CFTypeRef mtlTexture); + +SK_API CFTypeRef GetMtlTexture(const BackendTexture&); +} // namespace BackendTextures + +namespace BackendSemaphores { +// TODO(b/286088355) Determine creator's responsibility for setting refcnt. +SK_API BackendSemaphore MakeMetal(CFTypeRef mtlEvent, uint64_t value); + +SK_API CFTypeRef GetMtlEvent(const BackendSemaphore&); +SK_API uint64_t GetMtlValue(const BackendSemaphore&); +} // namespace BackendSemaphores + +} // namespace skgpu::graphite + +#endif // skgpu_graphite_MtlGraphiteTypesUtils_DEFINED diff --git a/gfx/skia/skia/include/gpu/graphite/mtl/MtlGraphiteUtils.h b/gfx/skia/skia/include/gpu/graphite/mtl/MtlGraphiteUtils.h index cd7837f86c23..1ce74f8b53c9 100644 --- a/gfx/skia/skia/include/gpu/graphite/mtl/MtlGraphiteUtils.h +++ b/gfx/skia/skia/include/gpu/graphite/mtl/MtlGraphiteUtils.h @@ -5,23 +5,5 @@ * found in the LICENSE file. */ -#ifndef skgpu_graphite_MtlGraphiteUtils_DEFINED -#define skgpu_graphite_MtlGraphiteUtils_DEFINED - -#include - -#include "include/private/base/SkAPI.h" - -namespace skgpu::graphite { - -class Context; -struct ContextOptions; -struct MtlBackendContext; - -namespace ContextFactory { -SK_API std::unique_ptr MakeMetal(const MtlBackendContext&, const ContextOptions&); -} // namespace ContextFactory - -} // namespace skgpu::graphite - -#endif // skgpu_graphite_MtlGraphiteUtils_DEFINED +// DEPRECRATED: MtlGraphiteUtils.h will be removed in the future, please include MtlBackendContext.h +#include "include/gpu/graphite/mtl/MtlBackendContext.h" diff --git a/gfx/skia/skia/include/gpu/graphite/precompile/Precompile.h b/gfx/skia/skia/include/gpu/graphite/precompile/Precompile.h index a26c73e8e74c..7331691abe12 100644 --- a/gfx/skia/skia/include/gpu/graphite/precompile/Precompile.h +++ b/gfx/skia/skia/include/gpu/graphite/precompile/Precompile.h @@ -8,13 +8,15 @@ #ifndef skgpu_graphite_precompile_Precompile_DEFINED #define skgpu_graphite_precompile_Precompile_DEFINED +#include "include/core/SkColorSpace.h" +#include "include/core/SkColorType.h" #include "include/core/SkSpan.h" #include "include/gpu/graphite/GraphiteTypes.h" namespace skgpu::graphite { -class Context; class PaintOptions; +class PrecompileContext; /** * Describes the required properties of a RenderPass that will be combined with the @@ -22,9 +24,18 @@ class PaintOptions; * a pipeline. */ struct SK_API RenderPassProperties { - DepthStencilFlags fDSFlags = DepthStencilFlags::kNone; - SkColorType fDstCT = kRGBA_8888_SkColorType; - bool fRequiresMSAA = false; + bool operator==(const RenderPassProperties& other) const { + return fDSFlags == other.fDSFlags && + fDstCT == other.fDstCT && + fRequiresMSAA == other.fRequiresMSAA && + SkColorSpace::Equals(fDstCS.get(), other.fDstCS.get()); + } + bool operator!= (const RenderPassProperties& other) const { return !(*this == other); } + + DepthStencilFlags fDSFlags = DepthStencilFlags::kNone; + SkColorType fDstCT = kRGBA_8888_SkColorType; + sk_sp fDstCS = nullptr; + bool fRequiresMSAA = false; }; /** @@ -33,12 +44,12 @@ struct SK_API RenderPassProperties { * drawing. Graphite will always be able to perform an inline compilation if some SkPaint * combination was omitted from precompilation. * - * @param context the Context to which the actual draws will be submitted + * @param precompileContext thread-safe helper holding required portions of the Context * @param paintOptions captures a set of SkPaints that will be drawn * @param drawTypes communicates which primitives those paints will be drawn with * @param renderPassProperties describes the RenderPasses needed for the desired Pipelines */ -void SK_API Precompile(Context* context, +void SK_API Precompile(PrecompileContext* precompileContext, const PaintOptions& paintOptions, DrawTypeFlags drawTypes, SkSpan renderPassProperties); diff --git a/gfx/skia/skia/include/gpu/graphite/precompile/PrecompileShader.h b/gfx/skia/skia/include/gpu/graphite/precompile/PrecompileShader.h index 374f6bd59406..16054c791b89 100644 --- a/gfx/skia/skia/include/gpu/graphite/precompile/PrecompileShader.h +++ b/gfx/skia/skia/include/gpu/graphite/precompile/PrecompileShader.h @@ -11,6 +11,8 @@ #include "include/gpu/graphite/precompile/PrecompileBase.h" #include "include/core/SkBlendMode.h" +#include "include/core/SkImageInfo.h" +#include "include/effects/SkGradientShader.h" class SkColorSpace; @@ -109,11 +111,13 @@ namespace PrecompileShaders { // time this entry point allows the equivalent precompilation program structure to be created. // Note that this factory is for non-YUV SkImages, the YUVImage factory (below) should be used // to represent the shading and sampling required for YUV images. - SK_API sk_sp Image(); + SK_API sk_sp Image(SkSpan = {}, + SkSpan = {}); // As with the above Image call, raw ImageShaders are usually created via an // SkImage::makeRawShader call. The RawImage call allows the equivalent precompilation // program structure to be created without needing the SkImage. - SK_API sk_sp RawImage(); + SK_API sk_sp RawImage(SkSpan = {}, + SkSpan = {}); // In the main Skia API, the specifics of the SkImage used for the SkImage::makeShader call // can determine whether normal or YUV sampling is required. This entry point allows clients @@ -126,10 +130,14 @@ namespace PrecompileShaders { SK_API sk_sp MakeTurbulence(); // --- This block of four matches all the factories in SkGradientShader (SkGradientShader.h) - SK_API sk_sp LinearGradient(); - SK_API sk_sp RadialGradient(); - SK_API sk_sp TwoPointConicalGradient(); - SK_API sk_sp SweepGradient(); + SK_API sk_sp LinearGradient( + SkGradientShader::Interpolation = SkGradientShader::Interpolation()); + SK_API sk_sp RadialGradient( + SkGradientShader::Interpolation = SkGradientShader::Interpolation()); + SK_API sk_sp TwoPointConicalGradient( + SkGradientShader::Interpolation = SkGradientShader::Interpolation()); + SK_API sk_sp SweepGradient( + SkGradientShader::Interpolation = SkGradientShader::Interpolation()); // Normally, SkPicture shaders are only created via SkPicture::makeShader. Since the // SkPicture to be drawn, most likely, won't be available at precompilation time, this diff --git a/gfx/skia/skia/include/gpu/graphite/vk/VulkanGraphiteContext.h b/gfx/skia/skia/include/gpu/graphite/vk/VulkanGraphiteContext.h new file mode 100644 index 000000000000..0466920800bc --- /dev/null +++ b/gfx/skia/skia/include/gpu/graphite/vk/VulkanGraphiteContext.h @@ -0,0 +1,31 @@ +/* + * Copyright 2022 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef skgpu_graphite_VulkanGraphiteUtils_DEFINED +#define skgpu_graphite_VulkanGraphiteUtils_DEFINED + +#include + +#include "include/private/base/SkAPI.h" + +// NOTE: Because there is already a VulkanBackendContext header shared between Ganesh and Graphite, +// this is named VulkanGraphiteContext.h to supply just the factory function (differing from the +// public headers exposed by other backends). +namespace skgpu { struct VulkanBackendContext; } + +namespace skgpu::graphite { + +class Context; +struct ContextOptions; + +namespace ContextFactory { +SK_API std::unique_ptr MakeVulkan(const VulkanBackendContext&, const ContextOptions&); +} // namespace ContextFactory + +} // namespace skgpu::graphite + +#endif // skgpu_graphite_VulkanGraphiteUtils_DEFINED diff --git a/gfx/skia/skia/include/gpu/graphite/vk/VulkanGraphiteTypes.h b/gfx/skia/skia/include/gpu/graphite/vk/VulkanGraphiteTypes.h index ba977dbdf64a..e485321deab5 100644 --- a/gfx/skia/skia/include/gpu/graphite/vk/VulkanGraphiteTypes.h +++ b/gfx/skia/skia/include/gpu/graphite/vk/VulkanGraphiteTypes.h @@ -13,20 +13,21 @@ #include "include/gpu/graphite/TextureInfo.h" #include "include/gpu/vk/VulkanTypes.h" +class SkStream; +class SkWStream; + namespace skgpu::graphite { -struct VulkanTextureInfo { - uint32_t fSampleCount = 1; - Mipmapped fMipmapped = Mipmapped::kNo; - +class SK_API VulkanTextureInfo final : public TextureInfo::Data { +public: // VkImageCreateInfo properties // Currently the only supported flag is VK_IMAGE_CREATE_PROTECTED_BIT. Any other flag will not // be accepted - VkImageCreateFlags fFlags = 0; - VkFormat fFormat = VK_FORMAT_UNDEFINED; - VkImageTiling fImageTiling = VK_IMAGE_TILING_OPTIMAL; - VkImageUsageFlags fImageUsageFlags = 0; - VkSharingMode fSharingMode = VK_SHARING_MODE_EXCLUSIVE; + VkImageCreateFlags fFlags = 0; + VkFormat fFormat = VK_FORMAT_UNDEFINED; + VkImageTiling fImageTiling = VK_IMAGE_TILING_OPTIMAL; + VkImageUsageFlags fImageUsageFlags = 0; + VkSharingMode fSharingMode = VK_SHARING_MODE_EXCLUSIVE; // Properties related to the image view and sampling. These are less inherent properties of the // VkImage but describe how the VkImage should be used within Skia. @@ -35,8 +36,8 @@ struct VulkanTextureInfo { // However, if the VkImage is a Ycbcr format, the client can pass a specific plan here to have // Skia directly sample a plane. In that case the client should also pass in a VkFormat that is // compatible with the plane as described by the Vulkan spec. - VkImageAspectFlags fAspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - VulkanYcbcrConversionInfo fYcbcrConversionInfo; + VkImageAspectFlags fAspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + VulkanYcbcrConversionInfo fYcbcrConversionInfo; VulkanTextureInfo() = default; VulkanTextureInfo(uint32_t sampleCount, @@ -48,8 +49,7 @@ struct VulkanTextureInfo { VkSharingMode sharingMode, VkImageAspectFlags aspectMask, VulkanYcbcrConversionInfo ycbcrConversionInfo) - : fSampleCount(sampleCount) - , fMipmapped(mipmapped) + : Data(sampleCount, mipmapped) , fFlags(flags) , fFormat(format) , fImageTiling(imageTiling) @@ -57,6 +57,29 @@ struct VulkanTextureInfo { , fSharingMode(sharingMode) , fAspectMask(aspectMask) , fYcbcrConversionInfo(ycbcrConversionInfo) {} + +private: + friend class TextureInfo; + friend class TextureInfoPriv; + + // Non-virtual template API for TextureInfo::Data accessed directly when backend type is known. + static constexpr skgpu::BackendApi kBackend = skgpu::BackendApi::kVulkan; + + Protected isProtected() const { + return fFlags & VK_IMAGE_CREATE_PROTECTED_BIT ? Protected::kYes : Protected::kNo; + } + TextureFormat viewFormat() const; + + bool serialize(SkWStream*) const; + bool deserialize(SkStream*); + + // Virtual API when the specific backend type is not available. + SkString toBackendString() const override; + + void copyTo(TextureInfo::AnyTextureInfoData& dstData) const override { + dstData.emplace(*this); + } + bool isCompatible(const TextureInfo& that, bool requireExact) const override; }; namespace TextureInfos { @@ -78,7 +101,6 @@ namespace BackendSemaphores { SK_API BackendSemaphore MakeVulkan(VkSemaphore); SK_API VkSemaphore GetVkSemaphore(const BackendSemaphore&); - } // namespace BackendSemaphores } // namespace skgpu::graphite diff --git a/gfx/skia/skia/include/gpu/graphite/vk/VulkanGraphiteUtils.h b/gfx/skia/skia/include/gpu/graphite/vk/VulkanGraphiteUtils.h index 0cfa0e9bf0a4..7a1902906329 100644 --- a/gfx/skia/skia/include/gpu/graphite/vk/VulkanGraphiteUtils.h +++ b/gfx/skia/skia/include/gpu/graphite/vk/VulkanGraphiteUtils.h @@ -5,24 +5,6 @@ * found in the LICENSE file. */ -#ifndef skgpu_graphite_VulkanGraphiteUtils_DEFINED -#define skgpu_graphite_VulkanGraphiteUtils_DEFINED - -#include - -#include "include/private/base/SkAPI.h" - -namespace skgpu { struct VulkanBackendContext; } - -namespace skgpu::graphite { - -class Context; -struct ContextOptions; - -namespace ContextFactory { -SK_API std::unique_ptr MakeVulkan(const VulkanBackendContext&, const ContextOptions&); -} // namespace ContextFactory - -} // namespace skgpu::graphite - -#endif // skgpu_graphite_VulkanGraphiteUtils_DEFINED +// DEPRECRATED: VulkanGraphiteUtils.h will be removed in the future, please include +// VulkanGraphiteContext.h +#include "include/gpu/graphite/vk/VulkanGraphiteContext.h" diff --git a/gfx/skia/skia/include/pathops/SkPathOps.h b/gfx/skia/skia/include/pathops/SkPathOps.h index 47d2b3118fbf..c4ace7ac6722 100644 --- a/gfx/skia/skia/include/pathops/SkPathOps.h +++ b/gfx/skia/skia/include/pathops/SkPathOps.h @@ -65,7 +65,15 @@ bool SK_API Simplify(const SkPath& path, SkPath* result); @param result The tight bounds of the path. @return True if the bounds could be computed. */ -bool SK_API TightBounds(const SkPath& path, SkRect* result); +[[deprecated]] +static inline bool TightBounds(const SkPath& path, SkRect* result) { + auto rect = path.computeTightBounds(); + if (rect.isFinite()) { + *result = rect; + return true; + } + return false; +} /** Set the result with fill type winding to area equivalent to path. Returns true if successful. Does not detect if path contains contours which diff --git a/gfx/skia/skia/include/ports/SkFontMgr_FontConfigInterface.h b/gfx/skia/skia/include/ports/SkFontMgr_FontConfigInterface.h index 05771257d25f..8fddfb557fdb 100644 --- a/gfx/skia/skia/include/ports/SkFontMgr_FontConfigInterface.h +++ b/gfx/skia/skia/include/ports/SkFontMgr_FontConfigInterface.h @@ -13,8 +13,11 @@ class SkFontMgr; class SkFontConfigInterface; +class SkFontScanner; /** Creates a SkFontMgr which wraps a SkFontConfigInterface. */ SK_API sk_sp SkFontMgr_New_FCI(sk_sp fci); +SK_API sk_sp SkFontMgr_New_FCI(sk_sp fci, + std::unique_ptr scanner); #endif // #ifndef SkFontMgr_FontConfigInterface_DEFINED diff --git a/gfx/skia/skia/include/ports/SkTypeface_fontations.h b/gfx/skia/skia/include/ports/SkTypeface_fontations.h index cd6531ab64d0..03a5e592e92b 100644 --- a/gfx/skia/skia/include/ports/SkTypeface_fontations.h +++ b/gfx/skia/skia/include/ports/SkTypeface_fontations.h @@ -18,4 +18,7 @@ SK_API sk_sp SkTypeface_Make_Fontations(std::unique_ptr fontData, const SkFontArguments& args); +SK_API sk_sp SkTypeface_Make_Fontations(sk_sp fontData, + const SkFontArguments& args); + #endif diff --git a/gfx/skia/skia/include/private/SkJpegMetadataDecoder.h b/gfx/skia/skia/include/private/SkJpegMetadataDecoder.h index 4e91ad495c49..67ce365e6e81 100644 --- a/gfx/skia/skia/include/private/SkJpegMetadataDecoder.h +++ b/gfx/skia/skia/include/private/SkJpegMetadataDecoder.h @@ -86,6 +86,13 @@ public: virtual bool findGainmapImage(sk_sp baseImageData, sk_sp& outGainmapImagedata, SkGainmapInfo& outGainmapInfo) = 0; + + /** + * Return the first JUMBF superbox, if any, and nullptr otherwise. If |copyData| is false, + * then the returned SkData may directly reference the data provided when this object was + * created. + */ + virtual sk_sp getJUMBFMetadata(bool copyData) const = 0; }; #endif diff --git a/gfx/skia/skia/include/private/SkPathRef.h b/gfx/skia/skia/include/private/SkPathRef.h index 13a79a551f16..5f9294f1bd58 100644 --- a/gfx/skia/skia/include/private/SkPathRef.h +++ b/gfx/skia/skia/include/private/SkPathRef.h @@ -28,15 +28,6 @@ class SkMatrix; class SkRRect; -// These are computed from a stream of verbs -struct SkPathVerbAnalysis { - bool valid; - int points, weights; - unsigned segmentMask; -}; -SkPathVerbAnalysis sk_path_analyze_verbs(const uint8_t verbs[], int count); - - /** * Holds the path verbs and points. It is versioned by a generation ID. None of its public methods * modify the contents. To modify or append to the verbs/points wrap the SkPathRef in an @@ -541,7 +532,12 @@ private: void callGenIDChangeListeners(); + PointsArray fPoints; + VerbsArray fVerbs; + ConicWeightsArray fConicWeights; + mutable SkRect fBounds; + SkRect fArcOval; enum { kEmptyGenID = 1, // GenID reserved for path ref with zero points and zero verbs. @@ -549,27 +545,25 @@ private: mutable uint32_t fGenerationID; SkIDChangeListener::List fGenIDChangeListeners; - PointsArray fPoints; - VerbsArray fVerbs; - ConicWeightsArray fConicWeights; - SkDEBUGCODE(std::atomic fEditorsAttached;) // assert only one editor in use at any time. - mutable uint8_t fBoundsIsDirty; - mutable bool fIsFinite; // only meaningful if bounds are valid + SkScalar fArcStartAngle; + SkScalar fArcSweepAngle; PathType fType; - // Both the circle and rrect special cases have a notion of direction and starting point - // The next two variables store that information for either. - bool fRRectOrOvalIsCCW; + + mutable uint8_t fBoundsIsDirty; + uint8_t fRRectOrOvalStartIdx; uint8_t fSegmentMask; // If the path is an arc, these four variables store that information. // We should just store an SkArc, but alignment would cost us 8 more bytes. SkArc::Type fArcType; - SkRect fArcOval; - SkScalar fArcStartAngle; - SkScalar fArcSweepAngle; + + mutable bool fIsFinite; // only meaningful if bounds are valid + // Both the circle and rrect special cases have a notion of direction and starting point + // The next two variables store that information for either. + bool fRRectOrOvalIsCCW; friend class PathRefTest_Private; friend class ForceIsRRect_Private; // unit test isRRect diff --git a/gfx/skia/skia/include/private/base/SkAnySubclass.h b/gfx/skia/skia/include/private/base/SkAnySubclass.h index 2b666cbdb174..0a60ab598439 100644 --- a/gfx/skia/skia/include/private/base/SkAnySubclass.h +++ b/gfx/skia/skia/include/private/base/SkAnySubclass.h @@ -52,6 +52,9 @@ public: fValid = false; } + bool has_value() const { return fValid; } + explicit operator bool() const { return this->has_value(); } + const Base* get() const { SkASSERT(fValid); return std::launder(reinterpret_cast(fData)); @@ -65,6 +68,9 @@ public: Base* operator->() { return this->get(); } const Base* operator->() const { return this->get(); } + Base& operator*() { return *this->get(); } + const Base& operator*() const { return *this->get(); } + private: alignas(8) std::byte fData[Size]; bool fValid = false; diff --git a/gfx/skia/skia/include/private/base/SkAttributes.h b/gfx/skia/skia/include/private/base/SkAttributes.h index f8df5905cde1..ff1adaa9a7ea 100644 --- a/gfx/skia/skia/include/private/base/SkAttributes.h +++ b/gfx/skia/skia/include/private/base/SkAttributes.h @@ -56,20 +56,26 @@ * Used to ignore sanitizer warnings. */ #if !defined(SK_NO_SANITIZE) -# define SK_NO_SANITIZE(A) SK_ATTRIBUTE(no_sanitize(A)) -#endif - -/** - * Helper macro to define no_sanitize attributes only with clang. - */ -#if defined(__clang__) && defined(__has_attribute) - #if __has_attribute(no_sanitize) - #define SK_CLANG_NO_SANITIZE(A) SK_NO_SANITIZE(A) + #if defined(__has_attribute) + #if __has_attribute(no_sanitize) + // This should be for clang and versions of gcc >= 8.0 + #define SK_NO_SANITIZE(A) SK_ATTRIBUTE(no_sanitize(A)) + #else + // For compilers that don't support sanitization, just do nothing. + #define SK_NO_SANITIZE(A) + #endif + #else // no __has_attribute, e.g. MSVC + #define SK_NO_SANITIZE(A) #endif #endif -#if !defined(SK_CLANG_NO_SANITIZE) - #define SK_CLANG_NO_SANITIZE(A) +/** + * Used to ignore CFI sanitizer warnings, supported only by Clang at the moment. + */ +#if defined(__clang__) + #define SK_NO_SANITIZE_CFI SK_NO_SANITIZE("cfi") +#else + #define SK_NO_SANITIZE_CFI #endif /** diff --git a/gfx/skia/skia/include/private/base/SkFloatingPoint.h b/gfx/skia/skia/include/private/base/SkFloatingPoint.h index c3b9ec49dc38..5a1e4e30b7fe 100644 --- a/gfx/skia/skia/include/private/base/SkFloatingPoint.h +++ b/gfx/skia/skia/include/private/base/SkFloatingPoint.h @@ -131,11 +131,7 @@ static constexpr int64_t sk_float_saturate2int64(float x) { // Cast double to float, ignoring any warning about too-large finite values being cast to float. // Clang thinks this is undefined, but it's actually implementation defined to return either // the largest float or infinity (one of the two bracketing representable floats). Good enough! -#ifdef __clang__ SK_NO_SANITIZE("float-cast-overflow") -#elif defined(__GNUC__) -SK_ATTRIBUTE(no_sanitize_undefined) -#endif static constexpr float sk_double_to_float(double x) { return static_cast(x); } @@ -161,20 +157,12 @@ static inline float sk_float_rsqrt (float x) { return 1.0f / std::sqrt(x #pragma warning(push) #pragma warning(disable : 4723) #endif -#ifdef __clang__ SK_NO_SANITIZE("float-divide-by-zero") -#elif defined(__GNUC__) -SK_ATTRIBUTE(no_sanitize_undefined) -#endif static constexpr float sk_ieee_float_divide(float numer, float denom) { return numer / denom; } -#ifdef __clang__ SK_NO_SANITIZE("float-divide-by-zero") -#elif defined(__GNUC__) -SK_ATTRIBUTE(no_sanitize_undefined) -#endif static constexpr double sk_ieee_double_divide(double numer, double denom) { return numer / denom; } diff --git a/gfx/skia/skia/include/private/base/SkMutex.h b/gfx/skia/skia/include/private/base/SkMutex.h index 4452beb912f5..2ebb45bf51ca 100644 --- a/gfx/skia/skia/include/private/base/SkMutex.h +++ b/gfx/skia/skia/include/private/base/SkMutex.h @@ -14,6 +14,14 @@ #include "include/private/base/SkThreadAnnotations.h" #include "include/private/base/SkThreadID.h" +/** + * class SkMutex + * + * This allows us to have a mutex without needing the one in + * the C++ std library which does not work with all clients. + * go/cstyle#Disallowed_Stdlib + */ + class SK_CAPABILITY("mutex") SkMutex { public: constexpr SkMutex() = default; diff --git a/gfx/skia/skia/include/private/base/SkTArray.h b/gfx/skia/skia/include/private/base/SkTArray.h index 879bebe5bb50..a0ec09e31e29 100644 --- a/gfx/skia/skia/include/private/base/SkTArray.h +++ b/gfx/skia/skia/include/private/base/SkTArray.h @@ -617,11 +617,7 @@ private: // unpredictable location in memory. Of course, TArray won't actually use fItemArray in this // way, and we don't want to construct a T before the user requests one. There's no real risk // here, so disable CFI when doing these casts. -#ifdef __clang__ - SK_NO_SANITIZE("cfi") -#elif defined(__GNUC__) - SK_ATTRIBUTE(no_sanitize_undefined) -#endif + SK_NO_SANITIZE_CFI static T* TCast(void* buffer) { return (T*)buffer; } diff --git a/gfx/skia/skia/include/private/base/SkTDArray.h b/gfx/skia/skia/include/private/base/SkTDArray.h index fef454bdc442..9ab141664da9 100644 --- a/gfx/skia/skia/include/private/base/SkTDArray.h +++ b/gfx/skia/skia/include/private/base/SkTDArray.h @@ -13,9 +13,9 @@ #include "include/private/base/SkDebug.h" #include "include/private/base/SkTo.h" -#include #include #include +#include class SK_SPI SkTDStorage { public: diff --git a/gfx/skia/skia/include/private/base/SkTemplates.h b/gfx/skia/skia/include/private/base/SkTemplates.h index 3c9ca5512537..206c737a974c 100644 --- a/gfx/skia/skia/include/private/base/SkTemplates.h +++ b/gfx/skia/skia/include/private/base/SkTemplates.h @@ -15,6 +15,7 @@ #include "include/private/base/SkTLogic.h" #include "include/private/base/SkTo.h" +#include #include #include #include @@ -94,7 +95,8 @@ public: namespace skia_private { -/** Allocate an array of T elements, and free the array in the destructor +/** Allocate an array of T elements on the heap. Once this goes out of scope, the + * elements will be cleaned up "auto"matically. */ template class AutoTArray { public: @@ -164,7 +166,10 @@ private: size_t fSize = 0; }; -/** Wraps AutoTArray, with room for kCountRequested elements preallocated. +/** Like AutoTArray with room for kCountRequested elements preallocated on + * the Stack. If count exceeds the space of the preallocation, the elements + * will live on the heap. Once this goes out of scope, the elements will be + * cleaned up "auto"matically. */ template class AutoSTArray { public: @@ -256,17 +261,25 @@ private: #if defined(SK_BUILD_FOR_GOOGLE3) // Stack frame size is limited for SK_BUILD_FOR_GOOGLE3. 4k is less than the actual max, // but some functions have multiple large stack allocations. - static const int kMaxBytes = 4 * 1024; - static const int kCount = kCountRequested * sizeof(T) > kMaxBytes + static constexpr int kMaxBytes = 4 * 1024; + static constexpr int kMinCount = kCountRequested * sizeof(T) > kMaxBytes ? kMaxBytes / sizeof(T) : kCountRequested; #else - static const int kCount = kCountRequested; + static constexpr int kMinCount = kCountRequested; #endif - int fCount; + // Because we are also storing an int, there is a tiny bit of padding that + // the C++ compiler adds after fStorage if sizeof(T) <= alignof(T*). + // Thus, we can expand how many elements are stored on the stack to make use of this + // (e.g. 1 extra element for 4 byte T if kCountRequested was even). + static_assert(alignof(int) <= alignof(T*) || alignof(int) <= alignof(T)); + static constexpr int kCount = + SkAlignTo(kMinCount*sizeof(T) + sizeof(int), std::max(alignof(T*), alignof(T))) / sizeof(T); + T* fArray; - alignas(T) char fStorage[kCount * sizeof(T)]; + alignas(T) std::byte fStorage[kCount * sizeof(T)]; + int fCount; }; /** Manages an array of T elements, freeing the array in the destructor. @@ -409,16 +422,16 @@ public: private: // Since we use uint32_t storage, we might be able to get more elements for free. - static const size_t kCountWithPadding = SkAlign4(kCountRequested*sizeof(T)) / sizeof(T); + static constexpr size_t kCountWithPadding = SkAlign4(kCountRequested*sizeof(T)) / sizeof(T); #if defined(SK_BUILD_FOR_GOOGLE3) // Stack frame size is limited for SK_BUILD_FOR_GOOGLE3. 4k is less than the actual max, but some functions // have multiple large stack allocations. - static const size_t kMaxBytes = 4 * 1024; - static const size_t kCount = kCountRequested * sizeof(T) > kMaxBytes + static constexpr size_t kMaxBytes = 4 * 1024; + static constexpr size_t kCount = kCountRequested * sizeof(T) > kMaxBytes ? kMaxBytes / sizeof(T) : kCountWithPadding; #else - static const size_t kCount = kCountWithPadding; + static constexpr size_t kCount = kCountWithPadding; #endif T* fPtr; diff --git a/gfx/skia/skia/include/private/chromium/SkPMColor.h b/gfx/skia/skia/include/private/chromium/SkPMColor.h new file mode 100644 index 000000000000..0a47f78f722a --- /dev/null +++ b/gfx/skia/skia/include/private/chromium/SkPMColor.h @@ -0,0 +1,38 @@ +/* + * Copyright 2025 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkPMColor_DEFINED +#define SkPMColor_DEFINED + +#include "include/core/SkColor.h" +#include "include/private/base/SkAPI.h" + +#include + +/** Returns a SkPMColor value from already premultiplied 8-bit component values. + + @param a amount of alpha, from fully transparent (0) to fully opaque (255) + @param r amount of red, from no red (0) to full red (255) + @param g amount of green, from no green (0) to full green (255) + @param b amount of blue, from no blue (0) to full blue (255) + @return premultiplied color +*/ +SK_API SkPMColor SkPMColorSetARGB(SkAlpha a, uint8_t r, uint8_t g, uint8_t b); + +/** Returns alpha component of premultiplied color. */ +SK_API SkAlpha SkPMColorGetA(SkPMColor); + +/** Returns red component of premultiplied color. */ +SK_API uint8_t SkPMColorGetR(SkPMColor); + +/** Returns green component of premultiplied color. */ +SK_API uint8_t SkPMColorGetG(SkPMColor); + +/** Returns blue component of premultiplied color. */ +SK_API uint8_t SkPMColorGetB(SkPMColor); + +#endif diff --git a/gfx/skia/skia/include/private/gpu/ganesh/GrTypesPriv.h b/gfx/skia/skia/include/private/gpu/ganesh/GrTypesPriv.h index 78605e602996..2d3f376c1c50 100644 --- a/gfx/skia/skia/include/private/gpu/ganesh/GrTypesPriv.h +++ b/gfx/skia/skia/include/private/gpu/ganesh/GrTypesPriv.h @@ -539,7 +539,7 @@ SK_MAKE_BITFIELD_CLASS_OPS(GpuPathRenderers) enum class GrColorType { kUnknown, kAlpha_8, - kBGR_565, + kBGR_565, // This corresponds to kRGB_565_SkColorType, which is misnamed. kRGB_565, kABGR_4444, // This name differs from SkColorType. kARGB_4444_SkColorType is misnamed. kRGBA_8888, diff --git a/gfx/skia/skia/include/sksl/SkSLDebugTrace.h b/gfx/skia/skia/include/sksl/SkSLDebugTrace.h index 9c5eafbc94ed..c0f61cb84e50 100644 --- a/gfx/skia/skia/include/sksl/SkSLDebugTrace.h +++ b/gfx/skia/skia/include/sksl/SkSLDebugTrace.h @@ -16,9 +16,6 @@ namespace SkSL { class DebugTrace : public SkRefCnt { public: - /** Serializes a debug trace to JSON which can be parsed by our debugger. */ - virtual void writeTrace(SkWStream* w) const = 0; - /** Generates a human-readable dump of the debug trace. */ virtual void dump(SkWStream* o) const = 0; }; diff --git a/gfx/skia/skia/modules/skcms/README.chromium b/gfx/skia/skia/modules/skcms/README.chromium new file mode 100644 index 000000000000..15543c64fd57 --- /dev/null +++ b/gfx/skia/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/gfx/skia/skia/modules/skcms/skcms.cc b/gfx/skia/skia/modules/skcms/skcms.cc index a9805c404a46..703af72b74ea 100644 --- a/gfx/skia/skia/modules/skcms/skcms.cc +++ b/gfx/skia/skia/modules/skcms/skcms.cc @@ -1464,88 +1464,89 @@ const skcms_ICCProfile* skcms_sRGB_profile() { // We choose to represent sRGB with its canonical transfer function, // and with its canonical XYZD50 gamut matrix. - true, // has_trc, followed by the 3 trc curves - { + { // the 3 trc curves {{0, {2.4f, (float)(1/1.055), (float)(0.055/1.055), (float)(1/12.92), 0.04045f, 0, 0}}}, {{0, {2.4f, (float)(1/1.055), (float)(0.055/1.055), (float)(1/12.92), 0.04045f, 0, 0}}}, {{0, {2.4f, (float)(1/1.055), (float)(0.055/1.055), (float)(1/12.92), 0.04045f, 0, 0}}}, }, - true, // has_toXYZD50, followed by 3x3 toXYZD50 matrix - {{ + {{ // 3x3 toXYZD50 matrix { 0.436065674f, 0.385147095f, 0.143066406f }, { 0.222488403f, 0.716873169f, 0.060607910f }, { 0.013916016f, 0.097076416f, 0.714096069f }, }}, - false, // has_A2B, followed by A2B itself, which we don't care about. - { - 0, - { + { // an empty A2B + { // input_curves {{0, {0,0, 0,0,0,0,0}}}, {{0, {0,0, 0,0,0,0,0}}}, {{0, {0,0, 0,0,0,0,0}}}, {{0, {0,0, 0,0,0,0,0}}}, }, - {0,0,0,0}, - nullptr, - nullptr, + nullptr, // grid_8 + nullptr, // grid_16 + 0, // input_channels + {0,0,0,0}, // grid_points - 0, - { + { // matrix_curves {{0, {0,0, 0,0,0,0,0}}}, {{0, {0,0, 0,0,0,0,0}}}, {{0, {0,0, 0,0,0,0,0}}}, }, - {{ + {{ // matrix (3x4) { 0,0,0,0 }, { 0,0,0,0 }, { 0,0,0,0 }, }}, + 0, // matrix_channels - 0, - { + 0, // output_channels + { // output_curves {{0, {0,0, 0,0,0,0,0}}}, {{0, {0,0, 0,0,0,0,0}}}, {{0, {0,0, 0,0,0,0,0}}}, }, }, - false, // has_B2A, followed by B2A itself, which we also don't care about. - { - 0, - { + { // an empty B2A + { // input_curves {{0, {0,0, 0,0,0,0,0}}}, {{0, {0,0, 0,0,0,0,0}}}, {{0, {0,0, 0,0,0,0,0}}}, }, + 0, // input_channels - 0, - {{ + 0, // matrix_channels + { // matrix_curves + {{0, {0,0, 0,0,0,0,0}}}, + {{0, {0,0, 0,0,0,0,0}}}, + {{0, {0,0, 0,0,0,0,0}}}, + }, + {{ // matrix (3x4) { 0,0,0,0 }, { 0,0,0,0 }, { 0,0,0,0 }, }}, - { - {{0, {0,0, 0,0,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, - { + { // output_curves {{0, {0,0, 0,0,0,0,0}}}, {{0, {0,0, 0,0,0,0,0}}}, {{0, {0,0, 0,0,0,0,0}}}, {{0, {0,0, 0,0,0,0,0}}}, }, + nullptr, // grid_8 + nullptr, // grid_16 + {0,0,0,0}, // grid_points + 0, // output_channels }, - false, // has_CICP, followed by cicp itself which we don't care about. - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, // an empty CICP + + true, // has_trc + true, // has_toXYZD50 + false, // has_A2B + false, // has B2A + false, // has_CICP }; return &sRGB_profile; } @@ -1560,88 +1561,89 @@ const skcms_ICCProfile* skcms_XYZD50_profile() { skcms_Signature_XYZ, // pcs 0, // tag count, moot here - true, // has_trc, followed by the 3 trc curves - { + { // the 3 trc curves {{0, {1,1, 0,0,0,0,0}}}, {{0, {1,1, 0,0,0,0,0}}}, {{0, {1,1, 0,0,0,0,0}}}, }, - true, // has_toXYZD50, followed by 3x3 toXYZD50 matrix - {{ + {{ // 3x3 toXYZD50 matrix { 1,0,0 }, { 0,1,0 }, { 0,0,1 }, }}, - false, // has_A2B, followed by A2B itself, which we don't care about. - { - 0, - { + { // an empty A2B + { // input_curves {{0, {0,0, 0,0,0,0,0}}}, {{0, {0,0, 0,0,0,0,0}}}, {{0, {0,0, 0,0,0,0,0}}}, {{0, {0,0, 0,0,0,0,0}}}, }, - {0,0,0,0}, - nullptr, - nullptr, + nullptr, // grid_8 + nullptr, // grid_16 + 0, // input_channels + {0,0,0,0}, // grid_points - 0, - { + { // matrix_curves {{0, {0,0, 0,0,0,0,0}}}, {{0, {0,0, 0,0,0,0,0}}}, {{0, {0,0, 0,0,0,0,0}}}, }, - {{ + {{ // matrix (3x4) { 0,0,0,0 }, { 0,0,0,0 }, { 0,0,0,0 }, }}, + 0, // matrix_channels - 0, - { + 0, // output_channels + { // output_curves {{0, {0,0, 0,0,0,0,0}}}, {{0, {0,0, 0,0,0,0,0}}}, {{0, {0,0, 0,0,0,0,0}}}, }, }, - false, // has_B2A, followed by B2A itself, which we also don't care about. - { - 0, - { + { // an empty B2A + { // input_curves {{0, {0,0, 0,0,0,0,0}}}, {{0, {0,0, 0,0,0,0,0}}}, {{0, {0,0, 0,0,0,0,0}}}, }, + 0, // input_channels - 0, - {{ + 0, // matrix_channels + { // matrix_curves + {{0, {0,0, 0,0,0,0,0}}}, + {{0, {0,0, 0,0,0,0,0}}}, + {{0, {0,0, 0,0,0,0,0}}}, + }, + {{ // matrix (3x4) { 0,0,0,0 }, { 0,0,0,0 }, { 0,0,0,0 }, }}, - { - {{0, {0,0, 0,0,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, - { + { // output_curves {{0, {0,0, 0,0,0,0,0}}}, {{0, {0,0, 0,0,0,0,0}}}, {{0, {0,0, 0,0,0,0,0}}}, {{0, {0,0, 0,0,0,0,0}}}, }, + nullptr, // grid_8 + nullptr, // grid_16 + {0,0,0,0}, // grid_points + 0, // output_channels }, - false, // has_CICP, followed by cicp itself which we don't care about. - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, // an empty CICP + + true, // has_trc + true, // has_toXYZD50 + false, // has_A2B + false, // has B2A + false, // has_CICP }; return &XYZD50_profile; @@ -2828,25 +2830,26 @@ bool skcms_Transform(const void* src, } 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_GA_88 >> 1: add_op(Op::store_ga88); 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_A_8 >> 1: add_op(Op::store_a8); break; + case skcms_PixelFormat_G_8 >> 1: add_op(Op::store_g8); break; + case skcms_PixelFormat_GA_88 >> 1: add_op(Op::store_ga88); 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_RGBA_10101010_XR >> 1: add_op(Op::store_10101010_XR); break; + case skcms_PixelFormat_RGB_hhh >> 1: add_op(Op::store_hhh); break; + case skcms_PixelFormat_RGBA_hhhh >> 1: add_op(Op::store_hhhh); break; + case skcms_PixelFormat_RGB_fff >> 1: add_op(Op::store_fff); break; + 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()); diff --git a/gfx/skia/skia/modules/skcms/src/Transform_inl.h b/gfx/skia/skia/modules/skcms/src/Transform_inl.h index 99bbbc508de3..bc10e7d47d76 100644 --- a/gfx/skia/skia/modules/skcms/src/Transform_inl.h +++ b/gfx/skia/skia/modules/skcms/src/Transform_inl.h @@ -904,24 +904,19 @@ STAGE(load_1010102, NoCtx) { } STAGE(load_101010x_XR, NoCtx) { - static constexpr float min = -0.752941f; - static constexpr float max = 1.25098f; - static constexpr float range = max - min; U32 rgba = load(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; + r = cast(((rgba >> 0) & 0x3ff) - 384) / 510.0f; + g = cast(((rgba >> 10) & 0x3ff) - 384) / 510.0f; + b = cast(((rgba >> 20) & 0x3ff) - 384) / 510.0f; } STAGE(load_10101010_XR, NoCtx) { - static constexpr float min = -0.752941f; - static constexpr float max = 1.25098f; - static constexpr float range = max - min; U64 rgba = load(src + 8 * i); - r = cast((rgba >> (0+6)) & 0x3ff) * (1/1023.0f) * range + min; - g = cast((rgba >> (16+6)) & 0x3ff) * (1/1023.0f) * range + min; - b = cast((rgba >> (32+6)) & 0x3ff) * (1/1023.0f) * range + min; - a = cast((rgba >> (48+6)) & 0x3ff) * (1/1023.0f) * range + min; + // Each channel is 16 bits, where the 6 low bits are padding. + r = cast(((rgba >> ( 0+6)) & 0x3ff) - 384) / 510.0f; + g = cast(((rgba >> (16+6)) & 0x3ff) - 384) / 510.0f; + b = cast(((rgba >> (32+6)) & 0x3ff) - 384) / 510.0f; + a = cast(((rgba >> (48+6)) & 0x3ff) - 384) / 510.0f; } STAGE(load_161616LE, NoCtx) { @@ -1310,12 +1305,17 @@ FINAL_STAGE(store_8888, NoCtx) { } FINAL_STAGE(store_101010x_XR, NoCtx) { - static constexpr float min = -0.752941f; - static constexpr float max = 1.25098f; - static constexpr float range = max - min; - store(dst + 4*i, cast(to_fixed(((r - min) / range) * 1023)) << 0 - | cast(to_fixed(((g - min) / range) * 1023)) << 10 - | cast(to_fixed(((b - min) / range) * 1023)) << 20); + store(dst + 4*i, cast(to_fixed((r * 510) + 384)) << 0 + | cast(to_fixed((g * 510) + 384)) << 10 + | cast(to_fixed((b * 510) + 384)) << 20); +} + +FINAL_STAGE(store_10101010_XR, NoCtx) { + // Each channel is 16 bits, where the 6 low bits are padding. + store(dst + 8*i, cast(to_fixed((r * 510) + 384)) << ( 0+6) + | cast(to_fixed((g * 510) + 384)) << (16+6) + | cast(to_fixed((b * 510) + 384)) << (32+6) + | cast(to_fixed((a * 510) + 384)) << (48+6)); } FINAL_STAGE(store_1010102, NoCtx) { @@ -1521,7 +1521,7 @@ FINAL_STAGE(store_ffff, NoCtx) { // NOLINTNEXTLINE(misc-definitions-in-headers) void run_program(const Op* program, const void** contexts, SKCMS_MAYBE_UNUSED ptrdiff_t programSize, const char* src, char* dst, int n, - const size_t src_bpp, const size_t dst_bpp) { + size_t src_bpp, size_t dst_bpp) { #if SKCMS_HAS_MUSTTAIL // Convert the program into an array of tailcall stages. StageFn stages[32]; diff --git a/gfx/skia/skia/modules/skcms/src/skcms_Transform.h b/gfx/skia/skia/modules/skcms/src/skcms_Transform.h index d5e542a3a8b1..b2c14e106d19 100644 --- a/gfx/skia/skia/modules/skcms/src/skcms_Transform.h +++ b/gfx/skia/skia/modules/skcms/src/skcms_Transform.h @@ -101,6 +101,7 @@ namespace skcms_private { M(store_161616BE) \ M(store_16161616BE) \ M(store_101010x_XR) \ + M(store_10101010_XR) \ M(store_hhh) \ M(store_hhhh) \ M(store_fff) \ @@ -144,21 +145,21 @@ namespace baseline { void run_program(const Op* program, const void** contexts, ptrdiff_t programSize, const char* src, char* dst, int n, - const size_t src_bpp, const size_t dst_bpp); + size_t src_bpp, size_t dst_bpp); } namespace hsw { void run_program(const Op* program, const void** contexts, ptrdiff_t programSize, const char* src, char* dst, int n, - const size_t src_bpp, const size_t dst_bpp); + size_t src_bpp, size_t dst_bpp); } namespace skx { void run_program(const Op* program, const void** contexts, ptrdiff_t programSize, const char* src, char* dst, int n, - const size_t src_bpp, const size_t dst_bpp); + size_t src_bpp, size_t dst_bpp); } } // namespace skcms_private diff --git a/gfx/skia/skia/modules/skcms/src/skcms_TransformHsw.cc b/gfx/skia/skia/modules/skcms/src/skcms_TransformHsw.cc index cd3673b1b0d9..7349f524f868 100644 --- a/gfx/skia/skia/modules/skcms/src/skcms_TransformHsw.cc +++ b/gfx/skia/skia/modules/skcms/src/skcms_TransformHsw.cc @@ -40,7 +40,7 @@ namespace hsw { void run_program(const Op* program, const void** contexts, ptrdiff_t programSize, const char* src, char* dst, int n, - const size_t src_bpp, const size_t dst_bpp) { + size_t src_bpp, size_t dst_bpp) { skcms_private::baseline::run_program(program, contexts, programSize, src, dst, n, src_bpp, dst_bpp); } diff --git a/gfx/skia/skia/modules/skcms/src/skcms_TransformSkx.cc b/gfx/skia/skia/modules/skcms/src/skcms_TransformSkx.cc index 3e849dd4eccc..8ded464c09ba 100644 --- a/gfx/skia/skia/modules/skcms/src/skcms_TransformSkx.cc +++ b/gfx/skia/skia/modules/skcms/src/skcms_TransformSkx.cc @@ -40,7 +40,7 @@ namespace skx { void run_program(const Op* program, const void** contexts, ptrdiff_t programSize, const char* src, char* dst, int n, - const size_t src_bpp, const size_t dst_bpp) { + size_t src_bpp, size_t dst_bpp) { skcms_private::baseline::run_program(program, contexts, programSize, src, dst, n, src_bpp, dst_bpp); } diff --git a/gfx/skia/skia/modules/skcms/src/skcms_public.h b/gfx/skia/skia/modules/skcms/src/skcms_public.h index eec6e05be951..82ac5a2529ba 100644 --- a/gfx/skia/skia/modules/skcms/src/skcms_public.h +++ b/gfx/skia/skia/modules/skcms/src/skcms_public.h @@ -105,6 +105,9 @@ SKCMS_API bool skcms_TransferFunction_isHLGish (const skcms_TransferFunction*); // Unified representation of 'curv' or 'para' tag data, or a 1D table from 'mft1' or 'mft2' typedef union skcms_Curve { struct { + // this needs to line up with alias_of_table_entries so we can tell if there are or + // are not table entries. If this is 0, this struct is a parametric function, + // otherwise it's a table entry. uint32_t alias_of_table_entries; skcms_TransferFunction parametric; }; @@ -123,44 +126,44 @@ typedef struct skcms_A2B { // Optional: N 1D "A" curves, followed by an N-dimensional CLUT. // If input_channels == 0, these curves and CLUT are skipped, // Otherwise, input_channels must be in [1, 4]. - uint32_t input_channels; skcms_Curve input_curves[4]; - uint8_t grid_points[4]; const uint8_t* grid_8; const uint8_t* grid_16; + uint32_t input_channels; + uint8_t grid_points[4]; // Optional: 3 1D "M" curves, followed by a color matrix. // If matrix_channels == 0, these curves and matrix are skipped, // Otherwise, matrix_channels must be 3. - uint32_t matrix_channels; skcms_Curve matrix_curves[3]; skcms_Matrix3x4 matrix; + uint32_t matrix_channels; // Required: 3 1D "B" curves. Always present, and output_channels must be 3. - uint32_t output_channels; + uint32_t output_channels; // list first to pack with matrix_channels skcms_Curve output_curves[3]; } skcms_A2B; typedef struct skcms_B2A { // Required: 3 1D "B" curves. Always present, and input_channels must be 3. - uint32_t input_channels; skcms_Curve input_curves[3]; + uint32_t input_channels; // Optional: a color matrix, followed by 3 1D "M" curves. // If matrix_channels == 0, this matrix and these curves are skipped, // Otherwise, matrix_channels must be 3. - uint32_t matrix_channels; - skcms_Matrix3x4 matrix; + uint32_t matrix_channels; // list first to pack with input_channels skcms_Curve matrix_curves[3]; + skcms_Matrix3x4 matrix; // Optional: an N-dimensional CLUT, followed by N 1D "A" curves. // If output_channels == 0, this CLUT and these curves are skipped, // Otherwise, output_channels must be in [1, 4]. - uint32_t output_channels; - uint8_t grid_points[4]; + skcms_Curve output_curves[4]; const uint8_t* grid_8; const uint8_t* grid_16; - skcms_Curve output_curves[4]; + uint8_t grid_points[4]; + uint32_t output_channels; } skcms_B2A; typedef struct skcms_CICP { @@ -182,30 +185,31 @@ typedef struct skcms_ICCProfile { // If we can parse red, green and blue transfer curves from the profile, // trc will be set to those three curves, and has_trc will be true. - bool has_trc; skcms_Curve trc[3]; // If this profile's gamut can be represented by a 3x3 transform to XYZD50, // skcms_Parse() sets toXYZD50 to that transform and has_toXYZD50 to true. - bool has_toXYZD50; skcms_Matrix3x3 toXYZD50; // If the profile has a valid A2B0 or A2B1 tag, skcms_Parse() sets A2B to // that data, and has_A2B to true. skcms_ParseWithA2BPriority() does the // same following any user-provided prioritization of A2B0, A2B1, or A2B2. - bool has_A2B; skcms_A2B A2B; // If the profile has a valid B2A0 or B2A1 tag, skcms_Parse() sets B2A to // that data, and has_B2A to true. skcms_ParseWithA2BPriority() does the // same following any user-provided prioritization of B2A0, B2A1, or B2A2. - bool has_B2A; skcms_B2A B2A; // If the profile has a valid CICP tag, skcms_Parse() sets CICP to that data, // and has_CICP to true. - bool has_CICP; skcms_CICP CICP; + + bool has_trc; + bool has_toXYZD50; + bool has_A2B; + bool has_B2A; + bool has_CICP; } skcms_ICCProfile; // The sRGB color profile is so commonly used that we offer a canonical skcms_ICCProfile for it. diff --git a/gfx/skia/skia/modules/skcms/version.sha1 b/gfx/skia/skia/modules/skcms/version.sha1 new file mode 100755 index 000000000000..a7daf9d48ea3 --- /dev/null +++ b/gfx/skia/skia/modules/skcms/version.sha1 @@ -0,0 +1 @@ +1e365691d01ad13edd93056c2731a5c6e0be2a15 diff --git a/gfx/skia/skia/src/base/SkCubics.cpp b/gfx/skia/skia/src/base/SkCubics.cpp index 90de88e397ae..df8347b84989 100644 --- a/gfx/skia/skia/src/base/SkCubics.cpp +++ b/gfx/skia/skia/src/base/SkCubics.cpp @@ -15,8 +15,6 @@ #include #include -static constexpr double PI = 3.141592653589793; - static bool nearly_equal(double x, double y) { if (sk_double_nearly_zero(x)) { return sk_double_nearly_zero(y); @@ -92,11 +90,11 @@ int SkCubics::RootsReal(double A, double B, double C, double D, double solution[ r = neg2RootQ * cos(theta / 3) - adiv3; *roots++ = r; - r = neg2RootQ * cos((theta + 2 * PI) / 3) - adiv3; + r = neg2RootQ * cos((theta + 2 * SK_DoublePI) / 3) - adiv3; if (!nearly_equal(solution[0], r)) { *roots++ = r; } - r = neg2RootQ * cos((theta - 2 * PI) / 3) - adiv3; + r = neg2RootQ * cos((theta - 2 * SK_DoublePI) / 3) - adiv3; if (!nearly_equal(solution[0], r) && (roots - solution == 1 || !nearly_equal(solution[1], r))) { *roots++ = r; diff --git a/gfx/skia/skia/src/base/SkSharedMutex.h b/gfx/skia/skia/src/base/SkSharedMutex.h index dc303e5df004..f480852da299 100644 --- a/gfx/skia/skia/src/base/SkSharedMutex.h +++ b/gfx/skia/skia/src/base/SkSharedMutex.h @@ -25,6 +25,10 @@ // // This lock does not obey strict queue ordering. It will always alternate between readers and // a single writer. +// +// This allows us to have a mutex without needing the one in +// the C++ std library which does not work with all clients. +// go/cstyle#Disallowed_Stdlib class SK_CAPABILITY("mutex") SkSharedMutex { public: SkSharedMutex(); diff --git a/gfx/skia/skia/src/base/SkTDArray.cpp b/gfx/skia/skia/src/base/SkTDArray.cpp index 7c4dbbcf0c4d..06ad1561231e 100644 --- a/gfx/skia/skia/src/base/SkTDArray.cpp +++ b/gfx/skia/skia/src/base/SkTDArray.cpp @@ -16,7 +16,6 @@ #include #include #include -#include SkTDStorage::SkTDStorage(int sizeOfT) : fSizeOfT{sizeOfT} {} diff --git a/gfx/skia/skia/src/base/SkTInternalLList.h b/gfx/skia/skia/src/base/SkTInternalLList.h index 5b655a35eb92..7d80a95b6ff2 100644 --- a/gfx/skia/skia/src/base/SkTInternalLList.h +++ b/gfx/skia/skia/src/base/SkTInternalLList.h @@ -238,7 +238,7 @@ public: /** * C++11 range-for interface. */ - bool operator!=(const Iter& that) { return fCurr != that.fCurr; } + bool operator!=(const Iter& that) const { return fCurr != that.fCurr; } T* operator*() { return this->get(); } void operator++() { this->next(); } diff --git a/gfx/skia/skia/src/base/SkVx.h b/gfx/skia/skia/src/base/SkVx.h index 8b921987e355..8938f55ccf47 100644 --- a/gfx/skia/skia/src/base/SkVx.h +++ b/gfx/skia/skia/src/base/SkVx.h @@ -684,14 +684,12 @@ template SI auto map(std::index_sequence, Fn&& fn, const Args&... args) -> skvx::Vec { auto lane = [&](size_t i) -#if defined(__clang__) // CFI, specifically -fsanitize=cfi-icall, seems to give a false positive here, // with errors like "control flow integrity check for type 'float (float) // noexcept' failed during indirect function call... note: sqrtf.cfi_jt defined // here". But we can be quite sure fn is the right type: it's all inferred! // So, stifle CFI in this function. - __attribute__((no_sanitize("cfi"))) -#endif + SK_NO_SANITIZE_CFI { return fn(args[static_cast(i)]...); }; return { lane(I)... }; diff --git a/gfx/skia/skia/src/base/SkZip.h b/gfx/skia/skia/src/base/SkZip.h index 5ec8a95d219b..3616d0562ca7 100644 --- a/gfx/skia/skia/src/base/SkZip.h +++ b/gfx/skia/skia/src/base/SkZip.h @@ -178,7 +178,7 @@ class SkMakeZipDetail { public: template static constexpr auto MakeZip(Ts&& ... ts) { - + // NOLINTBEGIN // Pick the first collection that has a size, and use that for the size. size_t size = PickOneSize...>::Size(std::forward(ts)...); @@ -197,6 +197,7 @@ public: #endif return SkZip...>(size, Span::Data(std::forward(ts))...); + // NOLINTEND } }; diff --git a/gfx/skia/skia/src/codec/SkAndroidCodec.cpp b/gfx/skia/skia/src/codec/SkAndroidCodec.cpp deleted file mode 100644 index 065b1d7afe0e..000000000000 --- a/gfx/skia/skia/src/codec/SkAndroidCodec.cpp +++ /dev/null @@ -1,560 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "include/codec/SkAndroidCodec.h" - -#include "include/codec/SkCodec.h" -#include "include/codec/SkEncodedImageFormat.h" -#include "include/core/SkAlphaType.h" -#include "include/core/SkColorType.h" -#include "include/core/SkData.h" -#include "include/core/SkRect.h" -#include "include/core/SkStream.h" -#include "modules/skcms/skcms.h" -#include "src/codec/SkAndroidCodecAdapter.h" -#include "src/codec/SkCodecPriv.h" -#include "src/codec/SkSampledCodec.h" - -#include -#include -#include -#include - -class SkPngChunkReader; - -static bool is_valid_sample_size(int sampleSize) { - // FIXME: As Leon has mentioned elsewhere, surely there is also a maximum sampleSize? - return sampleSize > 0; -} - -static bool cicp_get_primaries(uint8_t primaries, skcms_Matrix3x3* sk_primaries) { - // Rec. ITU-T H.273, Table 2. - switch (primaries) { - case 0: - // Reserved. - break; - case 1: - *sk_primaries = SkNamedGamut::kSRGB; - return true; - case 2: - // Unspecified. - break; - case 3: - // Reserved. - break; - case 4: - return skcms_PrimariesToXYZD50( - 0.67f, 0.33f, 0.21f, 0.71f, 0.14f, 0.08f, 0.31f, 0.316f, sk_primaries); - case 5: - return skcms_PrimariesToXYZD50( - 0.64f, 0.33f, 0.29f, 0.60f, 0.15f, 0.06f, 0.3127f, 0.3290f, sk_primaries); - case 6: - return skcms_PrimariesToXYZD50( - 0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f, 0.3127f, 0.3290f, sk_primaries); - case 7: - return skcms_PrimariesToXYZD50( - 0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f, 0.3127f, 0.3290f, sk_primaries); - case 8: - return skcms_PrimariesToXYZD50( - 0.681f, 0.319f, 0.243f, 0.692f, 0.145f, 0.049f, 0.310f, 0.316f, sk_primaries); - case 9: - *sk_primaries = SkNamedGamut::kRec2020; - return true; - case 10: - return skcms_PrimariesToXYZD50( - 1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f / 3.f, 1.f / 3.f, sk_primaries); - case 11: - return skcms_PrimariesToXYZD50( - 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f, 0.314f, 0.351f, sk_primaries); - case 12: - return skcms_PrimariesToXYZD50( - 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f, 0.3127f, 0.3290f, sk_primaries); - case 22: - return skcms_PrimariesToXYZD50( - 0.630f, 0.340f, 0.295f, 0.605f, 0.155f, 0.077f, 0.3127f, 0.3290f, sk_primaries); - default: - // Reserved. - break; - } - *sk_primaries = SkNamedGamut::kSRGB; - return false; -} - -static bool cicp_get_transfer_fn(uint8_t transfer_characteristics, skcms_TransferFunction* trfn) { - // Rec. ITU-T H.273, Table 3. - switch (transfer_characteristics) { - case 0: - // Reserved. - break; - case 1: - *trfn = SkNamedTransferFn::kRec2020; - return true; - case 2: - // Unspecified. - break; - case 3: - // Reserved. - break; - case 4: - *trfn = {2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}; - return true; - case 5: - *trfn = {2.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}; - return true; - case 6: - *trfn = SkNamedTransferFn::kRec2020; - return true; - case 7: - *trfn = {2.222222222222f, - 0.899626676224f, - 0.100373323776f, - 0.25f, - 0.091286342118f, - 0.f, - 0.f}; - return true; - case 8: - *trfn = SkNamedTransferFn::kLinear; - return true; - case 9: - // Logarithmic transfer characteristic (100:1 range). - // Not supported by skcms - break; - case 10: - // Logarithmic transfer characteristic (100 * Sqrt( 10 ) : 1 range). - // Not supported by skcms - break; - case 11: - *trfn = SkNamedTransferFn::kSRGB; - break; - case 12: - // Rec. ITU-R BT.1361-0 extended colour gamut system (historical). - // Same as kRec709 on positive values, differs on negative values. - // Not supported by skcms - break; - case 13: - *trfn = SkNamedTransferFn::kSRGB; - return true; - case 14: - *trfn = SkNamedTransferFn::kRec2020; - return true; - case 15: - *trfn = SkNamedTransferFn::kRec2020; - return true; - case 16: - // Android expects PQ to match 203 nits to SDR white - *trfn = {-2.f, - -1.55522297832f, - 1.86045365631f, - 32 / 2523.0f, - 2413 / 128.0f, - -2392 / 128.0f, - 8192 / 1305.0f}; - return true; - case 17: - *trfn = {2.6f, 1.034080527699f, 0.f, 0.f, 0.f, 0.f, 0.f}; - return true; - case 18: - // Android expects HLG to match 203 nits to SDR white - if (skcms_TransferFunction_makeScaledHLGish(trfn, - 0.314509843f, - 2.f, - 2.f, - 1.f / 0.17883277f, - 0.28466892f, - 0.55991073f)) { - return true; - } - break; - default: - // 19-255 Reserved. - break; - } - - *trfn = SkNamedTransferFn::kSRGB; - return false; -} - -static sk_sp cicp_get_sk_color_space(uint8_t color_primaries, - uint8_t transfer_characteristics, - uint8_t matrix_coefficients, - uint8_t full_range_flag) { - if (matrix_coefficients != 0) return nullptr; - - if (full_range_flag != 1) return nullptr; - - skcms_TransferFunction trfn; - if (!cicp_get_transfer_fn(transfer_characteristics, &trfn)) return nullptr; - - skcms_Matrix3x3 primaries; - if (!cicp_get_primaries(color_primaries, &primaries)) return nullptr; - - return SkColorSpace::MakeRGB(trfn, primaries); -} - -SkAndroidCodec::SkAndroidCodec(SkCodec* codec) - : fInfo(codec->getInfo()) - , fCodec(codec) -{} - -SkAndroidCodec::~SkAndroidCodec() {} - -std::unique_ptr SkAndroidCodec::MakeFromStream(std::unique_ptr stream, - SkPngChunkReader* chunkReader) { - auto codec = SkCodec::MakeFromStream(std::move(stream), nullptr, chunkReader); - return MakeFromCodec(std::move(codec)); -} - -std::unique_ptr SkAndroidCodec::MakeFromCodec(std::unique_ptr codec) { - if (nullptr == codec) { - return nullptr; - } - - const SkEncodedImageFormat format = codec->getEncodedFormat(); - if (format == SkEncodedImageFormat::kAVIF) { - if (SkCodecs::HasDecoder("avif")) { - // If a dedicated AVIF decoder has been registered, SkAvifCodec can - // handle scaling internally. - return std::make_unique(codec.release()); - } - // This will fallback to SkHeifCodec, which needs sampling. - return std::make_unique(codec.release()); - } - - switch (format) { - case SkEncodedImageFormat::kPNG: - case SkEncodedImageFormat::kICO: - case SkEncodedImageFormat::kJPEG: - case SkEncodedImageFormat::kBMP: - case SkEncodedImageFormat::kWBMP: - case SkEncodedImageFormat::kHEIF: - return std::make_unique(codec.release()); - case SkEncodedImageFormat::kGIF: - case SkEncodedImageFormat::kWEBP: - case SkEncodedImageFormat::kDNG: - return std::make_unique(codec.release()); - case SkEncodedImageFormat::kAVIF: // Handled above - case SkEncodedImageFormat::kPKM: - case SkEncodedImageFormat::kKTX: - case SkEncodedImageFormat::kASTC: - case SkEncodedImageFormat::kJPEGXL: - return nullptr; - } - SkUNREACHABLE; -} - -std::unique_ptr SkAndroidCodec::MakeFromData(sk_sp data, - SkPngChunkReader* chunkReader) { - if (!data) { - return nullptr; - } - - return MakeFromStream(SkMemoryStream::Make(std::move(data)), chunkReader); -} - -SkColorType SkAndroidCodec::computeOutputColorType(SkColorType requestedColorType) { - bool highPrecision = fCodec->getEncodedInfo().bitsPerComponent() > 8; - uint8_t colorDepth = fCodec->getEncodedInfo().getColorDepth(); - switch (requestedColorType) { - case kARGB_4444_SkColorType: - return kN32_SkColorType; - case kN32_SkColorType: - break; - case kAlpha_8_SkColorType: - // Fall through to kGray_8. Before kGray_8_SkColorType existed, - // we allowed clients to request kAlpha_8 when they wanted a - // grayscale decode. - case kGray_8_SkColorType: - if (kGray_8_SkColorType == this->getInfo().colorType()) { - return kGray_8_SkColorType; - } - break; - case kRGB_565_SkColorType: - if (kOpaque_SkAlphaType == this->getInfo().alphaType()) { - return kRGB_565_SkColorType; - } - break; - case kRGBA_1010102_SkColorType: - if (colorDepth == 10) { - return kRGBA_1010102_SkColorType; - } - break; - case kRGBA_F16_SkColorType: - return kRGBA_F16_SkColorType; - default: - break; - } - - // F16 is the Android default for high precision images. - return highPrecision ? kRGBA_F16_SkColorType : - (colorDepth == 10 ? kRGBA_1010102_SkColorType : kN32_SkColorType); -} - -SkAlphaType SkAndroidCodec::computeOutputAlphaType(bool requestedUnpremul) { - if (kOpaque_SkAlphaType == this->getInfo().alphaType()) { - return kOpaque_SkAlphaType; - } - return requestedUnpremul ? kUnpremul_SkAlphaType : kPremul_SkAlphaType; -} - -sk_sp SkAndroidCodec::computeOutputColorSpace(SkColorType outputColorType, - sk_sp prefColorSpace) { - switch (outputColorType) { - case kRGBA_F16_SkColorType: - case kRGB_565_SkColorType: - case kRGBA_8888_SkColorType: - case kBGRA_8888_SkColorType: - case kRGBA_1010102_SkColorType: { - // If |prefColorSpace| is supplied, choose it. - if (prefColorSpace) { - return prefColorSpace; - } - - const skcms_ICCProfile* encodedProfile = fCodec->getEncodedInfo().profile(); - if (encodedProfile) { - // Prefer CICP information if it exists. - if (encodedProfile->has_CICP) { - const auto cicpColorSpace = - cicp_get_sk_color_space(encodedProfile->CICP.color_primaries, - encodedProfile->CICP.transfer_characteristics, - encodedProfile->CICP.matrix_coefficients, - encodedProfile->CICP.video_full_range_flag); - if (cicpColorSpace) { - return cicpColorSpace; - } - } - if (auto encodedSpace = SkColorSpace::Make(*encodedProfile)) { - // Leave the pixels in the encoded color space. Color space conversion - // will be handled after decode time. - return encodedSpace; - } - - if (encodedProfile->has_toXYZD50) { - return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, - encodedProfile->toXYZD50); - } - } - - return SkColorSpace::MakeSRGB(); - } - default: - // Color correction not supported for kGray. - return nullptr; - } -} - -static bool supports_any_down_scale(const SkCodec* codec) { - return codec->getEncodedFormat() == SkEncodedImageFormat::kWEBP; -} - -// There are a variety of ways two SkISizes could be compared. This method -// returns true if either dimensions of a is < that of b. -// computeSampleSize also uses the opposite, which means that both -// dimensions of a >= b. -static inline bool smaller_than(const SkISize& a, const SkISize& b) { - return a.width() < b.width() || a.height() < b.height(); -} - -// Both dimensions of a > that of b. -static inline bool strictly_bigger_than(const SkISize& a, const SkISize& b) { - return a.width() > b.width() && a.height() > b.height(); -} - -int SkAndroidCodec::computeSampleSize(SkISize* desiredSize) const { - SkASSERT(desiredSize); - - const auto origDims = fCodec->dimensions(); - if (!desiredSize || *desiredSize == origDims) { - return 1; - } - - if (smaller_than(origDims, *desiredSize)) { - *desiredSize = origDims; - return 1; - } - - // Handle bad input: - if (desiredSize->width() < 1 || desiredSize->height() < 1) { - *desiredSize = SkISize::Make(std::max(1, desiredSize->width()), - std::max(1, desiredSize->height())); - } - - if (supports_any_down_scale(fCodec.get())) { - return 1; - } - - int sampleX = origDims.width() / desiredSize->width(); - int sampleY = origDims.height() / desiredSize->height(); - int sampleSize = std::min(sampleX, sampleY); - auto computedSize = this->getSampledDimensions(sampleSize); - if (computedSize == *desiredSize) { - return sampleSize; - } - - if (computedSize == origDims || sampleSize == 1) { - // Cannot downscale - *desiredSize = computedSize; - return 1; - } - - if (strictly_bigger_than(computedSize, *desiredSize)) { - // See if there is a tighter fit. - while (true) { - auto smaller = this->getSampledDimensions(sampleSize + 1); - if (smaller == *desiredSize) { - return sampleSize + 1; - } - if (smaller == computedSize || smaller_than(smaller, *desiredSize)) { - // Cannot get any smaller without being smaller than desired. - *desiredSize = computedSize; - return sampleSize; - } - - sampleSize++; - computedSize = smaller; - } - - SkASSERT(false); - } - - if (!smaller_than(computedSize, *desiredSize)) { - // This means one of the computed dimensions is equal to desired, and - // the other is bigger. This is as close as we can get. - *desiredSize = computedSize; - return sampleSize; - } - - // computedSize is too small. Make it larger. - while (sampleSize > 2) { - auto bigger = this->getSampledDimensions(sampleSize - 1); - if (bigger == *desiredSize || !smaller_than(bigger, *desiredSize)) { - *desiredSize = bigger; - return sampleSize - 1; - } - sampleSize--; - } - - *desiredSize = origDims; - return 1; -} - -SkISize SkAndroidCodec::getSampledDimensions(int sampleSize) const { - if (!is_valid_sample_size(sampleSize)) { - return {0, 0}; - } - - // Fast path for when we are not scaling. - if (1 == sampleSize) { - return fCodec->dimensions(); - } - - return this->onGetSampledDimensions(sampleSize); -} - -bool SkAndroidCodec::getSupportedSubset(SkIRect* desiredSubset) const { - if (!desiredSubset || !is_valid_subset(*desiredSubset, fCodec->dimensions())) { - return false; - } - - return this->onGetSupportedSubset(desiredSubset); -} - -SkISize SkAndroidCodec::getSampledSubsetDimensions(int sampleSize, const SkIRect& subset) const { - if (!is_valid_sample_size(sampleSize)) { - return {0, 0}; - } - - // We require that the input subset is a subset that is supported by SkAndroidCodec. - // We test this by calling getSupportedSubset() and verifying that no modifications - // are made to the subset. - SkIRect copySubset = subset; - if (!this->getSupportedSubset(©Subset) || copySubset != subset) { - return {0, 0}; - } - - // If the subset is the entire image, for consistency, use getSampledDimensions(). - if (fCodec->dimensions() == subset.size()) { - return this->getSampledDimensions(sampleSize); - } - - // This should perhaps call a virtual function, but currently both of our subclasses - // want the same implementation. - return {get_scaled_dimension(subset.width(), sampleSize), - get_scaled_dimension(subset.height(), sampleSize)}; -} - -SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo& requestInfo, - void* requestPixels, size_t requestRowBytes, const AndroidOptions* options) { - if (!requestPixels) { - return SkCodec::kInvalidParameters; - } - if (requestRowBytes < requestInfo.minRowBytes()) { - return SkCodec::kInvalidParameters; - } - - AndroidOptions defaultOptions; - if (!options) { - options = &defaultOptions; - } else { - if (options->fSubset) { - if (!is_valid_subset(*options->fSubset, fCodec->dimensions())) { - return SkCodec::kInvalidParameters; - } - - if (SkIRect::MakeSize(fCodec->dimensions()) == *options->fSubset) { - // The caller wants the whole thing, rather than a subset. Modify - // the AndroidOptions passed to onGetAndroidPixels to not specify - // a subset. - defaultOptions = *options; - defaultOptions.fSubset = nullptr; - options = &defaultOptions; - } - } - } - - // We may need to have handleFrameIndex recursively call this method - // to resolve one frame depending on another. The recursion stops - // when we find a frame which does not require an earlier frame - // e.g. frame->getRequiredFrame() returns kNoFrame - auto getPixelsFn = [&](const SkImageInfo& info, void* pixels, size_t rowBytes, - const SkCodec::Options& opts, int requiredFrame - ) -> SkCodec::Result { - SkAndroidCodec::AndroidOptions prevFrameOptions( - reinterpret_cast(opts)); - prevFrameOptions.fFrameIndex = requiredFrame; - return this->getAndroidPixels(info, pixels, rowBytes, &prevFrameOptions); - }; - if (auto result = fCodec->handleFrameIndex(requestInfo, requestPixels, requestRowBytes, - *options, getPixelsFn); result != SkCodec::kSuccess) { - return result; - } - - return this->onGetAndroidPixels(requestInfo, requestPixels, requestRowBytes, *options); -} - -SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo& info, void* pixels, - size_t rowBytes) { - return this->getAndroidPixels(info, pixels, rowBytes, nullptr); -} - -bool SkAndroidCodec::getGainmapAndroidCodec(SkGainmapInfo* info, - std::unique_ptr* outCodec) { - if (outCodec) { - std::unique_ptr gainmapCodec; - if (!fCodec->onGetGainmapCodec(info, &gainmapCodec)) { - return false; - } - *outCodec = MakeFromCodec(std::move(gainmapCodec)); - return true; - } - return fCodec->onGetGainmapCodec(info, nullptr); -} - -bool SkAndroidCodec::getAndroidGainmap(SkGainmapInfo* info, - std::unique_ptr* outGainmapImageStream) { - return fCodec->onGetGainmapInfo(info, outGainmapImageStream); -} diff --git a/gfx/skia/skia/src/codec/SkAndroidCodecAdapter.cpp b/gfx/skia/skia/src/codec/SkAndroidCodecAdapter.cpp deleted file mode 100644 index 089220fd1fe1..000000000000 --- a/gfx/skia/skia/src/codec/SkAndroidCodecAdapter.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "src/codec/SkAndroidCodecAdapter.h" -#include "src/codec/SkCodecPriv.h" - -struct SkIRect; -struct SkImageInfo; - -SkAndroidCodecAdapter::SkAndroidCodecAdapter(SkCodec* codec) - : INHERITED(codec) -{} - -SkISize SkAndroidCodecAdapter::onGetSampledDimensions(int sampleSize) const { - float scale = get_scale_from_sample_size(sampleSize); - return this->codec()->getScaledDimensions(scale); -} - -bool SkAndroidCodecAdapter::onGetSupportedSubset(SkIRect* desiredSubset) const { - return this->codec()->getValidSubset(desiredSubset); -} - -SkCodec::Result SkAndroidCodecAdapter::onGetAndroidPixels(const SkImageInfo& info, void* pixels, - size_t rowBytes, const AndroidOptions& options) { - return this->codec()->getPixels(info, pixels, rowBytes, &options); -} diff --git a/gfx/skia/skia/src/codec/SkAndroidCodecAdapter.h b/gfx/skia/skia/src/codec/SkAndroidCodecAdapter.h deleted file mode 100644 index 2d453e39f0b5..000000000000 --- a/gfx/skia/skia/src/codec/SkAndroidCodecAdapter.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#ifndef SkAndroidCodecAdapter_DEFINED -#define SkAndroidCodecAdapter_DEFINED - -#include "include/codec/SkAndroidCodec.h" -#include "include/codec/SkCodec.h" -#include "include/core/SkSize.h" - -#include - -struct SkIRect; -struct SkImageInfo; - -/** - * This class wraps SkCodec to implement the functionality of SkAndroidCodec. - * The underlying SkCodec implements sampled decodes. SkCodec's that do not - * implement that are wrapped with SkSampledCodec instead. - */ -class SkAndroidCodecAdapter : public SkAndroidCodec { -public: - - explicit SkAndroidCodecAdapter(SkCodec*); - - ~SkAndroidCodecAdapter() override {} - -protected: - - SkISize onGetSampledDimensions(int sampleSize) const override; - - bool onGetSupportedSubset(SkIRect* desiredSubset) const override; - - SkCodec::Result onGetAndroidPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, - const AndroidOptions& options) override; - -private: - - using INHERITED = SkAndroidCodec; -}; -#endif // SkAndroidCodecAdapter_DEFINED diff --git a/gfx/skia/skia/src/codec/SkAvifCodec.cpp b/gfx/skia/skia/src/codec/SkAvifCodec.cpp deleted file mode 100644 index 272c7b9022eb..000000000000 --- a/gfx/skia/skia/src/codec/SkAvifCodec.cpp +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Copyright 2022 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "src/codec/SkAvifCodec.h" - -#include "include/codec/SkAvifDecoder.h" -#include "include/codec/SkCodec.h" -#include "include/codec/SkCodecAnimation.h" -#include "include/core/SkColorType.h" -#include "include/core/SkImageInfo.h" -#include "include/core/SkSize.h" -#include "include/core/SkStream.h" -#include "include/core/SkTypes.h" -#include "modules/skcms/skcms.h" -#include "src/core/SkStreamPriv.h" - -#include -#include -#include - -#include "avif/avif.h" - -void AvifDecoderDeleter::operator()(avifDecoder* decoder) const { - if (decoder != nullptr) { - avifDecoderDestroy(decoder); - } -} - -bool SkAvifCodec::IsAvif(const void* buffer, size_t bytesRead) { - avifROData avifData = {static_cast(buffer), bytesRead}; - bool isAvif = avifPeekCompatibleFileType(&avifData) == AVIF_TRUE; - if (isAvif) return true; - // Peeking sometimes fails if the ftyp box is too large. Check the signature - // just to be sure. - const char* bytes = static_cast(buffer); - isAvif = bytesRead >= 12 && !memcmp(&bytes[4], "ftyp", 4) && - (!memcmp(&bytes[8], "avif", 4) || !memcmp(&bytes[8], "avis", 4)); - return isAvif; -} - -std::unique_ptr SkAvifCodec::MakeFromStream(std::unique_ptr stream, - Result* result) { - SkASSERT(result); - if (!stream) { - *result = SkCodec::kInvalidInput; - return nullptr; - } - AvifDecoder avifDecoder(avifDecoderCreate()); - if (avifDecoder == nullptr) { - *result = SkCodec::kInternalError; - return nullptr; - } - avifDecoder->ignoreXMP = AVIF_TRUE; - avifDecoder->ignoreExif = AVIF_TRUE; - avifDecoder->allowProgressive = AVIF_FALSE; - avifDecoder->allowIncremental = AVIF_FALSE; - avifDecoder->strictFlags = AVIF_STRICT_DISABLED; - // TODO(vigneshv): Enable threading based on number of CPU cores available. - avifDecoder->maxThreads = 1; - - // libavif needs a contiguous data buffer. - sk_sp data = nullptr; - if (stream->getMemoryBase()) { - // It is safe to make without copy because we'll hold onto the stream. - data = SkData::MakeWithoutCopy(stream->getMemoryBase(), stream->getLength()); - } else { - data = SkCopyStreamToData(stream.get()); - // If we are forced to copy the stream to a data, we can go ahead and - // delete the stream. - stream.reset(nullptr); - } - - avifResult res = avifDecoderSetIOMemory(avifDecoder.get(), data->bytes(), data->size()); - if (res != AVIF_RESULT_OK) { - *result = SkCodec::kInternalError; - return nullptr; - } - - res = avifDecoderParse(avifDecoder.get()); - if (res != AVIF_RESULT_OK) { - *result = SkCodec::kInvalidInput; - return nullptr; - } - - std::unique_ptr profile = nullptr; - // TODO(vigneshv): Get ICC Profile from the avif decoder. - - const int bitsPerComponent = avifDecoder->image->depth > 8 ? 16 : 8; - SkEncodedInfo::Color color; - SkEncodedInfo::Alpha alpha; - if (avifDecoder->alphaPresent) { - color = SkEncodedInfo::kRGBA_Color; - alpha = SkEncodedInfo::kUnpremul_Alpha; - } else { - color = SkEncodedInfo::kRGB_Color; - alpha = SkEncodedInfo::kOpaque_Alpha; - } - SkEncodedInfo info = SkEncodedInfo::Make(avifDecoder->image->width, - avifDecoder->image->height, - color, - alpha, - bitsPerComponent, - std::move(profile), - avifDecoder->image->depth); - bool animation = avifDecoder->imageCount > 1; - *result = kSuccess; - return std::unique_ptr(new SkAvifCodec(std::move(info), - std::move(stream), - std::move(data), - std::move(avifDecoder), - kDefault_SkEncodedOrigin, - animation)); -} - -SkAvifCodec::SkAvifCodec(SkEncodedInfo&& info, - std::unique_ptr stream, - sk_sp data, - AvifDecoder avifDecoder, - SkEncodedOrigin origin, - bool useAnimation) - : INHERITED(std::move(info), skcms_PixelFormat_RGBA_8888, std::move(stream), origin) - , fData(std::move(data)) - , fAvifDecoder(std::move(avifDecoder)) - , fUseAnimation(useAnimation) {} - -int SkAvifCodec::onGetFrameCount() { - if (!fUseAnimation) { - return 1; - } - - if (fFrameHolder.size() == 0) { - if (fAvifDecoder->imageCount <= 1) { - fUseAnimation = false; - return 1; - } - fFrameHolder.reserve(fAvifDecoder->imageCount); - for (int i = 0; i < fAvifDecoder->imageCount; i++) { - Frame* frame = fFrameHolder.appendNewFrame(fAvifDecoder->alphaPresent == AVIF_TRUE); - frame->setXYWH(0, 0, fAvifDecoder->image->width, fAvifDecoder->image->height); - frame->setDisposalMethod(SkCodecAnimation::DisposalMethod::kKeep); - avifImageTiming timing; - avifDecoderNthImageTiming(fAvifDecoder.get(), i, &timing); - frame->setDuration(timing.duration * 1000); - frame->setRequiredFrame(SkCodec::kNoFrame); - frame->setHasAlpha(fAvifDecoder->alphaPresent == AVIF_TRUE); - } - } - - return fFrameHolder.size(); -} - -const SkFrame* SkAvifCodec::FrameHolder::onGetFrame(int i) const { - return static_cast(this->frame(i)); -} - -SkAvifCodec::Frame* SkAvifCodec::FrameHolder::appendNewFrame(bool hasAlpha) { - const int i = this->size(); - fFrames.emplace_back(i, - hasAlpha ? SkEncodedInfo::kUnpremul_Alpha : SkEncodedInfo::kOpaque_Alpha); - return &fFrames[i]; -} - -const SkAvifCodec::Frame* SkAvifCodec::FrameHolder::frame(int i) const { - SkASSERT(i >= 0 && i < this->size()); - return &fFrames[i]; -} - -bool SkAvifCodec::onGetFrameInfo(int i, FrameInfo* frameInfo) const { - if (i >= fFrameHolder.size()) { - return false; - } - - const Frame* frame = fFrameHolder.frame(i); - if (!frame) { - return false; - } - - if (frameInfo) { - frame->fillIn(frameInfo, true); - } - - return true; -} - -int SkAvifCodec::onGetRepetitionCount() { return kRepetitionCountInfinite; } - -SkCodec::Result SkAvifCodec::onGetPixels(const SkImageInfo& dstInfo, - void* dst, - size_t dstRowBytes, - const Options& options, - int* rowsDecoded) { - if (options.fSubset) { - return kUnimplemented; - } - - const SkColorType dstColorType = dstInfo.colorType(); - if (dstColorType != kRGBA_8888_SkColorType && dstColorType != kRGBA_F16_SkColorType) { - // TODO(vigneshv): Check if more color types need to be supported. - // Currently android supports at least RGB565 and BGRA8888 which is not - // supported here. - return kUnimplemented; - } - - avifResult result = avifDecoderNthImage(fAvifDecoder.get(), options.fFrameIndex); - if (result != AVIF_RESULT_OK) { - return kInvalidInput; - } - - if (this->dimensions() != dstInfo.dimensions()) { - result = avifImageScale( - fAvifDecoder->image, dstInfo.width(), dstInfo.height(), &fAvifDecoder->diag); - if (result != AVIF_RESULT_OK) { - return kInvalidInput; - } - } - - avifRGBImage rgbImage; - avifRGBImageSetDefaults(&rgbImage, fAvifDecoder->image); - - if (dstColorType == kRGBA_8888_SkColorType) { - rgbImage.depth = 8; - } else if (dstColorType == kRGBA_F16_SkColorType) { - rgbImage.depth = 16; - rgbImage.isFloat = AVIF_TRUE; - } - - rgbImage.pixels = static_cast(dst); - rgbImage.rowBytes = dstRowBytes; - rgbImage.chromaUpsampling = AVIF_CHROMA_UPSAMPLING_FASTEST; - - result = avifImageYUVToRGB(fAvifDecoder->image, &rgbImage); - if (result != AVIF_RESULT_OK) { - return kInvalidInput; - } - - *rowsDecoded = fAvifDecoder->image->height; - return kSuccess; -} - -namespace SkAvifDecoder { -namespace LibAvif { - -bool IsAvif(const void* data, size_t len) { - return SkAvifCodec::IsAvif(data, len); -} - -std::unique_ptr Decode(std::unique_ptr stream, - SkCodec::Result* outResult, - SkCodecs::DecodeContext) { - SkCodec::Result resultStorage; - if (!outResult) { - outResult = &resultStorage; - } - return SkAvifCodec::MakeFromStream(std::move(stream), outResult); -} - -std::unique_ptr Decode(sk_sp data, - SkCodec::Result* outResult, - SkCodecs::DecodeContext) { - if (!data) { - if (outResult) { - *outResult = SkCodec::kInvalidInput; - } - return nullptr; - } - return Decode(SkMemoryStream::Make(std::move(data)), outResult, nullptr); -} - -} // namespace LibAvif -} // namespace SkAvifDecoder diff --git a/gfx/skia/skia/src/codec/SkAvifCodec.h b/gfx/skia/skia/src/codec/SkAvifCodec.h deleted file mode 100644 index c738553756cd..000000000000 --- a/gfx/skia/skia/src/codec/SkAvifCodec.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2022 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkAvifCodec_DEFINED -#define SkAvifCodec_DEFINED - -#include "include/codec/SkEncodedImageFormat.h" -#include "include/codec/SkEncodedOrigin.h" -#include "include/core/SkData.h" -#include "include/core/SkRefCnt.h" -#include "include/private/SkEncodedInfo.h" -#include "src/codec/SkFrameHolder.h" -#include "src/codec/SkScalingCodec.h" - -#include -#include -#include - -class SkCodec; -class SkStream; -struct SkImageInfo; -struct avifDecoder; -struct AvifDecoderDeleter { - void operator()(avifDecoder* decoder) const; -}; -using AvifDecoder = std::unique_ptr; - -class SkAvifCodec : public SkScalingCodec { -public: - /* - * Returns true if an AVIF image is detected. Returns false otherwise. - */ - static bool IsAvif(const void*, size_t); - - /* - * Assumes IsAvif() was called and it returned true. - */ - static std::unique_ptr MakeFromStream(std::unique_ptr, Result*); - -protected: - Result onGetPixels(const SkImageInfo& dstInfo, - void* dst, - size_t dstRowBytes, - const Options& options, - int* rowsDecoded) override; - - SkEncodedImageFormat onGetEncodedFormat() const override { return SkEncodedImageFormat::kAVIF; } - - int onGetFrameCount() override; - bool onGetFrameInfo(int, FrameInfo*) const override; - int onGetRepetitionCount() override; - const SkFrameHolder* getFrameHolder() const override { return &fFrameHolder; } - -private: - SkAvifCodec(SkEncodedInfo&&, - std::unique_ptr, - sk_sp, - AvifDecoder, - SkEncodedOrigin, - bool); - - // fAvifDecoder has a pointer to this data. This should not be freed until - // the decode is completed. To ensure that, we declare this before - // fAvifDecoder. - sk_sp fData; - - AvifDecoder fAvifDecoder; - bool fUseAnimation; - - class Frame : public SkFrame { - public: - Frame(int i, SkEncodedInfo::Alpha alpha) : INHERITED(i), fReportedAlpha(alpha) {} - - protected: - SkEncodedInfo::Alpha onReportedAlpha() const override { return fReportedAlpha; } - - private: - const SkEncodedInfo::Alpha fReportedAlpha; - - using INHERITED = SkFrame; - }; - - class FrameHolder : public SkFrameHolder { - public: - ~FrameHolder() override {} - void setScreenSize(int w, int h) { - fScreenWidth = w; - fScreenHeight = h; - } - Frame* appendNewFrame(bool hasAlpha); - const Frame* frame(int i) const; - int size() const { return static_cast(fFrames.size()); } - void reserve(int size) { fFrames.reserve(size); } - - protected: - const SkFrame* onGetFrame(int i) const override; - - private: - std::vector fFrames; - }; - - FrameHolder fFrameHolder; - using INHERITED = SkScalingCodec; -}; - -#endif // SkAvifCodec_DEFINED diff --git a/gfx/skia/skia/src/codec/SkBmpBaseCodec.cpp b/gfx/skia/skia/src/codec/SkBmpBaseCodec.cpp deleted file mode 100644 index 7c3947669150..000000000000 --- a/gfx/skia/skia/src/codec/SkBmpBaseCodec.cpp +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#include "src/codec/SkBmpBaseCodec.h" - -#include "include/core/SkStream.h" -#include "include/private/SkEncodedInfo.h" -#include "include/private/base/SkMalloc.h" - -#include - -SkBmpBaseCodec::~SkBmpBaseCodec() {} - -SkBmpBaseCodec::SkBmpBaseCodec(SkEncodedInfo&& info, std::unique_ptr stream, - uint16_t bitsPerPixel, SkCodec::SkScanlineOrder rowOrder) - : INHERITED(std::move(info), std::move(stream), bitsPerPixel, rowOrder) - , fSrcBuffer(sk_malloc_canfail(this->srcRowBytes())) -{} diff --git a/gfx/skia/skia/src/codec/SkBmpBaseCodec.h b/gfx/skia/skia/src/codec/SkBmpBaseCodec.h deleted file mode 100644 index d3a65fc5c5f6..000000000000 --- a/gfx/skia/skia/src/codec/SkBmpBaseCodec.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#ifndef SkBmpBaseCodec_DEFINED -#define SkBmpBaseCodec_DEFINED - -#include "include/codec/SkCodec.h" -#include "include/private/base/SkTemplates.h" -#include "src/codec/SkBmpCodec.h" - -#include -#include - -class SkStream; -struct SkEncodedInfo; - -/* - * Common base class for SkBmpStandardCodec and SkBmpMaskCodec. - */ -class SkBmpBaseCodec : public SkBmpCodec { -public: - ~SkBmpBaseCodec() override; - - /* - * Whether fSrcBuffer was successfully created. - * - * If false, this Codec must not be used. - */ - bool didCreateSrcBuffer() const { return fSrcBuffer != nullptr; } - -protected: - SkBmpBaseCodec(SkEncodedInfo&& info, std::unique_ptr, - uint16_t bitsPerPixel, SkCodec::SkScanlineOrder rowOrder); - - uint8_t* srcBuffer() { return reinterpret_cast(fSrcBuffer.get()); } - -private: - skia_private::UniqueVoidPtr fSrcBuffer; - - using INHERITED = SkBmpCodec; -}; -#endif // SkBmpBaseCodec_DEFINED diff --git a/gfx/skia/skia/src/codec/SkBmpCodec.cpp b/gfx/skia/skia/src/codec/SkBmpCodec.cpp deleted file mode 100644 index 82386df58cf0..000000000000 --- a/gfx/skia/skia/src/codec/SkBmpCodec.cpp +++ /dev/null @@ -1,695 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "src/codec/SkBmpCodec.h" - -#include "include/codec/SkBmpDecoder.h" -#include "include/core/SkData.h" -#include "include/core/SkImageInfo.h" -#include "include/core/SkRefCnt.h" -#include "include/core/SkSize.h" -#include "include/core/SkStream.h" -#include "include/private/SkEncodedInfo.h" -#include "include/private/base/SkAlign.h" -#include "src/codec/SkBmpMaskCodec.h" -#include "src/codec/SkBmpRLECodec.h" -#include "src/codec/SkBmpStandardCodec.h" -#include "src/codec/SkCodecPriv.h" -#include "src/core/SkMasks.h" - -#include -#include -#include - -/* - * Defines the version and type of the second bitmap header - */ -enum BmpHeaderType { - kInfoV1_BmpHeaderType, - kInfoV2_BmpHeaderType, - kInfoV3_BmpHeaderType, - kInfoV4_BmpHeaderType, - kInfoV5_BmpHeaderType, - kOS2V1_BmpHeaderType, - kOS2VX_BmpHeaderType, - kUnknown_BmpHeaderType -}; - -/* - * Possible bitmap compression types - */ -enum BmpCompressionMethod { - kNone_BmpCompressionMethod = 0, - k8BitRLE_BmpCompressionMethod = 1, - k4BitRLE_BmpCompressionMethod = 2, - kBitMasks_BmpCompressionMethod = 3, - kJpeg_BmpCompressionMethod = 4, - kPng_BmpCompressionMethod = 5, - kAlphaBitMasks_BmpCompressionMethod = 6, - kCMYK_BmpCompressionMethod = 11, - kCMYK8BitRLE_BmpCompressionMethod = 12, - kCMYK4BitRLE_BmpCompressionMethod = 13 -}; - -/* - * Used to define the input format of the bmp - */ -enum BmpInputFormat { - kStandard_BmpInputFormat, - kRLE_BmpInputFormat, - kBitMask_BmpInputFormat, - kUnknown_BmpInputFormat -}; - -/* - * Checks the start of the stream to see if the image is a bitmap - */ -bool SkBmpCodec::IsBmp(const void* buffer, size_t bytesRead) { - // TODO: Support "IC", "PT", "CI", "CP", "BA" - const char bmpSig[] = { 'B', 'M' }; - return bytesRead >= sizeof(bmpSig) && !memcmp(buffer, bmpSig, sizeof(bmpSig)); -} - -/* - * Assumes IsBmp was called and returned true - * Creates a bmp decoder - * Reads enough of the stream to determine the image format - */ -std::unique_ptr SkBmpCodec::MakeFromStream(std::unique_ptr stream, - Result* result) { - return SkBmpCodec::MakeFromStream(std::move(stream), result, false); -} - -/* - * Creates a bmp decoder for a bmp embedded in ico - * Reads enough of the stream to determine the image format - */ -std::unique_ptr SkBmpCodec::MakeFromIco(std::unique_ptr stream, Result* result) { - return SkBmpCodec::MakeFromStream(std::move(stream), result, true); -} - -// Header size constants -static constexpr uint32_t kBmpHeaderBytes = 14; -static constexpr uint32_t kBmpHeaderBytesPlusFour = kBmpHeaderBytes + 4; -static constexpr uint32_t kBmpOS2V1Bytes = 12; -static constexpr uint32_t kBmpOS2V2Bytes = 64; -static constexpr uint32_t kBmpInfoBaseBytes = 16; -static constexpr uint32_t kBmpInfoV1Bytes = 40; -static constexpr uint32_t kBmpInfoV2Bytes = 52; -static constexpr uint32_t kBmpInfoV3Bytes = 56; -static constexpr uint32_t kBmpInfoV4Bytes = 108; -static constexpr uint32_t kBmpInfoV5Bytes = 124; -static constexpr uint32_t kBmpMaskBytes = 12; - -static BmpHeaderType get_header_type(size_t infoBytes) { - if (infoBytes >= kBmpInfoBaseBytes) { - // Check the version of the header - switch (infoBytes) { - case kBmpInfoV1Bytes: - return kInfoV1_BmpHeaderType; - case kBmpInfoV2Bytes: - return kInfoV2_BmpHeaderType; - case kBmpInfoV3Bytes: - return kInfoV3_BmpHeaderType; - case kBmpInfoV4Bytes: - return kInfoV4_BmpHeaderType; - case kBmpInfoV5Bytes: - return kInfoV5_BmpHeaderType; - case 16: - case 20: - case 24: - case 28: - case 32: - case 36: - case 42: - case 46: - case 48: - case 60: - case kBmpOS2V2Bytes: - return kOS2VX_BmpHeaderType; - default: - SkCodecPrintf("Error: unknown bmp header format.\n"); - return kUnknown_BmpHeaderType; - } - } if (infoBytes >= kBmpOS2V1Bytes) { - // The OS2V1 is treated separately because it has a unique format - return kOS2V1_BmpHeaderType; - } else { - // There are no valid bmp headers - SkCodecPrintf("Error: second bitmap header size is invalid.\n"); - return kUnknown_BmpHeaderType; - } -} - -SkCodec::Result SkBmpCodec::ReadHeader(SkStream* stream, bool inIco, - std::unique_ptr* codecOut) { - // The total bytes in the bmp file - // We only need to use this value for RLE decoding, so we will only - // check that it is valid in the RLE case. - uint32_t totalBytes; - // The offset from the start of the file where the pixel data begins - uint32_t offset; - // The size of the second (info) header in bytes - uint32_t infoBytes; - - // Bmps embedded in Icos skip the first Bmp header - if (!inIco) { - // Read the first header and the size of the second header - uint8_t hBuffer[kBmpHeaderBytesPlusFour]; - if (stream->read(hBuffer, kBmpHeaderBytesPlusFour) != - kBmpHeaderBytesPlusFour) { - SkCodecPrintf("Error: unable to read first bitmap header.\n"); - return kIncompleteInput; - } - - totalBytes = get_int(hBuffer, 2); - offset = get_int(hBuffer, 10); - if (offset < kBmpHeaderBytes + kBmpOS2V1Bytes) { - SkCodecPrintf("Error: invalid starting location for pixel data\n"); - return kInvalidInput; - } - - // The size of the second (info) header in bytes - // The size is the first field of the second header, so we have already - // read the first four infoBytes. - infoBytes = get_int(hBuffer, 14); - if (infoBytes < kBmpOS2V1Bytes) { - SkCodecPrintf("Error: invalid second header size.\n"); - return kInvalidInput; - } - } else { - // This value is only used by RLE compression. Bmp in Ico files do not - // use RLE. If the compression field is incorrectly signaled as RLE, - // we will catch this and signal an error below. - totalBytes = 0; - - // Bmps in Ico cannot specify an offset. We will always assume that - // pixel data begins immediately after the color table. This value - // will be corrected below. - offset = 0; - - // Read the size of the second header - uint8_t hBuffer[4]; - if (stream->read(hBuffer, 4) != 4) { - SkCodecPrintf("Error: unable to read size of second bitmap header.\n"); - return kIncompleteInput; - } - infoBytes = get_int(hBuffer, 0); - if (infoBytes < kBmpOS2V1Bytes) { - SkCodecPrintf("Error: invalid second header size.\n"); - return kInvalidInput; - } - } - - // Determine image information depending on second header format - const BmpHeaderType headerType = get_header_type(infoBytes); - if (kUnknown_BmpHeaderType == headerType) { - return kInvalidInput; - } - - // We already read the first four bytes of the info header to get the size - const uint32_t infoBytesRemaining = infoBytes - 4; - - // Read the second header - std::unique_ptr iBuffer(new uint8_t[infoBytesRemaining]); - if (stream->read(iBuffer.get(), infoBytesRemaining) != infoBytesRemaining) { - SkCodecPrintf("Error: unable to read second bitmap header.\n"); - return kIncompleteInput; - } - - // The number of bits used per pixel in the pixel data - uint16_t bitsPerPixel; - - // The compression method for the pixel data - uint32_t compression = kNone_BmpCompressionMethod; - - // Number of colors in the color table, defaults to 0 or max (see below) - uint32_t numColors = 0; - - // Bytes per color in the color table, early versions use 3, most use 4 - uint32_t bytesPerColor; - - // The image width and height - int width, height; - - switch (headerType) { - case kInfoV1_BmpHeaderType: - case kInfoV2_BmpHeaderType: - case kInfoV3_BmpHeaderType: - case kInfoV4_BmpHeaderType: - case kInfoV5_BmpHeaderType: - case kOS2VX_BmpHeaderType: - // We check the size of the header before entering the if statement. - // We should not reach this point unless the size is large enough for - // these required fields. - SkASSERT(infoBytesRemaining >= 12); - width = get_int(iBuffer.get(), 0); - height = get_int(iBuffer.get(), 4); - bitsPerPixel = get_short(iBuffer.get(), 10); - - // Some versions do not have these fields, so we check before - // overwriting the default value. - if (infoBytesRemaining >= 16) { - compression = get_int(iBuffer.get(), 12); - if (infoBytesRemaining >= 32) { - numColors = get_int(iBuffer.get(), 28); - } - } - - // All of the headers that reach this point, store color table entries - // using 4 bytes per pixel. - bytesPerColor = 4; - break; - case kOS2V1_BmpHeaderType: - // The OS2V1 is treated separately because it has a unique format - width = (int) get_short(iBuffer.get(), 0); - height = (int) get_short(iBuffer.get(), 2); - bitsPerPixel = get_short(iBuffer.get(), 6); - bytesPerColor = 3; - break; - case kUnknown_BmpHeaderType: - // We'll exit above in this case. - SkASSERT(false); - return kInvalidInput; - } - - // Check for valid dimensions from header - SkCodec::SkScanlineOrder rowOrder = SkCodec::kBottomUp_SkScanlineOrder; - if (height < 0) { - // We can't negate INT32_MIN. - if (height == INT32_MIN) { - return kInvalidInput; - } - - height = -height; - rowOrder = SkCodec::kTopDown_SkScanlineOrder; - } - // The height field for bmp in ico is double the actual height because they - // contain an XOR mask followed by an AND mask - if (inIco) { - height /= 2; - } - - // Arbitrary maximum. Matches Chromium. - constexpr int kMaxDim = 1 << 16; - if (width <= 0 || height <= 0 || width >= kMaxDim || height >= kMaxDim) { - SkCodecPrintf("Error: invalid bitmap dimensions.\n"); - return kInvalidInput; - } - - // Create mask struct - SkMasks::InputMasks inputMasks; - memset(&inputMasks, 0, sizeof(SkMasks::InputMasks)); - - // Determine the input compression format and set bit masks if necessary - uint32_t maskBytes = 0; - BmpInputFormat inputFormat = kUnknown_BmpInputFormat; - switch (compression) { - case kNone_BmpCompressionMethod: - inputFormat = kStandard_BmpInputFormat; - - // In addition to more standard pixel compression formats, bmp supports - // the use of bit masks to determine pixel components. The standard - // format for representing 16-bit colors is 555 (XRRRRRGGGGGBBBBB), - // which does not map well to any Skia color formats. For this reason, - // we will always enable mask mode with 16 bits per pixel. - if (16 == bitsPerPixel) { - inputMasks.red = 0x7C00; - inputMasks.green = 0x03E0; - inputMasks.blue = 0x001F; - inputFormat = kBitMask_BmpInputFormat; - } - break; - case k8BitRLE_BmpCompressionMethod: - if (bitsPerPixel != 8) { - SkCodecPrintf("Warning: correcting invalid bitmap format.\n"); - bitsPerPixel = 8; - } - inputFormat = kRLE_BmpInputFormat; - break; - case k4BitRLE_BmpCompressionMethod: - if (bitsPerPixel != 4) { - SkCodecPrintf("Warning: correcting invalid bitmap format.\n"); - bitsPerPixel = 4; - } - inputFormat = kRLE_BmpInputFormat; - break; - case kAlphaBitMasks_BmpCompressionMethod: - case kBitMasks_BmpCompressionMethod: - // Load the masks - inputFormat = kBitMask_BmpInputFormat; - switch (headerType) { - case kInfoV1_BmpHeaderType: { - // The V1 header stores the bit masks after the header - uint8_t buffer[kBmpMaskBytes]; - if (stream->read(buffer, kBmpMaskBytes) != kBmpMaskBytes) { - SkCodecPrintf("Error: unable to read bit inputMasks.\n"); - return kIncompleteInput; - } - maskBytes = kBmpMaskBytes; - inputMasks.red = get_int(buffer, 0); - inputMasks.green = get_int(buffer, 4); - inputMasks.blue = get_int(buffer, 8); - break; - } - case kInfoV2_BmpHeaderType: - case kInfoV3_BmpHeaderType: - case kInfoV4_BmpHeaderType: - case kInfoV5_BmpHeaderType: - // Header types are matched based on size. If the header - // is V2+, we are guaranteed to be able to read at least - // this size. - SkASSERT(infoBytesRemaining >= 48); - inputMasks.red = get_int(iBuffer.get(), 36); - inputMasks.green = get_int(iBuffer.get(), 40); - inputMasks.blue = get_int(iBuffer.get(), 44); - - if (kInfoV2_BmpHeaderType == headerType || - (kInfoV3_BmpHeaderType == headerType && !inIco)) { - break; - } - - // V3+ bmp files introduce an alpha mask and allow the creator of the image - // to use the alpha channels. However, many of these images leave the - // alpha channel blank and expect to be rendered as opaque. This is the - // case for almost all V3 images, so we ignore the alpha mask. For V4+ - // images in kMask mode, we will use the alpha mask. Additionally, V3 - // bmp-in-ico expect us to use the alpha mask. - // - // skbug.com/4116: We should perhaps also apply the alpha mask in kStandard - // mode. We just haven't seen any images that expect this - // behavior. - // - // Header types are matched based on size. If the header is - // V3+, we are guaranteed to be able to read at least this size. - SkASSERT(infoBytesRemaining >= 52); - inputMasks.alpha = get_int(iBuffer.get(), 48); - break; - case kOS2VX_BmpHeaderType: - // TODO: Decide if we intend to support this. - // It is unsupported in the previous version and - // in chromium. I have not come across a test case - // that uses this format. - SkCodecPrintf("Error: huffman format unsupported.\n"); - return kUnimplemented; - default: - SkCodecPrintf("Error: invalid bmp bit masks header.\n"); - return kInvalidInput; - } - break; - case kJpeg_BmpCompressionMethod: - if (24 == bitsPerPixel) { - inputFormat = kRLE_BmpInputFormat; - break; - } - [[fallthrough]]; - case kPng_BmpCompressionMethod: - // TODO: Decide if we intend to support this. - // It is unsupported in the previous version and - // in chromium. I think it is used mostly for printers. - SkCodecPrintf("Error: compression format not supported.\n"); - return kUnimplemented; - case kCMYK_BmpCompressionMethod: - case kCMYK8BitRLE_BmpCompressionMethod: - case kCMYK4BitRLE_BmpCompressionMethod: - // TODO: Same as above. - SkCodecPrintf("Error: CMYK not supported for bitmap decoding.\n"); - return kUnimplemented; - default: - SkCodecPrintf("Error: invalid format for bitmap decoding.\n"); - return kInvalidInput; - } - iBuffer.reset(); - - // Calculate the number of bytes read so far - const uint32_t bytesRead = kBmpHeaderBytes + infoBytes + maskBytes; - if (!inIco && offset < bytesRead) { - // TODO (msarett): Do we really want to fail if the offset in the header is invalid? - // Seems like we can just assume that the offset is zero and try to decode? - // Maybe we don't want to try to decode corrupt images? - SkCodecPrintf("Error: pixel data offset less than header size.\n"); - return kInvalidInput; - } - - - - switch (inputFormat) { - case kStandard_BmpInputFormat: { - // BMPs are generally opaque, however BMPs-in-ICOs may contain - // a transparency mask after the image. Therefore, we mark the - // alpha as kBinary if the BMP is contained in an ICO. - // We use |isOpaque| to indicate if the BMP itself is opaque. - SkEncodedInfo::Alpha alpha = inIco ? SkEncodedInfo::kBinary_Alpha : - SkEncodedInfo::kOpaque_Alpha; - bool isOpaque = true; - - SkEncodedInfo::Color color; - uint8_t bitsPerComponent; - switch (bitsPerPixel) { - // Palette formats - case 1: - case 2: - case 4: - case 8: - // In the case of ICO, kBGRA is actually the closest match, - // since we will need to apply a transparency mask. - if (inIco) { - color = SkEncodedInfo::kBGRA_Color; - bitsPerComponent = 8; - } else { - color = SkEncodedInfo::kPalette_Color; - bitsPerComponent = (uint8_t) bitsPerPixel; - } - break; - case 24: - // In the case of ICO, kBGRA is actually the closest match, - // since we will need to apply a transparency mask. - color = inIco ? SkEncodedInfo::kBGRA_Color : SkEncodedInfo::kBGR_Color; - bitsPerComponent = 8; - break; - case 32: - // 32-bit BMP-in-ICOs actually use the alpha channel in place of a - // transparency mask. - if (inIco) { - isOpaque = false; - alpha = SkEncodedInfo::kUnpremul_Alpha; - color = SkEncodedInfo::kBGRA_Color; - } else { - color = SkEncodedInfo::kBGRX_Color; - } - bitsPerComponent = 8; - break; - default: - SkCodecPrintf("Error: invalid input value for bits per pixel.\n"); - return kInvalidInput; - } - - if (codecOut) { - // We require streams to have a memory base for Bmp-in-Ico decodes. - SkASSERT(!inIco || nullptr != stream->getMemoryBase()); - - // Set the image info and create a codec. - auto info = SkEncodedInfo::Make(width, height, color, alpha, bitsPerComponent); - *codecOut = std::make_unique(std::move(info), - std::unique_ptr(stream), - bitsPerPixel, numColors, bytesPerColor, - offset - bytesRead, rowOrder, isOpaque, - inIco); - return static_cast(codecOut->get())->didCreateSrcBuffer() - ? kSuccess : kInvalidInput; - } - return kSuccess; - } - - case kBitMask_BmpInputFormat: { - // Bmp-in-Ico must be standard mode - if (inIco) { - SkCodecPrintf("Error: Icos may not use bit mask format.\n"); - return kInvalidInput; - } - - switch (bitsPerPixel) { - case 16: - case 24: - case 32: - break; - default: - SkCodecPrintf("Error: invalid input value for bits per pixel.\n"); - return kInvalidInput; - } - - // Skip to the start of the pixel array. - // We can do this here because there is no color table to read - // in bit mask mode. - if (stream->skip(offset - bytesRead) != offset - bytesRead) { - SkCodecPrintf("Error: unable to skip to image data.\n"); - return kIncompleteInput; - } - - if (codecOut) { - // Check that input bit masks are valid and create the masks object - SkASSERT(bitsPerPixel % 8 == 0); - std::unique_ptr masks(SkMasks::CreateMasks(inputMasks, bitsPerPixel/8)); - if (nullptr == masks) { - SkCodecPrintf("Error: invalid input masks.\n"); - return kInvalidInput; - } - - // Masked bmps are not a great fit for SkEncodedInfo, since they have - // arbitrary component orderings and bits per component. Here we choose - // somewhat reasonable values - it's ok that we don't match exactly - // because SkBmpMaskCodec has its own mask swizzler anyway. - SkEncodedInfo::Color color; - SkEncodedInfo::Alpha alpha; - if (masks->getAlphaMask()) { - color = SkEncodedInfo::kBGRA_Color; - alpha = SkEncodedInfo::kUnpremul_Alpha; - } else { - color = SkEncodedInfo::kBGR_Color; - alpha = SkEncodedInfo::kOpaque_Alpha; - } - auto info = SkEncodedInfo::Make(width, height, color, alpha, 8); - *codecOut = std::make_unique(std::move(info), - std::unique_ptr(stream), bitsPerPixel, - masks.release(), rowOrder); - return static_cast(codecOut->get())->didCreateSrcBuffer() - ? kSuccess : kInvalidInput; - } - return kSuccess; - } - - case kRLE_BmpInputFormat: { - // We should not reach this point without a valid value of bitsPerPixel. - SkASSERT(4 == bitsPerPixel || 8 == bitsPerPixel || 24 == bitsPerPixel); - - // Check for a valid number of total bytes when in RLE mode - if (totalBytes <= offset) { - SkCodecPrintf("Error: RLE requires valid input size.\n"); - return kInvalidInput; - } - - // Bmp-in-Ico must be standard mode - // When inIco is true, this line cannot be reached, since we - // require that RLE Bmps have a valid number of totalBytes, and - // Icos skip the header that contains totalBytes. - SkASSERT(!inIco); - - if (codecOut) { - // RLE inputs may skip pixels, leaving them as transparent. This - // is uncommon, but we cannot be certain that an RLE bmp will be - // opaque or that we will be able to represent it with a palette. - // For that reason, we always indicate that we are kBGRA. - auto info = SkEncodedInfo::Make(width, height, SkEncodedInfo::kBGRA_Color, - SkEncodedInfo::kBinary_Alpha, 8); - *codecOut = std::make_unique(std::move(info), - std::unique_ptr(stream), bitsPerPixel, - numColors, bytesPerColor, offset - bytesRead, - rowOrder); - } - return kSuccess; - } - default: - SkASSERT(false); - return kInvalidInput; - } -} - -/* - * Creates a bmp decoder - * Reads enough of the stream to determine the image format - */ -std::unique_ptr SkBmpCodec::MakeFromStream(std::unique_ptr stream, - Result* result, bool inIco) { - SkASSERT(result); - if (!stream) { - *result = SkCodec::kInvalidInput; - return nullptr; - } - std::unique_ptr codec; - *result = ReadHeader(stream.get(), inIco, &codec); - if (codec) { - // codec has taken ownership of stream, so we do not need to delete it. - stream.release(); - } - return kSuccess == *result ? std::move(codec) : nullptr; -} - -SkBmpCodec::SkBmpCodec(SkEncodedInfo&& info, std::unique_ptr stream, - uint16_t bitsPerPixel, SkCodec::SkScanlineOrder rowOrder) - : INHERITED(std::move(info), kXformSrcColorFormat, std::move(stream)) - , fBitsPerPixel(bitsPerPixel) - , fRowOrder(rowOrder) - , fSrcRowBytes(SkAlign4(compute_row_bytes(this->dimensions().width(), fBitsPerPixel))) - , fXformBuffer(nullptr) -{} - -bool SkBmpCodec::onRewind() { - return SkBmpCodec::ReadHeader(this->stream(), this->inIco(), nullptr) == kSuccess; -} - -int32_t SkBmpCodec::getDstRow(int32_t y, int32_t height) const { - if (SkCodec::kTopDown_SkScanlineOrder == fRowOrder) { - return y; - } - SkASSERT(SkCodec::kBottomUp_SkScanlineOrder == fRowOrder); - return height - y - 1; -} - -SkCodec::Result SkBmpCodec::prepareToDecode(const SkImageInfo& dstInfo, - const SkCodec::Options& options) { - return this->onPrepareToDecode(dstInfo, options); -} - -SkCodec::Result SkBmpCodec::onStartScanlineDecode(const SkImageInfo& dstInfo, - const SkCodec::Options& options) { - return prepareToDecode(dstInfo, options); -} - -int SkBmpCodec::onGetScanlines(void* dst, int count, size_t rowBytes) { - // Create a new image info representing the portion of the image to decode - SkImageInfo rowInfo = this->dstInfo().makeWH(this->dstInfo().width(), count); - - // Decode the requested rows - return this->decodeRows(rowInfo, dst, rowBytes, this->options()); -} - -bool SkBmpCodec::skipRows(int count) { - const size_t bytesToSkip = count * fSrcRowBytes; - return this->stream()->skip(bytesToSkip) == bytesToSkip; -} - -bool SkBmpCodec::onSkipScanlines(int count) { - return this->skipRows(count); -} - -namespace SkBmpDecoder { -bool IsBmp(const void* data, size_t len) { - return SkBmpCodec::IsBmp(data, len); -} - -std::unique_ptr Decode(std::unique_ptr stream, - SkCodec::Result* outResult, - SkCodecs::DecodeContext) { - SkCodec::Result resultStorage; - if (!outResult) { - outResult = &resultStorage; - } - return SkBmpCodec::MakeFromStream(std::move(stream), outResult); -} - -std::unique_ptr Decode(sk_sp data, - SkCodec::Result* outResult, - SkCodecs::DecodeContext) { - if (!data) { - if (outResult) { - *outResult = SkCodec::kInvalidInput; - } - return nullptr; - } - return Decode(SkMemoryStream::Make(std::move(data)), outResult, nullptr); -} -} // namespace SkBmpDecoder diff --git a/gfx/skia/skia/src/codec/SkBmpCodec.h b/gfx/skia/skia/src/codec/SkBmpCodec.h deleted file mode 100644 index 345ddc75a0f8..000000000000 --- a/gfx/skia/skia/src/codec/SkBmpCodec.h +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#ifndef SkBmpCodec_DEFINED -#define SkBmpCodec_DEFINED - -#include "include/codec/SkCodec.h" -#include "include/codec/SkEncodedImageFormat.h" -#include "include/core/SkColorType.h" -#include "include/core/SkTypes.h" -#include "modules/skcms/skcms.h" - -#include -#include -#include - -class SkStream; -struct SkEncodedInfo; -struct SkImageInfo; - -/* - * This class enables code sharing between its bmp codec subclasses. The - * subclasses actually do the work. - */ -class SkBmpCodec : public SkCodec { -public: - static bool IsBmp(const void*, size_t); - - /* - * Assumes IsBmp was called and returned true - * Creates a bmp decoder - * Reads enough of the stream to determine the image format - */ - static std::unique_ptr MakeFromStream(std::unique_ptr, Result*); - - /* - * Creates a bmp decoder for a bmp embedded in ico - * Reads enough of the stream to determine the image format - */ - static std::unique_ptr MakeFromIco(std::unique_ptr, Result*); - -protected: - - SkBmpCodec(SkEncodedInfo&& info, std::unique_ptr, - uint16_t bitsPerPixel, SkCodec::SkScanlineOrder rowOrder); - - SkEncodedImageFormat onGetEncodedFormat() const override { return SkEncodedImageFormat::kBMP; } - - /* - * Read enough of the stream to initialize the SkBmpCodec. - * On kSuccess, if codecOut is not nullptr, it will be set to a new SkBmpCodec. - */ - static Result ReadHeader(SkStream*, bool inIco, std::unique_ptr* codecOut); - - bool onRewind() override; - - /* - * Returns whether this BMP is part of an ICO image. - */ - bool inIco() const { - return this->onInIco(); - } - - virtual bool onInIco() const { - return false; - } - - /* - * Get the destination row number corresponding to the encoded row number. - * For kTopDown, we simply return y, but for kBottomUp, the rows will be - * decoded in reverse order. - * - * @param y Iterates from 0 to height, indicating the current row. - * @param height The height of the current subset of the image that we are - * decoding. This is generally equal to the full height - * when we want to decode the full or one when we are - * sampling. - */ - int32_t getDstRow(int32_t y, int32_t height) const; - - /* - * Accessors used by subclasses - */ - uint16_t bitsPerPixel() const { return fBitsPerPixel; } - SkScanlineOrder onGetScanlineOrder() const override { return fRowOrder; } - size_t srcRowBytes() const { return fSrcRowBytes; } - - /* - * To be overriden by bmp subclasses, which provide unique implementations. - * Performs subclass specific setup. - * - * @param dstInfo Contains output information. Height specifies - * the total number of rows that will be decoded. - * @param options Additonal options to pass to the decoder. - */ - virtual SkCodec::Result onPrepareToDecode(const SkImageInfo& dstInfo, - const SkCodec::Options& options) = 0; - SkCodec::Result prepareToDecode(const SkImageInfo& dstInfo, - const SkCodec::Options& options); - - uint32_t* xformBuffer() const { return fXformBuffer.get(); } - void resetXformBuffer(int count) { fXformBuffer.reset(new uint32_t[count]); } - - /* - * BMPs are typically encoded as BGRA/BGR so this is a more efficient choice - * than RGBA. - */ - inline static constexpr SkColorType kXformSrcColorType = kBGRA_8888_SkColorType; - inline static constexpr auto kXformSrcColorFormat = skcms_PixelFormat_BGRA_8888; - -private: - - /* - * Creates a bmp decoder - * Reads enough of the stream to determine the image format - */ - static std::unique_ptr MakeFromStream(std::unique_ptr, Result*, bool inIco); - - /* - * Decodes the next dstInfo.height() lines. - * - * onGetPixels() uses this for full image decodes. - * SkScaledCodec::onGetPixels() uses the scanline decoder to call this with - * dstInfo.height() = 1, in order to implement sampling. - * A potential future use is to allow the caller to decode a subset of the - * lines in the image. - * - * @param dstInfo Contains output information. Height specifies the - * number of rows to decode at this time. - * @param dst Memory location to store output pixels - * @param dstRowBytes Bytes in a row of the destination - * @return Number of rows successfully decoded - */ - virtual int decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, - const Options& opts) = 0; - - virtual bool skipRows(int count); - - Result onStartScanlineDecode(const SkImageInfo& dstInfo, - const SkCodec::Options&) override; - - int onGetScanlines(void* dst, int count, size_t rowBytes) override; - - bool onSkipScanlines(int count) override; - - const uint16_t fBitsPerPixel; - const SkScanlineOrder fRowOrder; - const size_t fSrcRowBytes; - std::unique_ptr fXformBuffer; - - using INHERITED = SkCodec; -}; - -#endif diff --git a/gfx/skia/skia/src/codec/SkBmpMaskCodec.cpp b/gfx/skia/skia/src/codec/SkBmpMaskCodec.cpp deleted file mode 100644 index 0c392ef04ab5..000000000000 --- a/gfx/skia/skia/src/codec/SkBmpMaskCodec.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "src/codec/SkBmpMaskCodec.h" - -#include "include/core/SkAlphaType.h" -#include "include/core/SkImageInfo.h" -#include "include/core/SkSize.h" -#include "include/core/SkStream.h" -#include "include/private/SkEncodedInfo.h" -#include "include/private/base/SkTemplates.h" -#include "src/codec/SkCodecPriv.h" - -#include - -/* - * Creates an instance of the decoder - */ -SkBmpMaskCodec::SkBmpMaskCodec(SkEncodedInfo&& info, - std::unique_ptr stream, - uint16_t bitsPerPixel, SkMasks* masks, - SkCodec::SkScanlineOrder rowOrder) - : INHERITED(std::move(info), std::move(stream), bitsPerPixel, rowOrder) - , fMasks(masks) - , fMaskSwizzler(nullptr) -{} - -/* - * Initiates the bitmap decode - */ -SkCodec::Result SkBmpMaskCodec::onGetPixels(const SkImageInfo& dstInfo, - void* dst, size_t dstRowBytes, - const Options& opts, - int* rowsDecoded) { - if (opts.fSubset) { - // Subsets are not supported. - return kUnimplemented; - } - if (dstInfo.dimensions() != this->dimensions()) { - SkCodecPrintf("Error: scaling not supported.\n"); - return kInvalidScale; - } - - Result result = this->prepareToDecode(dstInfo, opts); - if (kSuccess != result) { - return result; - } - - int rows = this->decodeRows(dstInfo, dst, dstRowBytes, opts); - if (rows != dstInfo.height()) { - *rowsDecoded = rows; - return kIncompleteInput; - } - return kSuccess; -} - -SkCodec::Result SkBmpMaskCodec::onPrepareToDecode(const SkImageInfo& dstInfo, - const SkCodec::Options& options) { - if (this->colorXform()) { - this->resetXformBuffer(dstInfo.width()); - } - - SkImageInfo swizzlerInfo = dstInfo; - if (this->colorXform()) { - swizzlerInfo = swizzlerInfo.makeColorType(kXformSrcColorType); - if (kPremul_SkAlphaType == dstInfo.alphaType()) { - swizzlerInfo = swizzlerInfo.makeAlphaType(kUnpremul_SkAlphaType); - } - } - - bool srcIsOpaque = this->getEncodedInfo().opaque(); - fMaskSwizzler.reset(SkMaskSwizzler::CreateMaskSwizzler(swizzlerInfo, srcIsOpaque, - fMasks.get(), this->bitsPerPixel(), options)); - SkASSERT(fMaskSwizzler); - - return SkCodec::kSuccess; -} - -/* - * Performs the decoding - */ -int SkBmpMaskCodec::decodeRows(const SkImageInfo& dstInfo, - void* dst, size_t dstRowBytes, - const Options& opts) { - // Iterate over rows of the image - uint8_t* srcRow = this->srcBuffer(); - const int height = dstInfo.height(); - for (int y = 0; y < height; y++) { - // Read a row of the input - if (this->stream()->read(srcRow, this->srcRowBytes()) != this->srcRowBytes()) { - SkCodecPrintf("Warning: incomplete input stream.\n"); - return y; - } - - // Decode the row in destination format - uint32_t row = this->getDstRow(y, height); - void* dstRow = SkTAddOffset(dst, row * dstRowBytes); - - if (this->colorXform()) { - fMaskSwizzler->swizzle(this->xformBuffer(), srcRow); - this->applyColorXform(dstRow, this->xformBuffer(), fMaskSwizzler->swizzleWidth()); - } else { - fMaskSwizzler->swizzle(dstRow, srcRow); - } - } - - // Finished decoding the entire image - return height; -} diff --git a/gfx/skia/skia/src/codec/SkBmpMaskCodec.h b/gfx/skia/skia/src/codec/SkBmpMaskCodec.h deleted file mode 100644 index 18cee5d91424..000000000000 --- a/gfx/skia/skia/src/codec/SkBmpMaskCodec.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkBmpMaskCodec_DEFINED -#define SkBmpMaskCodec_DEFINED - -#include "include/codec/SkCodec.h" -#include "include/core/SkTypes.h" -#include "src/codec/SkBmpBaseCodec.h" -#include "src/codec/SkMaskSwizzler.h" -#include "src/core/SkMasks.h" - -#include -#include -#include - -class SkSampler; -class SkStream; -struct SkEncodedInfo; -struct SkImageInfo; - -/* - * This class implements the decoding for bmp images using bit masks - */ -class SkBmpMaskCodec : public SkBmpBaseCodec { -public: - - /* - * Creates an instance of the decoder - * - * Called only by SkBmpCodec::MakeFromStream - * There should be no other callers despite this being public - * - * @param info contains properties of the encoded data - * @param stream the stream of encoded image data - * @param bitsPerPixel the number of bits used to store each pixel - * @param masks color masks for certain bmp formats - * @param rowOrder indicates whether rows are ordered top-down or bottom-up - */ - SkBmpMaskCodec(SkEncodedInfo&& info, std::unique_ptr, - uint16_t bitsPerPixel, SkMasks* masks, - SkCodec::SkScanlineOrder rowOrder); - -protected: - - Result onGetPixels(const SkImageInfo& dstInfo, void* dst, - size_t dstRowBytes, const Options&, - int*) override; - - SkCodec::Result onPrepareToDecode(const SkImageInfo& dstInfo, - const SkCodec::Options& options) override; - -private: - - SkSampler* getSampler(bool createIfNecessary) override { - SkASSERT(fMaskSwizzler); - return fMaskSwizzler.get(); - } - - int decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, - const Options& opts) override; - - std::unique_ptr fMasks; - std::unique_ptr fMaskSwizzler; - - using INHERITED = SkBmpBaseCodec; -}; -#endif // SkBmpMaskCodec_DEFINED diff --git a/gfx/skia/skia/src/codec/SkBmpRLECodec.cpp b/gfx/skia/skia/src/codec/SkBmpRLECodec.cpp deleted file mode 100644 index f02937c9279f..000000000000 --- a/gfx/skia/skia/src/codec/SkBmpRLECodec.cpp +++ /dev/null @@ -1,581 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "src/codec/SkBmpRLECodec.h" - -#include "include/core/SkAlphaType.h" -#include "include/core/SkColor.h" -#include "include/core/SkColorPriv.h" -#include "include/core/SkColorType.h" -#include "include/core/SkImageInfo.h" -#include "include/core/SkSize.h" -#include "include/core/SkStream.h" -#include "include/private/SkColorData.h" -#include "include/private/SkEncodedInfo.h" -#include "include/private/base/SkAlign.h" -#include "include/private/base/SkMalloc.h" -#include "include/private/base/SkTemplates.h" -#include "src/codec/SkCodecPriv.h" - -#include -#include -#include -#include - -/* - * Creates an instance of the decoder - * Called only by NewFromStream - */ -SkBmpRLECodec::SkBmpRLECodec(SkEncodedInfo&& info, - std::unique_ptr stream, - uint16_t bitsPerPixel, uint32_t numColors, - uint32_t bytesPerColor, uint32_t offset, - SkCodec::SkScanlineOrder rowOrder) - : INHERITED(std::move(info), std::move(stream), bitsPerPixel, rowOrder) - , fColorTable(nullptr) - , fNumColors(numColors) - , fBytesPerColor(bytesPerColor) - , fOffset(offset) - , fBytesBuffered(0) - , fCurrRLEByte(0) - , fSampleX(1) -{} - -/* - * Initiates the bitmap decode - */ -SkCodec::Result SkBmpRLECodec::onGetPixels(const SkImageInfo& dstInfo, - void* dst, size_t dstRowBytes, - const Options& opts, - int* rowsDecoded) { - if (opts.fSubset) { - // Subsets are not supported. - return kUnimplemented; - } - - Result result = this->prepareToDecode(dstInfo, opts); - if (kSuccess != result) { - return result; - } - - // Perform the decode - int rows = this->decodeRows(dstInfo, dst, dstRowBytes, opts); - if (rows != dstInfo.height()) { - // We set rowsDecoded equal to the height because the background has already - // been filled. RLE encodings sometimes skip pixels, so we always start by - // filling the background. - *rowsDecoded = dstInfo.height(); - return kIncompleteInput; - } - - return kSuccess; -} - -/* - * Process the color table for the bmp input - */ - bool SkBmpRLECodec::createColorTable(SkColorType dstColorType) { - // Allocate memory for color table - uint32_t colorBytes = 0; - SkPMColor colorTable[256]; - if (this->bitsPerPixel() <= 8) { - // Inform the caller of the number of colors - uint32_t maxColors = 1 << this->bitsPerPixel(); - // Don't bother reading more than maxColors. - const uint32_t numColorsToRead = - fNumColors == 0 ? maxColors : std::min(fNumColors, maxColors); - - // Read the color table from the stream - colorBytes = numColorsToRead * fBytesPerColor; - std::unique_ptr cBuffer(new uint8_t[colorBytes]); - if (stream()->read(cBuffer.get(), colorBytes) != colorBytes) { - SkCodecPrintf("Error: unable to read color table.\n"); - return false; - } - - // Fill in the color table - PackColorProc packARGB = choose_pack_color_proc(false, dstColorType); - uint32_t i = 0; - for (; i < numColorsToRead; i++) { - uint8_t blue = get_byte(cBuffer.get(), i*fBytesPerColor); - uint8_t green = get_byte(cBuffer.get(), i*fBytesPerColor + 1); - uint8_t red = get_byte(cBuffer.get(), i*fBytesPerColor + 2); - colorTable[i] = packARGB(0xFF, red, green, blue); - } - - // To avoid segmentation faults on bad pixel data, fill the end of the - // color table with black. This is the same the behavior as the - // chromium decoder. - for (; i < maxColors; i++) { - colorTable[i] = SkPackARGB32(0xFF, 0, 0, 0); - } - - // Set the color table - fColorTable.reset(new SkColorPalette(colorTable, maxColors)); - } - - // Check that we have not read past the pixel array offset - if(fOffset < colorBytes) { - // This may occur on OS 2.1 and other old versions where the color - // table defaults to max size, and the bmp tries to use a smaller - // color table. This is invalid, and our decision is to indicate - // an error, rather than try to guess the intended size of the - // color table. - SkCodecPrintf("Error: pixel data offset less than color table size.\n"); - return false; - } - - // After reading the color table, skip to the start of the pixel array - if (stream()->skip(fOffset - colorBytes) != fOffset - colorBytes) { - SkCodecPrintf("Error: unable to skip to image data.\n"); - return false; - } - - // Return true on success - return true; -} - -bool SkBmpRLECodec::initializeStreamBuffer() { - fBytesBuffered = this->stream()->read(fStreamBuffer, kBufferSize); - if (fBytesBuffered == 0) { - SkCodecPrintf("Error: could not read RLE image data.\n"); - return false; - } - fCurrRLEByte = 0; - return true; -} - -/* - * @return the number of bytes remaining in the stream buffer after - * attempting to read more bytes from the stream - */ -size_t SkBmpRLECodec::checkForMoreData() { - const size_t remainingBytes = fBytesBuffered - fCurrRLEByte; - uint8_t* buffer = fStreamBuffer; - - // We will be reusing the same buffer, starting over from the beginning. - // Move any remaining bytes to the start of the buffer. - // We use memmove() instead of memcpy() because there is risk that the dst - // and src memory will overlap in corrupt images. - memmove(buffer, SkTAddOffset(buffer, fCurrRLEByte), remainingBytes); - - // Adjust the buffer ptr to the start of the unfilled data. - buffer += remainingBytes; - - // Try to read additional bytes from the stream. There are fCurrRLEByte - // bytes of additional space remaining in the buffer, assuming that we - // have already copied remainingBytes to the start of the buffer. - size_t additionalBytes = this->stream()->read(buffer, fCurrRLEByte); - - // Update counters and return the number of bytes we currently have - // available. We are at the start of the buffer again. - fCurrRLEByte = 0; - fBytesBuffered = remainingBytes + additionalBytes; - return fBytesBuffered; -} - -/* - * Set an RLE pixel using the color table - */ -void SkBmpRLECodec::setPixel(void* dst, size_t dstRowBytes, - const SkImageInfo& dstInfo, uint32_t x, uint32_t y, - uint8_t index) { - if (dst && is_coord_necessary(x, fSampleX, dstInfo.width())) { - // Set the row - uint32_t row = this->getDstRow(y, dstInfo.height()); - - // Set the pixel based on destination color type - const int dstX = get_dst_coord(x, fSampleX); - switch (dstInfo.colorType()) { - case kRGBA_8888_SkColorType: - case kBGRA_8888_SkColorType: { - SkPMColor* dstRow = SkTAddOffset(dst, row * (int) dstRowBytes); - dstRow[dstX] = fColorTable->operator[](index); - break; - } - case kRGB_565_SkColorType: { - uint16_t* dstRow = SkTAddOffset(dst, row * (int) dstRowBytes); - dstRow[dstX] = SkPixel32ToPixel16(fColorTable->operator[](index)); - break; - } - default: - // This case should not be reached. We should catch an invalid - // color type when we check that the conversion is possible. - SkASSERT(false); - break; - } - } -} - -/* - * Set an RLE pixel from R, G, B values - */ -void SkBmpRLECodec::setRGBPixel(void* dst, size_t dstRowBytes, - const SkImageInfo& dstInfo, uint32_t x, - uint32_t y, uint8_t red, uint8_t green, - uint8_t blue) { - if (dst && is_coord_necessary(x, fSampleX, dstInfo.width())) { - // Set the row - uint32_t row = this->getDstRow(y, dstInfo.height()); - - // Set the pixel based on destination color type - const int dstX = get_dst_coord(x, fSampleX); - switch (dstInfo.colorType()) { - case kRGBA_8888_SkColorType: { - SkPMColor* dstRow = SkTAddOffset(dst, row * (int) dstRowBytes); - dstRow[dstX] = SkPackARGB_as_RGBA(0xFF, red, green, blue); - break; - } - case kBGRA_8888_SkColorType: { - SkPMColor* dstRow = SkTAddOffset(dst, row * (int) dstRowBytes); - dstRow[dstX] = SkPackARGB_as_BGRA(0xFF, red, green, blue); - break; - } - case kRGB_565_SkColorType: { - uint16_t* dstRow = SkTAddOffset(dst, row * (int) dstRowBytes); - dstRow[dstX] = SkPack888ToRGB16(red, green, blue); - break; - } - default: - // This case should not be reached. We should catch an invalid - // color type when we check that the conversion is possible. - SkASSERT(false); - break; - } - } -} - -SkCodec::Result SkBmpRLECodec::onPrepareToDecode(const SkImageInfo& dstInfo, - const SkCodec::Options& options) { - // FIXME: Support subsets for scanline decodes. - if (options.fSubset) { - // Subsets are not supported. - return kUnimplemented; - } - - // Reset fSampleX. If it needs to be a value other than 1, it will get modified by - // the sampler. - fSampleX = 1; - fLinesToSkip = 0; - - SkColorType colorTableColorType = dstInfo.colorType(); - if (this->colorXform()) { - // Just set a known colorType for the colorTable. No need to actually transform - // the colors in the colorTable. - colorTableColorType = kBGRA_8888_SkColorType; - } - - // Create the color table if necessary and prepare the stream for decode - // Note that if it is non-NULL, inputColorCount will be modified - if (!this->createColorTable(colorTableColorType)) { - SkCodecPrintf("Error: could not create color table.\n"); - return SkCodec::kInvalidInput; - } - - // Initialize a buffer for encoded RLE data - if (!this->initializeStreamBuffer()) { - SkCodecPrintf("Error: cannot initialize stream buffer.\n"); - return SkCodec::kInvalidInput; - } - - return SkCodec::kSuccess; -} - -/* - * Performs the bitmap decoding for RLE input format - * RLE decoding is performed all at once, rather than a one row at a time - */ -int SkBmpRLECodec::decodeRows(const SkImageInfo& info, void* dst, size_t dstRowBytes, - const Options& opts) { - int height = info.height(); - - // Account for sampling. - SkImageInfo dstInfo = info.makeWH(this->fillWidth(), height); - - // Set the background as transparent. Then, if the RLE code skips pixels, - // the skipped pixels will be transparent. - if (dst) { - SkSampler::Fill(dstInfo, dst, dstRowBytes, opts.fZeroInitialized); - } - - // Adjust the height and the dst if the previous call to decodeRows() left us - // with lines that need to be skipped. - if (height > fLinesToSkip) { - height -= fLinesToSkip; - if (dst) { - dst = SkTAddOffset(dst, fLinesToSkip * dstRowBytes); - } - fLinesToSkip = 0; - - dstInfo = dstInfo.makeWH(dstInfo.width(), height); - } else { - fLinesToSkip -= height; - return height; - } - - void* decodeDst = dst; - size_t decodeRowBytes = dstRowBytes; - SkImageInfo decodeInfo = dstInfo; - if (decodeDst) { - if (this->colorXform()) { - decodeInfo = decodeInfo.makeColorType(kXformSrcColorType); - if (kRGBA_F16_SkColorType == dstInfo.colorType()) { - int count = height * dstInfo.width(); - this->resetXformBuffer(count); - sk_bzero(this->xformBuffer(), count * sizeof(uint32_t)); - decodeDst = this->xformBuffer(); - decodeRowBytes = dstInfo.width() * sizeof(uint32_t); - } - } - } - - int decodedHeight = this->decodeRLE(decodeInfo, decodeDst, decodeRowBytes); - if (this->colorXform() && decodeDst) { - for (int y = 0; y < decodedHeight; y++) { - this->applyColorXform(dst, decodeDst, dstInfo.width()); - decodeDst = SkTAddOffset(decodeDst, decodeRowBytes); - dst = SkTAddOffset(dst, dstRowBytes); - } - } - - return decodedHeight; -} - -int SkBmpRLECodec::decodeRLE(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes) { - // Use the original width to count the number of pixels in each row. - const int width = this->dimensions().width(); - - // This tells us the number of rows that we are meant to decode. - const int height = dstInfo.height(); - - // Set RLE flags - constexpr uint8_t RLE_ESCAPE = 0; - constexpr uint8_t RLE_EOL = 0; - constexpr uint8_t RLE_EOF = 1; - constexpr uint8_t RLE_DELTA = 2; - - // Destination parameters - int x = 0; - int y = 0; - - while (true) { - // If we have reached a row that is beyond the requested height, we have - // succeeded. - if (y >= height) { - // It would be better to check for the EOF marker before indicating - // success, but we may be performing a scanline decode, which - // would require us to stop before decoding the full height. - return height; - } - - // Every entry takes at least two bytes - if ((int) fBytesBuffered - fCurrRLEByte < 2) { - if (this->checkForMoreData() < 2) { - return y; - } - } - - // Read the next two bytes. These bytes have different meanings - // depending on their values. In the first interpretation, the first - // byte is an escape flag and the second byte indicates what special - // task to perform. - const uint8_t flag = fStreamBuffer[fCurrRLEByte++]; - const uint8_t task = fStreamBuffer[fCurrRLEByte++]; - - // Perform decoding - if (RLE_ESCAPE == flag) { - switch (task) { - case RLE_EOL: - x = 0; - y++; - break; - case RLE_EOF: - return height; - case RLE_DELTA: { - // Two bytes are needed to specify delta - if ((int) fBytesBuffered - fCurrRLEByte < 2) { - if (this->checkForMoreData() < 2) { - return y; - } - } - // Modify x and y - const uint8_t dx = fStreamBuffer[fCurrRLEByte++]; - const uint8_t dy = fStreamBuffer[fCurrRLEByte++]; - x += dx; - y += dy; - if (x > width) { - SkCodecPrintf("Warning: invalid RLE input.\n"); - return y - dy; - } else if (y > height) { - fLinesToSkip = y - height; - return height; - } - break; - } - default: { - // If task does not match any of the above signals, it - // indicates that we have a sequence of non-RLE pixels. - // Furthermore, the value of task is equal to the number - // of pixels to interpret. - uint8_t numPixels = task; - const size_t rowBytes = compute_row_bytes(numPixels, - this->bitsPerPixel()); - if (x + numPixels > width) { - SkCodecPrintf("Warning: invalid RLE input.\n"); - } - - // Abort if there are not enough bytes - // remaining in the stream to set numPixels. - - // At most, alignedRowBytes can be 255 (max uint8_t) * - // 3 (max bytes per pixel) + 1 (aligned) = 766. If - // fStreamBuffer was smaller than this, - // checkForMoreData would never succeed for some bmps. - static_assert(255 * 3 + 1 < kBufferSize, - "kBufferSize needs to be larger!"); - const size_t alignedRowBytes = SkAlign2(rowBytes); - if ((int) fBytesBuffered - fCurrRLEByte < alignedRowBytes) { - SkASSERT(alignedRowBytes < kBufferSize); - if (this->checkForMoreData() < alignedRowBytes) { - return y; - } - } - // Set numPixels number of pixels - while ((numPixels > 0) && (x < width)) { - switch(this->bitsPerPixel()) { - case 4: { - SkASSERT(fCurrRLEByte < fBytesBuffered); - uint8_t val = fStreamBuffer[fCurrRLEByte++]; - setPixel(dst, dstRowBytes, dstInfo, x++, - y, val >> 4); - numPixels--; - if (numPixels != 0) { - setPixel(dst, dstRowBytes, dstInfo, - x++, y, val & 0xF); - numPixels--; - } - break; - } - case 8: - SkASSERT(fCurrRLEByte < fBytesBuffered); - setPixel(dst, dstRowBytes, dstInfo, x++, - y, fStreamBuffer[fCurrRLEByte++]); - numPixels--; - break; - case 24: { - SkASSERT(fCurrRLEByte + 2 < fBytesBuffered); - uint8_t blue = fStreamBuffer[fCurrRLEByte++]; - uint8_t green = fStreamBuffer[fCurrRLEByte++]; - uint8_t red = fStreamBuffer[fCurrRLEByte++]; - setRGBPixel(dst, dstRowBytes, dstInfo, - x++, y, red, green, blue); - numPixels--; - break; - } - default: - SkASSERT(false); - return y; - } - } - // Skip a byte if necessary to maintain alignment - if (!SkIsAlign2(rowBytes)) { - fCurrRLEByte++; - } - break; - } - } - } else { - // If the first byte read is not a flag, it indicates the number of - // pixels to set in RLE mode. - const uint8_t numPixels = flag; - const int endX = std::min(x + numPixels, width); - - if (24 == this->bitsPerPixel()) { - // In RLE24, the second byte read is part of the pixel color. - // There are two more required bytes to finish encoding the - // color. - if ((int) fBytesBuffered - fCurrRLEByte < 2) { - if (this->checkForMoreData() < 2) { - return y; - } - } - - // Fill the pixels up to endX with the specified color - uint8_t blue = task; - uint8_t green = fStreamBuffer[fCurrRLEByte++]; - uint8_t red = fStreamBuffer[fCurrRLEByte++]; - while (x < endX) { - setRGBPixel(dst, dstRowBytes, dstInfo, x++, y, red, green, blue); - } - } else { - // In RLE8 or RLE4, the second byte read gives the index in the - // color table to look up the pixel color. - // RLE8 has one color index that gets repeated - // RLE4 has two color indexes in the upper and lower 4 bits of - // the bytes, which are alternated - uint8_t indices[2] = { task, task }; - if (4 == this->bitsPerPixel()) { - indices[0] >>= 4; - indices[1] &= 0xf; - } - - // Set the indicated number of pixels - for (int which = 0; x < endX; x++) { - setPixel(dst, dstRowBytes, dstInfo, x, y, indices[which]); - which = !which; - } - } - } - } -} - -bool SkBmpRLECodec::skipRows(int count) { - const SkImageInfo rowInfo = SkImageInfo::Make(this->dimensions().width(), count, - kN32_SkColorType, kUnpremul_SkAlphaType); - return count == this->decodeRows(rowInfo, nullptr, 0, this->options()); -} - -// FIXME: Make SkBmpRLECodec have no knowledge of sampling. -// Or it should do all sampling natively. -// It currently is a hybrid that needs to know what SkScaledCodec is doing. -class SkBmpRLESampler : public SkSampler { -public: - SkBmpRLESampler(SkBmpRLECodec* codec) - : fCodec(codec) - { - SkASSERT(fCodec); - } - - int fillWidth() const override { - return fCodec->fillWidth(); - } - -private: - int onSetSampleX(int sampleX) override { - return fCodec->setSampleX(sampleX); - } - - // Unowned pointer. fCodec will delete this class in its destructor. - SkBmpRLECodec* fCodec; -}; - -SkSampler* SkBmpRLECodec::getSampler(bool createIfNecessary) { - if (!fSampler && createIfNecessary) { - fSampler = std::make_unique(this); - } - - return fSampler.get(); -} - -int SkBmpRLECodec::setSampleX(int sampleX) { - fSampleX = sampleX; - return this->fillWidth(); -} - -int SkBmpRLECodec::fillWidth() const { - return get_scaled_dimension(this->dimensions().width(), fSampleX); -} diff --git a/gfx/skia/skia/src/codec/SkBmpRLECodec.h b/gfx/skia/skia/src/codec/SkBmpRLECodec.h deleted file mode 100644 index 0e8bb5953b01..000000000000 --- a/gfx/skia/skia/src/codec/SkBmpRLECodec.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#ifndef SkBmpRLECodec_DEFINED -#define SkBmpRLECodec_DEFINED - -#include "include/codec/SkCodec.h" -#include "include/core/SkRefCnt.h" -#include "include/core/SkTypes.h" -#include "src/codec/SkBmpCodec.h" -#include "src/codec/SkColorPalette.h" -#include "src/codec/SkSampler.h" - -#include -#include -#include - -class SkStream; -enum SkColorType : int; -struct SkEncodedInfo; -struct SkImageInfo; - -/* - * This class implements the decoding for bmp images that use an RLE encoding - */ -class SkBmpRLECodec : public SkBmpCodec { -public: - - /* - * Creates an instance of the decoder - * - * Called only by SkBmpCodec::MakeFromStream - * There should be no other callers despite this being public - * - * @param info contains properties of the encoded data - * @param stream the stream of encoded image data - * @param bitsPerPixel the number of bits used to store each pixel - * @param numColors the number of colors in the color table - * @param bytesPerColor the number of bytes in the stream used to represent - each color in the color table - * @param offset the offset of the image pixel data from the end of the - * headers - * @param rowOrder indicates whether rows are ordered top-down or bottom-up - */ - SkBmpRLECodec(SkEncodedInfo&& info, std::unique_ptr, - uint16_t bitsPerPixel, uint32_t numColors, uint32_t bytesPerColor, - uint32_t offset, SkCodec::SkScanlineOrder rowOrder); - - int setSampleX(int); - - int fillWidth() const; - -protected: - - Result onGetPixels(const SkImageInfo& dstInfo, void* dst, - size_t dstRowBytes, const Options&, - int*) override; - - SkCodec::Result onPrepareToDecode(const SkImageInfo& dstInfo, - const SkCodec::Options& options) override; - -private: - - /* - * Creates the color table - * Sets colorCount to the new color count if it is non-nullptr - */ - bool createColorTable(SkColorType dstColorType); - - bool initializeStreamBuffer(); - - /* - * Before signalling kIncompleteInput, we should attempt to load the - * stream buffer with additional data. - * - * @return the number of bytes remaining in the stream buffer after - * attempting to read more bytes from the stream - */ - size_t checkForMoreData(); - - /* - * Set an RLE pixel using the color table - */ - void setPixel(void* dst, size_t dstRowBytes, - const SkImageInfo& dstInfo, uint32_t x, uint32_t y, - uint8_t index); - /* - * Set an RLE24 pixel from R, G, B values - */ - void setRGBPixel(void* dst, size_t dstRowBytes, - const SkImageInfo& dstInfo, uint32_t x, uint32_t y, - uint8_t red, uint8_t green, uint8_t blue); - - /* - * If dst is NULL, this is a signal to skip the rows. - */ - int decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, - const Options& opts) override; - int decodeRLE(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes); - - bool skipRows(int count) override; - - SkSampler* getSampler(bool createIfNecessary) override; - - sk_sp fColorTable; - // fNumColors is the number specified in the header, or 0 if not present in the header. - const uint32_t fNumColors; - const uint32_t fBytesPerColor; - const uint32_t fOffset; - - inline static constexpr size_t kBufferSize = 4096; - uint8_t fStreamBuffer[kBufferSize]; - size_t fBytesBuffered; - - uint32_t fCurrRLEByte; - int fSampleX; - std::unique_ptr fSampler; - - // Scanline decodes allow the client to ask for a single scanline at a time. - // This can be tricky when the RLE encoding instructs the decoder to jump down - // multiple lines. This field keeps track of lines that need to be skipped - // on subsequent calls to decodeRows(). - int fLinesToSkip; - - using INHERITED = SkBmpCodec; -}; -#endif // SkBmpRLECodec_DEFINED diff --git a/gfx/skia/skia/src/codec/SkBmpStandardCodec.cpp b/gfx/skia/skia/src/codec/SkBmpStandardCodec.cpp deleted file mode 100644 index 1a9b8441173f..000000000000 --- a/gfx/skia/skia/src/codec/SkBmpStandardCodec.cpp +++ /dev/null @@ -1,352 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "src/codec/SkBmpStandardCodec.h" - -#include "include/core/SkAlphaType.h" -#include "include/core/SkColor.h" -#include "include/core/SkColorPriv.h" -#include "include/core/SkColorType.h" -#include "include/core/SkImageInfo.h" -#include "include/core/SkSize.h" -#include "include/core/SkStream.h" -#include "include/private/base/SkAlign.h" -#include "include/private/base/SkTemplates.h" -#include "src/base/SkMathPriv.h" -#include "src/codec/SkCodecPriv.h" - -#include -#include - -/* - * Creates an instance of the decoder - * Called only by NewFromStream - */ -SkBmpStandardCodec::SkBmpStandardCodec(SkEncodedInfo&& info, std::unique_ptr stream, - uint16_t bitsPerPixel, uint32_t numColors, - uint32_t bytesPerColor, uint32_t offset, - SkCodec::SkScanlineOrder rowOrder, - bool isOpaque, bool inIco) - : INHERITED(std::move(info), std::move(stream), bitsPerPixel, rowOrder) - , fColorTable(nullptr) - , fNumColors(numColors) - , fBytesPerColor(bytesPerColor) - , fOffset(offset) - , fSwizzler(nullptr) - , fIsOpaque(isOpaque) - , fInIco(inIco) - , fAndMaskRowBytes(fInIco ? SkAlign4(compute_row_bytes(this->dimensions().width(), 1)) : 0) -{} - -/* - * Initiates the bitmap decode - */ -SkCodec::Result SkBmpStandardCodec::onGetPixels(const SkImageInfo& dstInfo, - void* dst, size_t dstRowBytes, - const Options& opts, - int* rowsDecoded) { - if (opts.fSubset) { - // Subsets are not supported. - return kUnimplemented; - } - if (dstInfo.dimensions() != this->dimensions()) { - SkCodecPrintf("Error: scaling not supported.\n"); - return kInvalidScale; - } - - Result result = this->prepareToDecode(dstInfo, opts); - if (kSuccess != result) { - return result; - } - int rows = this->decodeRows(dstInfo, dst, dstRowBytes, opts); - if (rows != dstInfo.height()) { - *rowsDecoded = rows; - return kIncompleteInput; - } - return kSuccess; -} - -/* - * Process the color table for the bmp input - */ - bool SkBmpStandardCodec::createColorTable(SkColorType dstColorType, SkAlphaType dstAlphaType) { - // Allocate memory for color table - uint32_t colorBytes = 0; - SkPMColor colorTable[256]; - if (this->bitsPerPixel() <= 8) { - // Inform the caller of the number of colors - uint32_t maxColors = 1 << this->bitsPerPixel(); - // Don't bother reading more than maxColors. - const uint32_t numColorsToRead = - fNumColors == 0 ? maxColors : std::min(fNumColors, maxColors); - - // Read the color table from the stream - colorBytes = numColorsToRead * fBytesPerColor; - std::unique_ptr cBuffer(new uint8_t[colorBytes]); - if (stream()->read(cBuffer.get(), colorBytes) != colorBytes) { - SkCodecPrintf("Error: unable to read color table.\n"); - return false; - } - - SkColorType packColorType = dstColorType; - SkAlphaType packAlphaType = dstAlphaType; - if (this->colorXform()) { - packColorType = kBGRA_8888_SkColorType; - packAlphaType = kUnpremul_SkAlphaType; - } - - // Choose the proper packing function - bool isPremul = (kPremul_SkAlphaType == packAlphaType) && !fIsOpaque; - PackColorProc packARGB = choose_pack_color_proc(isPremul, packColorType); - - // Fill in the color table - uint32_t i = 0; - for (; i < numColorsToRead; i++) { - uint8_t blue = get_byte(cBuffer.get(), i*fBytesPerColor); - uint8_t green = get_byte(cBuffer.get(), i*fBytesPerColor + 1); - uint8_t red = get_byte(cBuffer.get(), i*fBytesPerColor + 2); - uint8_t alpha; - if (fIsOpaque) { - alpha = 0xFF; - } else { - alpha = get_byte(cBuffer.get(), i*fBytesPerColor + 3); - } - colorTable[i] = packARGB(alpha, red, green, blue); - } - - // To avoid segmentation faults on bad pixel data, fill the end of the - // color table with black. This is the same the behavior as the - // chromium decoder. - for (; i < maxColors; i++) { - colorTable[i] = SkPackARGB32(0xFF, 0, 0, 0); - } - - if (this->colorXform() && !this->xformOnDecode()) { - this->applyColorXform(colorTable, colorTable, maxColors); - } - - // Set the color table - fColorTable.reset(new SkColorPalette(colorTable, maxColors)); - } - - // Bmp-in-Ico files do not use an offset to indicate where the pixel data - // begins. Pixel data always begins immediately after the color table. - if (!fInIco) { - // Check that we have not read past the pixel array offset - if(fOffset < colorBytes) { - // This may occur on OS 2.1 and other old versions where the color - // table defaults to max size, and the bmp tries to use a smaller - // color table. This is invalid, and our decision is to indicate - // an error, rather than try to guess the intended size of the - // color table. - SkCodecPrintf("Error: pixel data offset less than color table size.\n"); - return false; - } - - // After reading the color table, skip to the start of the pixel array - if (stream()->skip(fOffset - colorBytes) != fOffset - colorBytes) { - SkCodecPrintf("Error: unable to skip to image data.\n"); - return false; - } - } - - // Return true on success - return true; -} - -static SkEncodedInfo make_info(SkEncodedInfo::Color color, - SkEncodedInfo::Alpha alpha, int bitsPerPixel) { - // This is just used for the swizzler, which does not need the width or height. - return SkEncodedInfo::Make(0, 0, color, alpha, bitsPerPixel); -} - -SkEncodedInfo SkBmpStandardCodec::swizzlerInfo() const { - const auto& info = this->getEncodedInfo(); - if (fInIco) { - if (this->bitsPerPixel() <= 8) { - return make_info(SkEncodedInfo::kPalette_Color, - info.alpha(), this->bitsPerPixel()); - } - if (this->bitsPerPixel() == 24) { - return make_info(SkEncodedInfo::kBGR_Color, - SkEncodedInfo::kOpaque_Alpha, 8); - } - } - - return make_info(info.color(), info.alpha(), info.bitsPerComponent()); -} - -void SkBmpStandardCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& opts) { - // In the case of bmp-in-icos, we will report BGRA to the client, - // since we may be required to apply an alpha mask after the decode. - // However, the swizzler needs to know the actual format of the bmp. - SkEncodedInfo encodedInfo = this->swizzlerInfo(); - - // Get a pointer to the color table if it exists - const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); - - SkImageInfo swizzlerInfo = dstInfo; - SkCodec::Options swizzlerOptions = opts; - if (this->colorXform()) { - swizzlerInfo = swizzlerInfo.makeColorType(kXformSrcColorType); - if (kPremul_SkAlphaType == dstInfo.alphaType()) { - swizzlerInfo = swizzlerInfo.makeAlphaType(kUnpremul_SkAlphaType); - } - - swizzlerOptions.fZeroInitialized = kNo_ZeroInitialized; - } - - fSwizzler = SkSwizzler::Make(encodedInfo, colorPtr, swizzlerInfo, swizzlerOptions); - SkASSERT(fSwizzler); -} - -SkCodec::Result SkBmpStandardCodec::onPrepareToDecode(const SkImageInfo& dstInfo, - const SkCodec::Options& options) { - if (this->xformOnDecode()) { - this->resetXformBuffer(dstInfo.width()); - } - - // Create the color table if necessary and prepare the stream for decode - // Note that if it is non-NULL, inputColorCount will be modified - if (!this->createColorTable(dstInfo.colorType(), dstInfo.alphaType())) { - SkCodecPrintf("Error: could not create color table.\n"); - return SkCodec::kInvalidInput; - } - - // Initialize a swizzler - this->initializeSwizzler(dstInfo, options); - return SkCodec::kSuccess; -} - -/* - * Performs the bitmap decoding for standard input format - */ -int SkBmpStandardCodec::decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, - const Options& opts) { - // Iterate over rows of the image - const int height = dstInfo.height(); - for (int y = 0; y < height; y++) { - // Read a row of the input - if (this->stream()->read(this->srcBuffer(), this->srcRowBytes()) != this->srcRowBytes()) { - SkCodecPrintf("Warning: incomplete input stream.\n"); - return y; - } - - // Decode the row in destination format - uint32_t row = this->getDstRow(y, dstInfo.height()); - - void* dstRow = SkTAddOffset(dst, row * dstRowBytes); - - if (this->xformOnDecode()) { - SkASSERT(this->colorXform()); - fSwizzler->swizzle(this->xformBuffer(), this->srcBuffer()); - this->applyColorXform(dstRow, this->xformBuffer(), fSwizzler->swizzleWidth()); - } else { - fSwizzler->swizzle(dstRow, this->srcBuffer()); - } - } - - if (fInIco && fIsOpaque) { - const int startScanline = this->currScanline(); - if (startScanline < 0) { - // We are not performing a scanline decode. - // Just decode the entire ICO mask and return. - decodeIcoMask(this->stream(), dstInfo, dst, dstRowBytes); - return height; - } - - // In order to perform a scanline ICO decode, we must be able - // to skip ahead in the stream in order to apply the AND mask - // to the requested scanlines. - // We will do this by taking advantage of the fact that - // SkIcoCodec always uses a SkMemoryStream as its underlying - // representation of the stream. - const void* memoryBase = this->stream()->getMemoryBase(); - SkASSERT(nullptr != memoryBase); - SkASSERT(this->stream()->hasLength()); - SkASSERT(this->stream()->hasPosition()); - - const size_t length = this->stream()->getLength(); - const size_t currPosition = this->stream()->getPosition(); - - // Calculate how many bytes we must skip to reach the AND mask. - const int remainingScanlines = this->dimensions().height() - startScanline - height; - const size_t bytesToSkip = remainingScanlines * this->srcRowBytes() + - startScanline * fAndMaskRowBytes; - const size_t subStreamStartPosition = currPosition + bytesToSkip; - if (subStreamStartPosition >= length) { - // FIXME: How can we indicate that this decode was actually incomplete? - return height; - } - - // Create a subStream to pass to decodeIcoMask(). It is useful to encapsulate - // the memory base into a stream in order to safely handle incomplete images - // without reading out of bounds memory. - const void* subStreamMemoryBase = SkTAddOffset(memoryBase, - subStreamStartPosition); - const size_t subStreamLength = length - subStreamStartPosition; - // This call does not transfer ownership of the subStreamMemoryBase. - SkMemoryStream subStream(subStreamMemoryBase, subStreamLength, false); - - // FIXME: If decodeIcoMask does not succeed, is there a way that we can - // indicate the decode was incomplete? - decodeIcoMask(&subStream, dstInfo, dst, dstRowBytes); - } - - return height; -} - -void SkBmpStandardCodec::decodeIcoMask(SkStream* stream, const SkImageInfo& dstInfo, - void* dst, size_t dstRowBytes) { - // BMP in ICO have transparency, so this cannot be 565. The below code depends - // on the output being an SkPMColor. - SkASSERT(kRGBA_8888_SkColorType == dstInfo.colorType() || - kBGRA_8888_SkColorType == dstInfo.colorType() || - kRGBA_F16_SkColorType == dstInfo.colorType()); - - // If we are sampling, make sure that we only mask the sampled pixels. - // We do not need to worry about sampling in the y-dimension because that - // should be handled by SkSampledCodec. - const int sampleX = fSwizzler->sampleX(); - const int sampledWidth = get_scaled_dimension(this->dimensions().width(), sampleX); - const int srcStartX = get_start_coord(sampleX); - - - SkPMColor* dstPtr = (SkPMColor*) dst; - for (int y = 0; y < dstInfo.height(); y++) { - // The srcBuffer will at least be large enough - if (stream->read(this->srcBuffer(), fAndMaskRowBytes) != fAndMaskRowBytes) { - SkCodecPrintf("Warning: incomplete AND mask for bmp-in-ico.\n"); - return; - } - - auto applyMask = [dstInfo](void* dstRow, int x, uint64_t bit) { - if (kRGBA_F16_SkColorType == dstInfo.colorType()) { - uint64_t* dst64 = (uint64_t*) dstRow; - dst64[x] &= bit - 1; - } else { - uint32_t* dst32 = (uint32_t*) dstRow; - dst32[x] &= bit - 1; - } - }; - - int row = this->getDstRow(y, dstInfo.height()); - - void* dstRow = SkTAddOffset(dstPtr, row * dstRowBytes); - - int srcX = srcStartX; - for (int dstX = 0; dstX < sampledWidth; dstX++) { - int quotient; - int modulus; - SkTDivMod(srcX, 8, "ient, &modulus); - uint32_t shift = 7 - modulus; - uint64_t alphaBit = (this->srcBuffer()[quotient] >> shift) & 0x1; - applyMask(dstRow, dstX, alphaBit); - srcX += sampleX; - } - } -} diff --git a/gfx/skia/skia/src/codec/SkBmpStandardCodec.h b/gfx/skia/skia/src/codec/SkBmpStandardCodec.h deleted file mode 100644 index 39d10b13d9d4..000000000000 --- a/gfx/skia/skia/src/codec/SkBmpStandardCodec.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#ifndef SkBmpStandardCodec_DEFINED -#define SkBmpStandardCodec_DEFINED - -#include "include/codec/SkCodec.h" -#include "include/core/SkRefCnt.h" -#include "include/core/SkTypes.h" -#include "include/private/SkEncodedInfo.h" -#include "src/codec/SkBmpBaseCodec.h" -#include "src/codec/SkColorPalette.h" -#include "src/codec/SkSwizzler.h" - -#include -#include -#include - -class SkSampler; -class SkStream; -enum SkAlphaType : int; -enum SkColorType : int; -struct SkImageInfo; - -/* - * This class implements the decoding for bmp images that use "standard" modes, - * which essentially means they do not contain bit masks or RLE codes. - */ -class SkBmpStandardCodec : public SkBmpBaseCodec { -public: - - /* - * Creates an instance of the decoder - * - * Called only by SkBmpCodec::MakeFromStream - * There should be no other callers despite this being public - * - * @param info contains properties of the encoded data - * @param stream the stream of encoded image data - * @param bitsPerPixel the number of bits used to store each pixel - * @param numColors the number of colors in the color table - * @param bytesPerColor the number of bytes in the stream used to represent - each color in the color table - * @param offset the offset of the image pixel data from the end of the - * headers - * @param rowOrder indicates whether rows are ordered top-down or bottom-up - * @param isOpaque indicates if the bmp itself is opaque (before applying - * the icp mask, if there is one) - * @param inIco indicates if the bmp is embedded in an ico file - */ - SkBmpStandardCodec(SkEncodedInfo&& info, std::unique_ptr stream, - uint16_t bitsPerPixel, uint32_t numColors, uint32_t bytesPerColor, - uint32_t offset, SkCodec::SkScanlineOrder rowOrder, - bool isOpaque, bool inIco); - -protected: - - Result onGetPixels(const SkImageInfo& dstInfo, void* dst, - size_t dstRowBytes, const Options&, - int*) override; - - bool onInIco() const override { - return fInIco; - } - - SkCodec::Result onPrepareToDecode(const SkImageInfo& dstInfo, - const SkCodec::Options& options) override; - - SkSampler* getSampler(bool createIfNecessary) override { - SkASSERT(fSwizzler); - return fSwizzler.get(); - } - -private: - bool createColorTable(SkColorType colorType, SkAlphaType alphaType); - SkEncodedInfo swizzlerInfo() const; - void initializeSwizzler(const SkImageInfo& dstInfo, const Options& opts); - - int decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, - const Options& opts) override; - - /* - * @param stream This may be a pointer to the stream owned by the parent SkCodec - * or a sub-stream of the stream owned by the parent SkCodec. - * Either way, this stream is unowned. - */ - void decodeIcoMask(SkStream* stream, const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes); - - sk_sp fColorTable; - // fNumColors is the number specified in the header, or 0 if not present in the header. - const uint32_t fNumColors; - const uint32_t fBytesPerColor; - const uint32_t fOffset; - std::unique_ptr fSwizzler; - const bool fIsOpaque; - const bool fInIco; - const size_t fAndMaskRowBytes; // only used for fInIco decodes - - using INHERITED = SkBmpBaseCodec; -}; -#endif // SkBmpStandardCodec_DEFINED diff --git a/gfx/skia/skia/src/codec/SkCodec.cpp b/gfx/skia/skia/src/codec/SkCodec.cpp index 8cc94f0858a5..114984febc2b 100644 --- a/gfx/skia/skia/src/codec/SkCodec.cpp +++ b/gfx/skia/skia/src/codec/SkCodec.cpp @@ -11,7 +11,6 @@ #include "include/codec/SkPixmapUtils.h" #include "include/core/SkAlphaType.h" #include "include/core/SkBitmap.h" -#include "include/core/SkColorPriv.h" #include "include/core/SkColorSpace.h" #include "include/core/SkColorType.h" #include "include/core/SkData.h" @@ -26,6 +25,7 @@ #include "src/codec/SkFrameHolder.h" #include "src/codec/SkPixmapUtilsPriv.h" #include "src/codec/SkSampler.h" +#include "src/core/SkColorPriv.h" #include #include @@ -62,7 +62,7 @@ #include "include/codec/SkJpegxlDecoder.h" #endif -#if defined(SK_CODEC_DECODES_PNG) +#if defined(SK_CODEC_DECODES_PNG_WITH_LIBPNG) #include "include/codec/SkPngDecoder.h" #endif @@ -79,10 +79,6 @@ #endif #endif // !defined(SK_DISABLE_LEGACY_INIT_DECODERS) -#if defined(SK_BUILD_FOR_ANDROID_FRAMEWORK) -#include "cutils/properties.h" -#endif // defined(SK_BUILD_FOR_ANDROID_FRAMEWORK) - namespace SkCodecs { // A static variable inside a function avoids a static initializer. // https://chromium.googlesource.com/chromium/src/+/HEAD/docs/static_initializers.md#removing-static-initializers @@ -92,7 +88,7 @@ static std::vector* get_decoders_for_editing() { static SkOnce once; once([] { if (decoders->empty()) { -#if defined(SK_CODEC_DECODES_PNG) +#if defined(SK_CODEC_DECODES_PNG_WITH_LIBPNG) decoders->push_back(SkPngDecoder::Decoder()); #endif #if defined(SK_CODEC_DECODES_JPEG) @@ -115,14 +111,7 @@ static std::vector* get_decoders_for_editing() { #endif #if defined(SK_CODEC_DECODES_AVIF) #if defined(SK_BUILD_FOR_ANDROID_FRAMEWORK) - // Register CrabbyAvif based SkAvifDecoder on the Android framework - // if it is allowed. Otherwise Android framework will use - // SkHeifDecoder for decoding AVIF. - // TODO: Codec registration for the Android framework has to be - // moved outside of skia and this logic has to be moved there. - if (property_get_int32("media.avif.crabbyavif", 0) != 0) { - decoders->push_back(SkAvifDecoder::CrabbyAvif::Decoder()); - } + decoders->push_back(SkAvifDecoder::CrabbyAvif::Decoder()); #else decoders->push_back(SkAvifDecoder::LibAvif::Decoder()); #endif @@ -299,7 +288,7 @@ SkCodec::Result SkCodec::getYUVAPlanes(const SkYUVAPixmaps& yuvaPixmaps) { } bool SkCodec::conversionSupported(const SkImageInfo& dst, bool srcIsOpaque, bool needsColorXform) { - if (!valid_alpha(dst.alphaType(), srcIsOpaque)) { + if (!SkCodecPriv::ValidAlpha(dst.alphaType(), srcIsOpaque)) { return false; } @@ -779,8 +768,9 @@ void SkCodec::fillIncompleteImage(const SkImageInfo& info, void* dst, size_t row SkSampler::Fill(fillInfo, fillDst, rowBytes, kNo_ZeroInitialized); } -bool sk_select_xform_format(SkColorType colorType, bool forColorTable, - skcms_PixelFormat* outFormat) { +bool SkCodecPriv::SelectXformFormat(SkColorType colorType, + bool forColorTable, + skcms_PixelFormat* outFormat) { SkASSERT(outFormat); switch (colorType) { @@ -851,8 +841,8 @@ bool SkCodec::initializeColorXform(const SkImageInfo& dstInfo, SkEncodedInfo::Al fXformTime = SkEncodedInfo::kPalette_Color != fEncodedInfo.color() || kRGBA_F16_SkColorType == dstInfo.colorType() ? kDecodeRow_XformTime : kPalette_XformTime; - if (!sk_select_xform_format(dstInfo.colorType(), fXformTime == kPalette_XformTime, - &fDstXformFormat)) { + if (!SkCodecPriv::SelectXformFormat( + dstInfo.colorType(), fXformTime == kPalette_XformTime, &fDstXformFormat)) { return false; } if (encodedAlpha == SkEncodedInfo::kUnpremul_Alpha diff --git a/gfx/skia/skia/src/codec/SkCodecPriv.h b/gfx/skia/skia/src/codec/SkCodecPriv.h index 6c5a992b72e9..86e5e1276cb9 100644 --- a/gfx/skia/skia/src/codec/SkCodecPriv.h +++ b/gfx/skia/skia/src/codec/SkCodecPriv.h @@ -8,12 +8,13 @@ #ifndef SkCodecPriv_DEFINED #define SkCodecPriv_DEFINED +#include "include/codec/SkCodec.h" #include "include/codec/SkEncodedOrigin.h" #include "include/core/SkImageInfo.h" #include "include/core/SkTypes.h" -#include "include/private/SkColorData.h" #include "include/private/SkEncodedInfo.h" #include "src/codec/SkColorPalette.h" +#include "src/core/SkColorData.h" #include "src/base/SkEndian.h" #include @@ -24,240 +25,244 @@ #define SkCodecPrintf(...) #endif -// Defined in SkCodec.cpp -bool sk_select_xform_format(SkColorType colorType, bool forColorTable, - skcms_PixelFormat* outFormat); - -// FIXME: Consider sharing with dm, nanbench, and tools. -static inline float get_scale_from_sample_size(int sampleSize) { - return 1.0f / ((float) sampleSize); -} - -static inline bool is_valid_subset(const SkIRect& subset, const SkISize& imageDims) { - return SkIRect::MakeSize(imageDims).contains(subset); -} - -/* - * returns a scaled dimension based on the original dimension and the sampleSize - * NOTE: we round down here for scaled dimension to match the behavior of SkImageDecoder - * FIXME: I think we should call this get_sampled_dimension(). - */ -static inline int get_scaled_dimension(int srcDimension, int sampleSize) { - if (sampleSize > srcDimension) { - return 1; - } - return srcDimension / sampleSize; -} - -/* - * Returns the first coordinate that we will keep during a scaled decode. - * The output can be interpreted as an x-coordinate or a y-coordinate. - * - * This does not need to be called and is not called when sampleFactor == 1. - */ -static inline int get_start_coord(int sampleFactor) { return sampleFactor / 2; } - -/* - * Given a coordinate in the original image, this returns the corresponding - * coordinate in the scaled image. This function is meaningless if - * IsCoordNecessary returns false. - * The output can be interpreted as an x-coordinate or a y-coordinate. - * - * This does not need to be called and is not called when sampleFactor == 1. - */ -static inline int get_dst_coord(int srcCoord, int sampleFactor) { return srcCoord / sampleFactor; } - -/* - * When scaling, we will discard certain y-coordinates (rows) and - * x-coordinates (columns). This function returns true if we should keep the - * coordinate and false otherwise. - * The inputs may be x-coordinates or y-coordinates. - * - * This does not need to be called and is not called when sampleFactor == 1. - */ -static inline bool is_coord_necessary(int srcCoord, int sampleFactor, int scaledDim) { - // Get the first coordinate that we want to keep - int startCoord = get_start_coord(sampleFactor); - - // Return false on edge cases - if (srcCoord < startCoord || get_dst_coord(srcCoord, sampleFactor) >= scaledDim) { - return false; - } - - // Every sampleFactor rows are necessary - return ((srcCoord - startCoord) % sampleFactor) == 0; -} - -static inline bool valid_alpha(SkAlphaType dstAlpha, bool srcIsOpaque) { - if (kUnknown_SkAlphaType == dstAlpha) { - return false; - } - - if (srcIsOpaque) { - if (kOpaque_SkAlphaType != dstAlpha) { - SkCodecPrintf("Warning: an opaque image should be decoded as opaque " - "- it is being decoded as non-opaque, which will draw slower\n"); - } - return true; - } - - return dstAlpha != kOpaque_SkAlphaType; -} - -/* - * If there is a color table, get a pointer to the colors, otherwise return nullptr - */ -static inline const SkPMColor* get_color_ptr(SkColorPalette* colorTable) { - return nullptr != colorTable ? colorTable->readColors() : nullptr; -} - -/* - * Compute row bytes for an image using pixels per byte - */ -static inline size_t compute_row_bytes_ppb(int width, uint32_t pixelsPerByte) { - return (width + pixelsPerByte - 1) / pixelsPerByte; -} - -/* - * Compute row bytes for an image using bytes per pixel - */ -static inline size_t compute_row_bytes_bpp(int width, uint32_t bytesPerPixel) { - return width * bytesPerPixel; -} - -/* - * Compute row bytes for an image - */ -static inline size_t compute_row_bytes(int width, uint32_t bitsPerPixel) { - if (bitsPerPixel < 16) { - SkASSERT(0 == 8 % bitsPerPixel); - const uint32_t pixelsPerByte = 8 / bitsPerPixel; - return compute_row_bytes_ppb(width, pixelsPerByte); - } else { - SkASSERT(0 == bitsPerPixel % 8); - const uint32_t bytesPerPixel = bitsPerPixel / 8; - return compute_row_bytes_bpp(width, bytesPerPixel); - } -} - -/* - * Get a byte from a buffer - * This method is unsafe, the caller is responsible for performing a check - */ -static inline uint8_t get_byte(const uint8_t* buffer, uint32_t i) { - return buffer[i]; -} - -/* - * Get a short from a buffer - * This method is unsafe, the caller is responsible for performing a check - */ -static inline uint16_t get_short(const uint8_t* buffer, uint32_t i) { - uint16_t result; - memcpy(&result, &(buffer[i]), 2); -#ifdef SK_CPU_BENDIAN - return SkEndianSwap16(result); -#else - return result; -#endif -} - -/* - * Get an int from a buffer - * This method is unsafe, the caller is responsible for performing a check - */ -static inline uint32_t get_int(const uint8_t* buffer, uint32_t i) { - uint32_t result; - memcpy(&result, &(buffer[i]), 4); -#ifdef SK_CPU_BENDIAN - return SkEndianSwap32(result); -#else - return result; -#endif -} - -/* - * @param data Buffer to read bytes from - * @param isLittleEndian Output parameter - * Indicates if the data is little endian - * Is unaffected on false returns - */ -static inline bool is_valid_endian_marker(const uint8_t* data, bool* isLittleEndian) { - // II indicates Intel (little endian) and MM indicates motorola (big endian). - if (('I' != data[0] || 'I' != data[1]) && ('M' != data[0] || 'M' != data[1])) { - return false; - } - - *isLittleEndian = ('I' == data[0]); - return true; -} - -static inline uint16_t get_endian_short(const uint8_t* data, bool littleEndian) { - if (littleEndian) { - return (data[1] << 8) | (data[0]); - } - - return (data[0] << 8) | (data[1]); -} - -static inline uint32_t get_endian_int(const uint8_t* data, bool littleEndian) { - if (littleEndian) { - return (data[3] << 24) | (data[2] << 16) | (data[1] << 8) | (data[0]); - } - - return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | (data[3]); -} - -static inline SkPMColor premultiply_argb_as_rgba(U8CPU a, U8CPU r, U8CPU g, U8CPU b) { - if (a != 255) { - r = SkMulDiv255Round(r, a); - g = SkMulDiv255Round(g, a); - b = SkMulDiv255Round(b, a); - } - - return SkPackARGB_as_RGBA(a, r, g, b); -} - -static inline SkPMColor premultiply_argb_as_bgra(U8CPU a, U8CPU r, U8CPU g, U8CPU b) { - if (a != 255) { - r = SkMulDiv255Round(r, a); - g = SkMulDiv255Round(g, a); - b = SkMulDiv255Round(b, a); - } - - return SkPackARGB_as_BGRA(a, r, g, b); -} - -static inline bool is_rgba(SkColorType colorType) { -#ifdef SK_PMCOLOR_IS_RGBA - return (kBGRA_8888_SkColorType != colorType); -#else - return (kRGBA_8888_SkColorType == colorType); -#endif -} - -// Method for coverting to a 32 bit pixel. -typedef uint32_t (*PackColorProc)(U8CPU a, U8CPU r, U8CPU g, U8CPU b); - -static inline PackColorProc choose_pack_color_proc(bool isPremul, SkColorType colorType) { - bool isRGBA = is_rgba(colorType); - if (isPremul) { - if (isRGBA) { - return &premultiply_argb_as_rgba; - } else { - return &premultiply_argb_as_bgra; - } - } else { - if (isRGBA) { - return &SkPackARGB_as_RGBA; - } else { - return &SkPackARGB_as_BGRA; - } - } -} - namespace SkCodecs { bool HasDecoder(std::string_view id); } +class SkCodecPriv final { +public: + static const SkEncodedInfo& GetEncodedInfo(const SkCodec* codec) { + SkASSERT(codec); + return codec->getEncodedInfo(); + } + + static bool SelectXformFormat(SkColorType colorType, + bool forColorTable, + skcms_PixelFormat* outFormat); + + // FIXME: Consider sharing with dm, nanbench, and tools. + static float GetScaleFromSampleSize(int sampleSize) { return 1.0f / ((float)sampleSize); } + + static bool IsValidSubset(const SkIRect& subset, const SkISize& imageDims) { + return SkIRect::MakeSize(imageDims).contains(subset); + } + + /* + * returns a scaled dimension based on the original dimension and the sampleSize + * NOTE: we round down here for scaled dimension to match the behavior of SkImageDecoder + */ + static int GetSampledDimension(int srcDimension, int sampleSize) { + if (sampleSize > srcDimension) { + return 1; + } + return srcDimension / sampleSize; + } + + /* + * Returns the first coordinate that we will keep during a scaled decode. + * The output can be interpreted as an x-coordinate or a y-coordinate. + * + * This does not need to be called and is not called when sampleFactor == 1. + */ + static int GetStartCoord(int sampleFactor) { return sampleFactor / 2; } + + /* + * Given a coordinate in the original image, this returns the corresponding + * coordinate in the scaled image. This function is meaningless if + * IsCoordNecessary returns false. + * The output can be interpreted as an x-coordinate or a y-coordinate. + * + * This does not need to be called and is not called when sampleFactor == 1. + */ + static int GetDstCoord(int srcCoord, int sampleFactor) { return srcCoord / sampleFactor; } + + /* + * When scaling, we will discard certain y-coordinates (rows) and + * x-coordinates (columns). This function returns true if we should keep the + * coordinate and false otherwise. + * The inputs may be x-coordinates or y-coordinates. + * + * This does not need to be called and is not called when sampleFactor == 1. + */ + static bool IsCoordNecessary(int srcCoord, int sampleFactor, int scaledDim) { + // Get the first coordinate that we want to keep + int startCoord = GetStartCoord(sampleFactor); + + // Return false on edge cases + if (srcCoord < startCoord || GetDstCoord(srcCoord, sampleFactor) >= scaledDim) { + return false; + } + + // Every sampleFactor rows are necessary + return ((srcCoord - startCoord) % sampleFactor) == 0; + } + + static bool ValidAlpha(SkAlphaType dstAlpha, bool srcIsOpaque) { + if (kUnknown_SkAlphaType == dstAlpha) { + return false; + } + + if (srcIsOpaque) { + if (kOpaque_SkAlphaType != dstAlpha) { + SkCodecPrintf( + "Warning: an opaque image should be decoded as opaque " + "- it is being decoded as non-opaque, which will draw slower\n"); + } + return true; + } + + return dstAlpha != kOpaque_SkAlphaType; + } + + /* + * If there is a color table, get a pointer to the colors, otherwise return nullptr + */ + static const SkPMColor* GetColorPtr(SkColorPalette* colorTable) { + return nullptr != colorTable ? colorTable->readColors() : nullptr; + } + + /* + * Compute row bytes for an image using pixels per byte + */ + static size_t ComputeRowBytesPixelsPerByte(int width, uint32_t pixelsPerByte) { + return (width + pixelsPerByte - 1) / pixelsPerByte; + } + + /* + * Compute row bytes for an image using bytes per pixel + */ + static size_t ComputeRowBytesBytesPerPixel(int width, uint32_t bytesPerPixel) { + return width * bytesPerPixel; + } + + /* + * Compute row bytes for an image + */ + static size_t ComputeRowBytes(int width, uint32_t bitsPerPixel) { + if (bitsPerPixel < 16) { + SkASSERT(0 == 8 % bitsPerPixel); + const uint32_t pixelsPerByte = 8 / bitsPerPixel; + return ComputeRowBytesPixelsPerByte(width, pixelsPerByte); + } else { + SkASSERT(0 == bitsPerPixel % 8); + const uint32_t bytesPerPixel = bitsPerPixel / 8; + return ComputeRowBytesBytesPerPixel(width, bytesPerPixel); + } + } + + /* + * Get a byte from a buffer + * This method is unsafe, the caller is responsible for performing a check + */ + static uint8_t UnsafeGetByte(const uint8_t* buffer, uint32_t i) { return buffer[i]; } + + /* + * Get a short from a buffer + * This method is unsafe, the caller is responsible for performing a check + */ + static uint16_t UnsafeGetShort(const uint8_t* buffer, uint32_t i) { + uint16_t result; + memcpy(&result, &(buffer[i]), 2); +#ifdef SK_CPU_BENDIAN + return SkEndianSwap16(result); +#else + return result; +#endif + } + + /* + * Get an int from a buffer + * This method is unsafe, the caller is responsible for performing a check + */ + static uint32_t UnsafeGetInt(const uint8_t* buffer, uint32_t i) { + uint32_t result; + memcpy(&result, &(buffer[i]), 4); +#ifdef SK_CPU_BENDIAN + return SkEndianSwap32(result); +#else + return result; +#endif + } + + /* + * @param data Buffer to read bytes from + * @param isLittleEndian Output parameter + * Indicates if the data is little endian + * Is unaffected on false returns + */ + static bool IsValidEndianMarker(const uint8_t* data, bool* isLittleEndian) { + // II indicates Intel (little endian) and MM indicates motorola (big endian). + if (('I' != data[0] || 'I' != data[1]) && ('M' != data[0] || 'M' != data[1])) { + return false; + } + + *isLittleEndian = ('I' == data[0]); + return true; + } + + static uint16_t GetEndianShort(const uint8_t* data, bool littleEndian) { + if (littleEndian) { + return (data[1] << 8) | (data[0]); + } + + return (data[0] << 8) | (data[1]); + } + + static uint32_t GetEndianInt(const uint8_t* data, bool littleEndian) { + if (littleEndian) { + return (data[3] << 24) | (data[2] << 16) | (data[1] << 8) | (data[0]); + } + + return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | (data[3]); + } + + static SkPMColor PremultiplyARGBasRGBA(U8CPU a, U8CPU r, U8CPU g, U8CPU b) { + if (a != 255) { + r = SkMulDiv255Round(r, a); + g = SkMulDiv255Round(g, a); + b = SkMulDiv255Round(b, a); + } + + return SkPackARGB_as_RGBA(a, r, g, b); + } + + static SkPMColor PremultiplyARGBasBGRA(U8CPU a, U8CPU r, U8CPU g, U8CPU b) { + if (a != 255) { + r = SkMulDiv255Round(r, a); + g = SkMulDiv255Round(g, a); + b = SkMulDiv255Round(b, a); + } + + return SkPackARGB_as_BGRA(a, r, g, b); + } + + static bool IsRGBA(SkColorType colorType) { +#ifdef SK_PMCOLOR_IS_RGBA + return (kBGRA_8888_SkColorType != colorType); +#else + return (kRGBA_8888_SkColorType == colorType); +#endif + } + + // Method for coverting to a 32 bit pixel. + using PackColorProc = uint32_t (*)(U8CPU a, U8CPU r, U8CPU g, U8CPU b); + + static PackColorProc ChoosePackColorProc(bool isPremul, SkColorType colorType) { + bool isRGBA = IsRGBA(colorType); + if (isPremul) { + if (isRGBA) { + return &PremultiplyARGBasRGBA; + } else { + return &PremultiplyARGBasBGRA; + } + } else { + if (isRGBA) { + return &SkPackARGB_as_RGBA; + } else { + return &SkPackARGB_as_BGRA; + } + } + } +}; + #endif // SkCodecPriv_DEFINED diff --git a/gfx/skia/skia/src/codec/SkCrabbyAvifCodec.cpp b/gfx/skia/skia/src/codec/SkCrabbyAvifCodec.cpp deleted file mode 100644 index 62d56378e72d..000000000000 --- a/gfx/skia/skia/src/codec/SkCrabbyAvifCodec.cpp +++ /dev/null @@ -1,404 +0,0 @@ -/* - * Copyright 2024 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "src/codec/SkCrabbyAvifCodec.h" - -#include "include/codec/SkAndroidCodec.h" -#include "include/codec/SkAvifDecoder.h" -#include "include/codec/SkCodec.h" -#include "include/codec/SkCodecAnimation.h" -#include "include/core/SkColorType.h" -#include "include/core/SkImageInfo.h" -#include "include/core/SkSize.h" -#include "include/core/SkStream.h" -#include "include/core/SkTypes.h" -#include "include/private/SkGainmapInfo.h" -#include "modules/skcms/skcms.h" -#include "src/core/SkStreamPriv.h" - -#include -#include -#include - -#include "avif/avif.h" -#include "avif/libavif_compat.h" - -namespace { - -template -float FractionToFloat(NumeratorType numerator, uint32_t denominator) { - // First cast to double and not float because uint32_t->float conversion can - // cause precision loss. - return static_cast(numerator) / denominator; -} - -bool PopulateGainmapInfo(const crabbyavif::avifGainMap& gain_map, SkGainmapInfo* info) { - if (gain_map.baseHdrHeadroom.d == 0 || gain_map.alternateHdrHeadroom.d == 0) { - return false; - } - const float base_headroom = - std::exp2(FractionToFloat(gain_map.baseHdrHeadroom.n, gain_map.baseHdrHeadroom.d)); - const float alternate_headroom = std::exp2( - FractionToFloat(gain_map.alternateHdrHeadroom.n, gain_map.alternateHdrHeadroom.d)); - const bool base_is_hdr = base_headroom > alternate_headroom; - info->fDisplayRatioSdr = base_is_hdr ? alternate_headroom : base_headroom; - info->fDisplayRatioHdr = base_is_hdr ? base_headroom : alternate_headroom; - info->fBaseImageType = - base_is_hdr ? SkGainmapInfo::BaseImageType::kHDR : SkGainmapInfo::BaseImageType::kSDR; - for (int i = 0; i < 3; ++i) { - if (gain_map.gainMapMin[i].d == 0 || gain_map.gainMapMax[i].d == 0 || - gain_map.gainMapGamma[i].d == 0 || gain_map.baseOffset[i].d == 0 || - gain_map.alternateOffset[i].d == 0 || gain_map.gainMapGamma[i].n == 0) { - return false; - } - const float min_log2 = FractionToFloat(gain_map.gainMapMin[i].n, gain_map.gainMapMin[i].d); - const float max_log2 = FractionToFloat(gain_map.gainMapMax[i].n, gain_map.gainMapMax[i].d); - info->fGainmapRatioMin[i] = std::exp2(min_log2); - info->fGainmapRatioMax[i] = std::exp2(max_log2); - // Numerator and denominator intentionally swapped to get 1.0/gamma. - info->fGainmapGamma[i] = - FractionToFloat(gain_map.gainMapGamma[i].d, gain_map.gainMapGamma[i].n); - const float base_offset = - FractionToFloat(gain_map.baseOffset[i].n, gain_map.baseOffset[i].d); - const float alternate_offset = - FractionToFloat(gain_map.alternateOffset[i].n, gain_map.alternateOffset[i].d); - info->fEpsilonSdr[i] = base_is_hdr ? alternate_offset : base_offset; - info->fEpsilonHdr[i] = base_is_hdr ? base_offset : alternate_offset; - - if (!gain_map.useBaseColorSpace) { - // TODO(vigneshv): Compute fGainmapMathColorSpace. - } - } - return true; -} - -} // namespace - -void AvifDecoderDeleter::operator()(crabbyavif::avifDecoder* decoder) const { - if (decoder != nullptr) { - crabbyavif::avifDecoderDestroy(decoder); - } -} - -bool SkCrabbyAvifCodec::IsAvif(const void* buffer, size_t bytesRead) { - crabbyavif::avifROData avifData = {static_cast(buffer), bytesRead}; - return crabbyavif::avifPeekCompatibleFileType(&avifData) == crabbyavif::CRABBY_AVIF_TRUE; -} - -std::unique_ptr SkCrabbyAvifCodec::MakeFromStream(std::unique_ptr stream, - Result* result, - bool gainmapOnly /*=false*/) { - SkASSERT(result); - if (!stream) { - *result = SkCodec::kInvalidInput; - return nullptr; - } - - // CrabbyAvif needs a contiguous data buffer. - sk_sp data = nullptr; - if (stream->getMemoryBase()) { - // It is safe to make without copy because we'll hold onto the stream. - data = SkData::MakeWithoutCopy(stream->getMemoryBase(), stream->getLength()); - } else { - data = SkCopyStreamToData(stream.get()); - // If we are forced to copy the stream to a data, we can go ahead and - // delete the stream. - stream.reset(nullptr); - } - return SkCrabbyAvifCodec::MakeFromData(std::move(stream), std::move(data), result, gainmapOnly); -} - -std::unique_ptr SkCrabbyAvifCodec::MakeFromData(std::unique_ptr stream, - sk_sp data, - Result* result, - bool gainmapOnly /*=false*/) { - SkASSERT(result); - - AvifDecoder avifDecoder(crabbyavif::avifDecoderCreate()); - if (avifDecoder == nullptr) { - *result = SkCodec::kInternalError; - return nullptr; - } - - // Ignore XMP and Exif to ensure that avifDecoderParse() isn't waiting for - // some tiny Exif payload hiding at the end of a file. - avifDecoder->ignoreXMP = crabbyavif::CRABBY_AVIF_TRUE; - avifDecoder->ignoreExif = crabbyavif::CRABBY_AVIF_TRUE; - - // Disable strict mode. This allows some AVIF files in the wild that are - // technically invalid according to the specification because they were - // created with older tools but can be decoded and rendered without any - // issues. - avifDecoder->strictFlags = crabbyavif::AVIF_STRICT_DISABLED; - - // TODO(vigneshv): Enable threading based on number of CPU cores available. - avifDecoder->maxThreads = 1; - - if (gainmapOnly) { - avifDecoder->imageContentToDecode = crabbyavif::AVIF_IMAGE_CONTENT_GAIN_MAP; - } - - crabbyavif::avifResult res = - crabbyavif::avifDecoderSetIOMemory(avifDecoder.get(), data->bytes(), data->size()); - if (res != crabbyavif::AVIF_RESULT_OK) { - *result = SkCodec::kInternalError; - return nullptr; - } - - res = crabbyavif::avifDecoderParse(avifDecoder.get()); - if (res != crabbyavif::AVIF_RESULT_OK) { - *result = SkCodec::kInvalidInput; - return nullptr; - } - - std::unique_ptr profile = nullptr; - // TODO(vigneshv): Get ICC Profile from the avif decoder. - - // CrabbyAvif uses MediaCodec, which always sets bitsPerComponent to 8. - const int bitsPerComponent = 8; - SkEncodedInfo::Color color; - SkEncodedInfo::Alpha alpha; - if (avifDecoder->alphaPresent && !gainmapOnly) { - color = SkEncodedInfo::kRGBA_Color; - alpha = SkEncodedInfo::kUnpremul_Alpha; - } else { - color = SkEncodedInfo::kRGB_Color; - alpha = SkEncodedInfo::kOpaque_Alpha; - } - if (gainmapOnly && !avifDecoder->image->gainMap) { - *result = SkCodec::kInvalidInput; - return nullptr; - } - crabbyavif::avifImage* image = - gainmapOnly ? avifDecoder->image->gainMap->image : avifDecoder->image; - auto width = image->width; - auto height = image->height; - if (image->transformFlags & crabbyavif::AVIF_TRANSFORM_CLAP) { - crabbyavif::avifCropRect rect; - if (crabbyavif::crabby_avifCropRectConvertCleanApertureBox( - &rect, &image->clap, width, height, image->yuvFormat, nullptr)) { - width = rect.width; - height = rect.height; - } - } - SkEncodedInfo info = SkEncodedInfo::Make( - width, height, color, alpha, bitsPerComponent, std::move(profile), image->depth); - bool animation = avifDecoder->imageCount > 1; - *result = kSuccess; - return std::unique_ptr(new SkCrabbyAvifCodec(std::move(info), - std::move(stream), - std::move(data), - std::move(avifDecoder), - kDefault_SkEncodedOrigin, - animation, - gainmapOnly)); -} - -SkCrabbyAvifCodec::SkCrabbyAvifCodec(SkEncodedInfo&& info, - std::unique_ptr stream, - sk_sp data, - AvifDecoder avifDecoder, - SkEncodedOrigin origin, - bool useAnimation, - bool gainmapOnly) - : SkScalingCodec(std::move(info), skcms_PixelFormat_RGBA_8888, std::move(stream), origin) - , fData(std::move(data)) - , fAvifDecoder(std::move(avifDecoder)) - , fUseAnimation(useAnimation) - , fGainmapOnly(gainmapOnly) {} - -int SkCrabbyAvifCodec::onGetFrameCount() { - if (!fUseAnimation) { - return 1; - } - - if (fFrameHolder.size() == 0) { - if (fAvifDecoder->imageCount <= 1) { - fUseAnimation = false; - return 1; - } - fFrameHolder.reserve(fAvifDecoder->imageCount); - for (int i = 0; i < fAvifDecoder->imageCount; i++) { - Frame* frame = fFrameHolder.appendNewFrame(fAvifDecoder->alphaPresent == - crabbyavif::CRABBY_AVIF_TRUE); - frame->setXYWH(0, 0, fAvifDecoder->image->width, fAvifDecoder->image->height); - frame->setDisposalMethod(SkCodecAnimation::DisposalMethod::kKeep); - crabbyavif::avifImageTiming timing; - avifDecoderNthImageTiming(fAvifDecoder.get(), i, &timing); - frame->setDuration(timing.duration * 1000); - frame->setRequiredFrame(SkCodec::kNoFrame); - frame->setHasAlpha(fAvifDecoder->alphaPresent == crabbyavif::CRABBY_AVIF_TRUE); - } - } - - return fFrameHolder.size(); -} - -const SkFrame* SkCrabbyAvifCodec::FrameHolder::onGetFrame(int i) const { - return static_cast(this->frame(i)); -} - -SkCrabbyAvifCodec::Frame* SkCrabbyAvifCodec::FrameHolder::appendNewFrame(bool hasAlpha) { - const int i = this->size(); - fFrames.emplace_back(i, - hasAlpha ? SkEncodedInfo::kUnpremul_Alpha : SkEncodedInfo::kOpaque_Alpha); - return &fFrames[i]; -} - -const SkCrabbyAvifCodec::Frame* SkCrabbyAvifCodec::FrameHolder::frame(int i) const { - SkASSERT(i >= 0 && i < this->size()); - return &fFrames[i]; -} - -bool SkCrabbyAvifCodec::onGetFrameInfo(int i, FrameInfo* frameInfo) const { - if (i >= fFrameHolder.size()) { - return false; - } - - const Frame* frame = fFrameHolder.frame(i); - if (!frame) { - return false; - } - - if (frameInfo) { - frame->fillIn(frameInfo, true); - } - - return true; -} - -int SkCrabbyAvifCodec::onGetRepetitionCount() { return kRepetitionCountInfinite; } - -bool SkCrabbyAvifCodec::conversionSupported(const SkImageInfo& dstInfo, - bool srcIsOpaque, - bool needsColorXform) { - return dstInfo.colorType() == kRGBA_8888_SkColorType || - dstInfo.colorType() == kRGBA_1010102_SkColorType || - dstInfo.colorType() == kRGBA_F16_SkColorType; -} - -SkCodec::Result SkCrabbyAvifCodec::onGetPixels(const SkImageInfo& dstInfo, - void* dst, - size_t dstRowBytes, - const Options& options, - int* rowsDecoded) { - if (options.fSubset) { - return kUnimplemented; - } - - crabbyavif::avifResult result = - crabbyavif::avifDecoderNthImage(fAvifDecoder.get(), options.fFrameIndex); - if (result != crabbyavif::AVIF_RESULT_OK) { - return kInvalidInput; - } - if (fGainmapOnly && !fAvifDecoder->image->gainMap) { - return kInvalidInput; - } - crabbyavif::avifImage* image = - fGainmapOnly ? fAvifDecoder->image->gainMap->image : fAvifDecoder->image; - if (this->dimensions() != dstInfo.dimensions()) { - result = crabbyavif::avifImageScale( - image, dstInfo.width(), dstInfo.height(), &fAvifDecoder->diag); - if (result != crabbyavif::AVIF_RESULT_OK) { - return kInvalidInput; - } - } - - using AvifImagePtr = - std::unique_ptr; - // cropped_image is a view into the underlying image. It can be safely deleted once the pixels - // are converted into RGB (or when it goes out of scope in one of the error paths). - AvifImagePtr cropped_image{nullptr, crabbyavif::crabby_avifImageDestroy}; - if (image->transformFlags & crabbyavif::AVIF_TRANSFORM_CLAP) { - crabbyavif::avifCropRect rect; - if (crabbyavif::crabby_avifCropRectConvertCleanApertureBox( - &rect, &image->clap, image->width, image->height, image->yuvFormat, nullptr)) { - cropped_image.reset(crabbyavif::crabby_avifImageCreateEmpty()); - result = crabbyavif::crabby_avifImageSetViewRect(cropped_image.get(), image, &rect); - if (result != crabbyavif::AVIF_RESULT_OK) { - return kInvalidInput; - } - image = cropped_image.get(); - } - } - - crabbyavif::avifRGBImage rgbImage; - crabbyavif::avifRGBImageSetDefaults(&rgbImage, image); - - switch (dstInfo.colorType()) { - case kRGBA_8888_SkColorType: - rgbImage.depth = 8; - break; - case kRGBA_F16_SkColorType: - rgbImage.depth = 16; - rgbImage.isFloat = crabbyavif::CRABBY_AVIF_TRUE; - break; - case kRGBA_1010102_SkColorType: - rgbImage.depth = 10; - rgbImage.format = crabbyavif::AVIF_RGB_FORMAT_RGBA1010102; - break; - default: - // TODO(vigneshv): Check if more color types need to be supported. - // Currently android supports at least RGB565 and BGRA8888 which is - // not supported here. - return kUnimplemented; - } - - rgbImage.pixels = static_cast(dst); - rgbImage.rowBytes = dstRowBytes; - rgbImage.chromaUpsampling = crabbyavif::AVIF_CHROMA_UPSAMPLING_FASTEST; - - result = crabbyavif::avifImageYUVToRGB(image, &rgbImage); - if (result != crabbyavif::AVIF_RESULT_OK) { - return kInvalidInput; - } - - *rowsDecoded = image->height; - return kSuccess; -} - -bool SkCrabbyAvifCodec::onGetGainmapCodec(SkGainmapInfo* info, - std::unique_ptr* gainmapCodec) { - if (!gainmapCodec || !info || !fAvifDecoder->image || !fAvifDecoder->image->gainMap || - !PopulateGainmapInfo(*fAvifDecoder->image->gainMap, info)) { - return false; - } - Result result; - *gainmapCodec = SkCrabbyAvifCodec::MakeFromData( - /*stream=*/nullptr, fData, &result, /*gainmapOnly=*/true); - return static_cast(*gainmapCodec); -} - -namespace SkAvifDecoder { -namespace CrabbyAvif { - -bool IsAvif(const void* data, size_t len) { return SkCrabbyAvifCodec::IsAvif(data, len); } - -std::unique_ptr Decode(std::unique_ptr stream, - SkCodec::Result* outResult, - SkCodecs::DecodeContext) { - SkCodec::Result resultStorage; - if (!outResult) { - outResult = &resultStorage; - } - return SkCrabbyAvifCodec::MakeFromStream(std::move(stream), outResult); -} - -std::unique_ptr Decode(sk_sp data, - SkCodec::Result* outResult, - SkCodecs::DecodeContext) { - if (!data) { - if (outResult) { - *outResult = SkCodec::kInvalidInput; - } - return nullptr; - } - return Decode(SkMemoryStream::Make(std::move(data)), outResult, nullptr); -} - -} // namespace CrabbyAvif -} // namespace SkAvifDecoder diff --git a/gfx/skia/skia/src/codec/SkCrabbyAvifCodec.h b/gfx/skia/skia/src/codec/SkCrabbyAvifCodec.h deleted file mode 100644 index 664739c380a3..000000000000 --- a/gfx/skia/skia/src/codec/SkCrabbyAvifCodec.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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 SkCrabbyAvifCodec_DEFINED -#define SkCrabbyAvifCodec_DEFINED - -#include "include/codec/SkEncodedImageFormat.h" -#include "include/codec/SkEncodedOrigin.h" -#include "include/core/SkData.h" -#include "include/core/SkRefCnt.h" -#include "include/private/SkEncodedInfo.h" -#include "src/codec/SkFrameHolder.h" -#include "src/codec/SkScalingCodec.h" - -#include -#include -#include - -namespace crabbyavif { -struct avifDecoder; -} // namespace crabbyavif - -class SkCodec; -class SkStream; -struct SkImageInfo; -struct SkGainmapInfo; - -struct AvifDecoderDeleter { - void operator()(crabbyavif::avifDecoder* decoder) const; -}; -using AvifDecoder = std::unique_ptr; - -class SkCrabbyAvifCodec : public SkScalingCodec { -public: - /* - * Returns true if an AVIF image is detected. Returns false otherwise. - */ - static bool IsAvif(const void*, size_t); - - /* - * Assumes IsAvif() was called and it returned true. - */ - static std::unique_ptr MakeFromStream(std::unique_ptr, - Result*, - bool gainmapOnly = false); - -protected: - Result onGetPixels(const SkImageInfo& dstInfo, - void* dst, - size_t dstRowBytes, - const Options& options, - int* rowsDecoded) override; - - SkEncodedImageFormat onGetEncodedFormat() const override { return SkEncodedImageFormat::kAVIF; } - - int onGetFrameCount() override; - bool onGetFrameInfo(int, FrameInfo*) const override; - int onGetRepetitionCount() override; - const SkFrameHolder* getFrameHolder() const override { return &fFrameHolder; } - bool conversionSupported(const SkImageInfo&, bool, bool) override; - bool onGetGainmapCodec(SkGainmapInfo* info, std::unique_ptr* gainmapCodec) override; - -private: - SkCrabbyAvifCodec(SkEncodedInfo&&, - std::unique_ptr, - sk_sp, - AvifDecoder, - SkEncodedOrigin, - bool, - bool); - - static std::unique_ptr MakeFromData(std::unique_ptr, - sk_sp, - Result*, - bool gainmapOnly); - - // fAvifDecoder has a pointer to this data. This should not be freed until - // the decode is completed. To ensure that, we declare this before - // fAvifDecoder. - sk_sp fData; - - AvifDecoder fAvifDecoder; - bool fUseAnimation; - bool fGainmapOnly; - - class Frame : public SkFrame { - public: - Frame(int i, SkEncodedInfo::Alpha alpha) : SkFrame(i), fReportedAlpha(alpha) {} - - protected: - SkEncodedInfo::Alpha onReportedAlpha() const override { return fReportedAlpha; } - - private: - const SkEncodedInfo::Alpha fReportedAlpha; - }; - - class FrameHolder : public SkFrameHolder { - public: - ~FrameHolder() override {} - void setScreenSize(int w, int h) { - fScreenWidth = w; - fScreenHeight = h; - } - Frame* appendNewFrame(bool hasAlpha); - const Frame* frame(int i) const; - int size() const { return static_cast(fFrames.size()); } - void reserve(int size) { fFrames.reserve(size); } - - protected: - const SkFrame* onGetFrame(int i) const override; - - private: - std::vector fFrames; - }; - - FrameHolder fFrameHolder; -}; - -#endif // SkCrabbyAvifCodec_DEFINED diff --git a/gfx/skia/skia/src/codec/SkEncodedInfo.cpp b/gfx/skia/skia/src/codec/SkEncodedInfo.cpp deleted file mode 100644 index 56f1a0259da8..000000000000 --- a/gfx/skia/skia/src/codec/SkEncodedInfo.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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 "include/private/SkEncodedInfo.h" - -#include "modules/skcms/skcms.h" - -std::unique_ptr SkEncodedInfo::ICCProfile::Make(sk_sp data) { - if (data) { - skcms_ICCProfile profile; - if (skcms_Parse(data->data(), data->size(), &profile)) { - return std::unique_ptr(new ICCProfile(profile, std::move(data))); - } - } - return nullptr; -} - -std::unique_ptr SkEncodedInfo::ICCProfile::Make( - const skcms_ICCProfile& profile) { - return std::unique_ptr(new ICCProfile(profile)); -} - -SkEncodedInfo::ICCProfile::ICCProfile(const skcms_ICCProfile& profile, sk_sp data) - : fProfile(profile) - , fData(std::move(data)) -{} diff --git a/gfx/skia/skia/src/codec/SkExif.cpp b/gfx/skia/skia/src/codec/SkExif.cpp deleted file mode 100644 index 5035a068dd92..000000000000 --- a/gfx/skia/skia/src/codec/SkExif.cpp +++ /dev/null @@ -1,364 +0,0 @@ -/* - * Copyright 2023 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "include/private/SkExif.h" - -#include "include/core/SkData.h" -#include "include/core/SkRefCnt.h" -#include "include/core/SkStream.h" -#include "src/codec/SkCodecPriv.h" -#include "src/codec/SkTiffUtility.h" -#include "src/core/SkStreamPriv.h" - -#include -#include -#include -#include -#include - -namespace SkExif { - -constexpr uint16_t kSubIFDOffsetTag = 0x8769; -constexpr uint16_t kMarkerNoteTag = 0x927c; - -static std::optional get_maker_note_hdr_headroom(sk_sp data) { - // No little endian images that specify this data have been observed. Do not add speculative - // support. - const bool kLittleEndian = false; - const uint8_t kSig[] = { - 'A', 'p', 'p', 'l', 'e', ' ', 'i', 'O', 'S', 0, 0, 1, 'M', 'M', // - }; - if (!data || data->size() < sizeof(kSig)) { - return std::nullopt; - } - if (memcmp(data->data(), kSig, sizeof(kSig)) != 0) { - return std::nullopt; - } - auto ifd = SkTiff::ImageFileDirectory::MakeFromOffset( - std::move(data), kLittleEndian, sizeof(kSig)); - if (!ifd) { - return std::nullopt; - } - - // See documentation at: - // https://developer.apple.com/documentation/appkit/images_and_pdf/applying_apple_hdr_effect_to_your_photos - bool hasMaker33 = false; - bool hasMaker48 = false; - float maker33 = 0.f; - float maker48 = 0.f; - for (uint32_t i = 0; i < ifd->getNumEntries(); ++i) { - switch (ifd->getEntryTag(i)) { - case 33: - if (!hasMaker33) { - hasMaker33 = ifd->getEntrySignedRational(i, 1, &maker33); - } - break; - case 48: - if (!hasMaker48) { - hasMaker48 = ifd->getEntrySignedRational(i, 1, &maker48); - } - break; - default: - break; - } - } - // Many images have a maker33 but not a maker48. Treat them as having maker48 of 0. - if (!hasMaker33) { - return std::nullopt; - } - float stops = 0.f; - if (maker33 < 1.0f) { - if (maker48 <= 0.01f) { - stops = -20.0f * maker48 + 1.8f; - } else { - stops = -0.101f * maker48 + 1.601f; - } - } else { - if (maker48 <= 0.01f) { - stops = -70.0f * maker48 + 3.0f; - } else { - stops = -0.303f * maker48 + 2.303f; - } - } - return std::pow(2.f, std::max(stops, 0.f)); -} - -static void parse_ifd(Metadata& exif, - sk_sp data, - std::unique_ptr ifd, - bool littleEndian, - bool isRoot) { - if (!ifd) { - return; - } - for (uint32_t i = 0; i < ifd->getNumEntries(); ++i) { - switch (ifd->getEntryTag(i)) { - case kOriginTag: { - uint16_t value = 0; - if (!exif.fOrigin.has_value() && ifd->getEntryUnsignedShort(i, 1, &value)) { - if (0 < value && value <= kLast_SkEncodedOrigin) { - exif.fOrigin = static_cast(value); - } - } - break; - } - case kMarkerNoteTag: - if (!exif.fHdrHeadroom.has_value()) { - if (auto makerNoteData = ifd->getEntryUndefinedData(i)) { - exif.fHdrHeadroom = get_maker_note_hdr_headroom(std::move(makerNoteData)); - } - } - break; - case kSubIFDOffsetTag: { - uint32_t subIfdOffset = 0; - if (isRoot && ifd->getEntryUnsignedLong(i, 1, &subIfdOffset)) { - auto subIfd = SkTiff::ImageFileDirectory::MakeFromOffset( - data, littleEndian, subIfdOffset, /*allowTruncated=*/true); - parse_ifd(exif, - data, - std::move(subIfd), - littleEndian, - /*isRoot=*/false); - } - break; - } - case kXResolutionTag: { - float value = 0.f; - if (!exif.fXResolution.has_value() && ifd->getEntryUnsignedRational(i, 1, &value)) { - exif.fXResolution = value; - } - break; - } - case kYResolutionTag: { - float value = 0.f; - if (!exif.fYResolution.has_value() && ifd->getEntryUnsignedRational(i, 1, &value)) { - exif.fYResolution = value; - } - break; - } - case kResolutionUnitTag: { - uint16_t value = 0; - if (!exif.fResolutionUnit.has_value() && ifd->getEntryUnsignedShort(i, 1, &value)) { - exif.fResolutionUnit = value; - } - break; - } - case kPixelXDimensionTag: { - // The type for this tag can be unsigned short or unsigned long (as per the Exif 2.3 - // spec, aka CIPA DC-008-2012). Support for unsigned long was added in - // https://crrev.com/817600. - uint16_t value16 = 0; - if (!exif.fPixelXDimension.has_value() && - ifd->getEntryUnsignedShort(i, 1, &value16)) { - exif.fPixelXDimension = value16; - } - uint32_t value32 = 0; - if (!exif.fPixelXDimension.has_value() && - ifd->getEntryUnsignedLong(i, 1, &value32)) { - exif.fPixelXDimension = value32; - } - break; - } - case kPixelYDimensionTag: { - uint16_t value16 = 0; - if (!exif.fPixelYDimension.has_value() && - ifd->getEntryUnsignedShort(i, 1, &value16)) { - exif.fPixelYDimension = value16; - } - uint32_t value32 = 0; - if (!exif.fPixelYDimension.has_value() && - ifd->getEntryUnsignedLong(i, 1, &value32)) { - exif.fPixelYDimension = value32; - } - break; - } - default: - break; - } - } -} - -void Parse(Metadata& metadata, const SkData* data) { - bool littleEndian = false; - uint32_t ifdOffset = 0; - if (data && SkTiff::ImageFileDirectory::ParseHeader(data, &littleEndian, &ifdOffset)) { - auto dataRef = SkData::MakeWithoutCopy(data->data(), data->size()); - auto ifd = SkTiff::ImageFileDirectory::MakeFromOffset( - dataRef, littleEndian, ifdOffset, /*allowTruncated=*/true); - parse_ifd(metadata, std::move(dataRef), std::move(ifd), littleEndian, /*isRoot=*/true); - } -} - -// Helper function to write a single IFD entry. -bool write_entry(uint16_t tag, uint16_t type, uint32_t count, uint32_t value, - uint32_t* endOfData, SkWStream* stream, SkWStream* buffer) { - bool success = true; - success &= SkWStreamWriteU16BE(stream, tag); - success &= SkWStreamWriteU16BE(stream, type); - success &= SkWStreamWriteU32BE(stream, count); - switch (tag) { - case kOriginTag: - case kResolutionUnitTag: - success &= SkWStreamWriteU16BE(stream, value); - success &= SkWStreamWriteU16BE(stream, 0); // Complete the IFD entry. - return success; - case kPixelXDimensionTag: - case kPixelYDimensionTag: - success &= SkWStreamWriteU32BE(stream, value); - return success; - case kXResolutionTag: - case kYResolutionTag: - // If the number of bytes for the type of the entry is greater than 4, we have - // to append the value to the end and replace the value section with an offset - // to where the data can be found. - success &= SkWStreamWriteU32BE(stream, *endOfData); - *endOfData += 8; - success &= SkWStreamWriteU32BE(buffer, value); // Numerator - success &= SkWStreamWriteU32BE(buffer, 1); // Denominator - return success; - case kSubIFDOffsetTag: - // This does not write the subIFD itself, just the IFD0 entry that points - // to where it is located. - success &= SkWStreamWriteU32BE(stream, value); - return success; - default: - return false; - } -} - -sk_sp WriteExif(Metadata& metadata) { - // Cannot write an IFD entry for MakerNote from the HDR Headroom. Information - // about maker48 and maker33 is lost in encode. - // See documentation at: - // https://developer.apple.com/documentation/appkit/images_and_pdf/applying_apple_hdr_effect_to_your_photos - if (metadata.fHdrHeadroom.has_value()) { - SkCodecPrintf("Cannot encode maker noter from the headroom value.\n"); - return nullptr; - } - - SkDynamicMemoryWStream stream; - // If there exists metadata that belongs in a subIFD, we will write that to a - // separate stream and append it to the end of the data, before |bufferForLargerValues|. - bool subIFDExists = false; - // This buffer will hold the values that are more than 4 bytes and will be - // appended to the end of the data after going through all available fields. - SkDynamicMemoryWStream bufferForLargerValues; - constexpr uint32_t kOffset = 8; - - // Write the IFD header. - if (!stream.write(SkTiff::kEndianBig, sizeof(SkTiff::kEndianBig))) { - return nullptr; - } - // Offset of index IFD. - if (!SkWStreamWriteU32BE(&stream, kOffset)) { - return nullptr; - } - // Count the number of valid metadata entries. - uint16_t numTags = 0; - uint16_t numSubIFDTags = 0; - if (metadata.fOrigin.has_value()) numTags++; - if (metadata.fResolutionUnit.has_value()) numTags++; - if (metadata.fXResolution.has_value()) numTags++; - if (metadata.fYResolution.has_value()) numTags++; - if (metadata.fPixelXDimension.has_value()) numSubIFDTags++; - if (metadata.fPixelYDimension.has_value()) numSubIFDTags++; - if (numSubIFDTags > 0) { - subIFDExists = true; - numTags++; - } - - // Offset that represents where data will be appended. - uint32_t endOfData = kOffset - + SkTiff::kSizeShort // Number of tags - + (SkTiff::kSizeEntry * numTags) // Entries - + SkTiff::kSizeLong; // Next IFD offset - // Offset that represents where the subIFD will start if it exists. - const uint32_t kSubIfdOffset = endOfData; - if (subIFDExists) { - endOfData += SkTiff::kSizeShort // Number of subIFD tags - + (SkTiff::kSizeEntry * numSubIFDTags) // SubIFD entries - + SkTiff::kSizeLong; // SubIFD next offset; - } - - // Write the number of tags in the IFD. - SkWStreamWriteU16BE(&stream, numTags); - - // Write the IFD entries. - if (metadata.fOrigin.has_value() - && !write_entry(kOriginTag, SkTiff::kTypeUnsignedShort, 1, - metadata.fOrigin.value(), &endOfData, &stream, - &bufferForLargerValues)) { - return nullptr; - } - - if (metadata.fResolutionUnit.has_value() - && !write_entry(kResolutionUnitTag, SkTiff::kTypeUnsignedShort, 1, - metadata.fResolutionUnit.value(), &endOfData, &stream, - &bufferForLargerValues)) { - return nullptr; - } - - if (metadata.fXResolution.has_value() - && !write_entry(kXResolutionTag, SkTiff::kTypeUnsignedRational, 1, - metadata.fXResolution.value(), &endOfData, &stream, - &bufferForLargerValues)) { - return nullptr; - } - - if (metadata.fYResolution.has_value() - && !write_entry(kYResolutionTag, SkTiff::kTypeUnsignedRational, 1, - metadata.fYResolution.value(), &endOfData, &stream, - &bufferForLargerValues)) { - return nullptr; - } - - if (subIFDExists && !write_entry(kSubIFDOffsetTag, SkTiff::kTypeUnsignedLong, 1, - kSubIfdOffset, &endOfData, &stream, &bufferForLargerValues)) { - return nullptr; - } - - // Next IFD offset (0 for no next IFD). - if (!SkWStreamWriteU32BE(&stream, 0)) { - return nullptr; - } - - // After all IFD0 data has been written, then write the SubIFD (ExifIFD). - if (subIFDExists) { - // Write the number of tags in the subIFD. - if (!SkWStreamWriteU16BE(&stream, numSubIFDTags)) { - return nullptr; - } - - if (metadata.fPixelXDimension.has_value() - && !write_entry(kPixelXDimensionTag, SkTiff::kTypeUnsignedLong, 1, - metadata.fPixelXDimension.value(), &endOfData, &stream, - &bufferForLargerValues)) { - return nullptr; - } - - if (metadata.fPixelYDimension.has_value() - && !write_entry(kPixelYDimensionTag, SkTiff::kTypeUnsignedLong, 1, - metadata.fPixelYDimension.value(), &endOfData, &stream, - &bufferForLargerValues)) { - return nullptr; - } - - // Write the SubIFD next offset (0). - if (!SkWStreamWriteU32BE(&stream, 0)) { - return nullptr; - } - } - - // Append the data buffer to the end of the stream. - if (!bufferForLargerValues.writeToStream(&stream)) { - return nullptr; - } - - return stream.detachAsData(); -} - -} // namespace SkExif diff --git a/gfx/skia/skia/src/codec/SkGainmapInfo.cpp b/gfx/skia/skia/src/codec/SkGainmapInfo.cpp deleted file mode 100644 index 961b1a964ee4..000000000000 --- a/gfx/skia/skia/src/codec/SkGainmapInfo.cpp +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "include/private/SkGainmapInfo.h" - -#include "include/core/SkColor.h" -#include "include/core/SkData.h" -#include "include/core/SkRefCnt.h" -#include "include/core/SkStream.h" -#include "src/base/SkEndian.h" -#include "src/codec/SkCodecPriv.h" -#include "src/core/SkStreamPriv.h" - -#include -#include -#include - -namespace { -constexpr uint8_t kIsMultiChannelMask = (1u << 7); -constexpr uint8_t kUseBaseColourSpaceMask = (1u << 6); -} // namespace - -static void write_rational_be(SkDynamicMemoryWStream& s, float x) { - // TODO(b/338342146): Select denominator to get maximum precision and robustness. - uint32_t denominator = 0x10000000; - if (std::abs(x) > 1.f) { - denominator = 0x1000; - } - int32_t numerator = static_cast(std::llround(static_cast(x) * denominator)); - SkWStreamWriteS32BE(&s, numerator); - SkWStreamWriteU32BE(&s, denominator); -} - -static void write_positive_rational_be(SkDynamicMemoryWStream& s, float x) { - // TODO(b/338342146): Select denominator to get maximum precision and robustness. - uint32_t denominator = 0x10000000; - if (x > 1.f) { - denominator = 0x1000; - } - uint32_t numerator = static_cast(std::llround(static_cast(x) * denominator)); - SkWStreamWriteU32BE(&s, numerator); - SkWStreamWriteU32BE(&s, denominator); -} - -static bool read_u16_be(SkStream* s, uint16_t* value) { - if (!s->readU16(value)) { - return false; - } - *value = SkEndian_SwapBE16(*value); - return true; -} - -static bool read_u32_be(SkStream* s, uint32_t* value) { - if (!s->readU32(value)) { - return false; - } - *value = SkEndian_SwapBE32(*value); - return true; -} - -static bool read_s32_be(SkStream* s, int32_t* value) { - if (!s->readS32(value)) { - return false; - } - *value = SkEndian_SwapBE32(*value); - return true; -} - -static bool read_rational_be(SkStream* s, float* value) { - int32_t numerator = 0; - uint32_t denominator = 0; - if (!read_s32_be(s, &numerator)) { - return false; - } - if (!read_u32_be(s, &denominator)) { - return false; - } - *value = static_cast(static_cast(numerator) / static_cast(denominator)); - return true; -} - -static bool read_positive_rational_be(SkStream* s, float* value) { - uint32_t numerator = 0; - uint32_t denominator = 0; - if (!read_u32_be(s, &numerator)) { - return false; - } - if (!read_u32_be(s, &denominator)) { - return false; - } - *value = static_cast(static_cast(numerator) / static_cast(denominator)); - return true; -} - -static bool read_iso_gainmap_version(SkStream* s) { - // Ensure minimum version is 0. - uint16_t minimum_version = 0; - if (!read_u16_be(s, &minimum_version)) { - SkCodecPrintf("Failed to read ISO 21496-1 minimum version.\n"); - return false; - } - if (minimum_version != 0) { - SkCodecPrintf("Unsupported ISO 21496-1 minimum version.\n"); - return false; - } - - // Ensure writer version is present. No value is invalid. - uint16_t writer_version = 0; - if (!read_u16_be(s, &writer_version)) { - SkCodecPrintf("Failed to read ISO 21496-1 version.\n"); - return false; - } - - return true; -} - -static bool read_iso_gainmap_info(SkStream* s, SkGainmapInfo& info) { - if (!read_iso_gainmap_version(s)) { - SkCodecPrintf("Failed to read ISO 21496-1 version.\n"); - return false; - } - - uint8_t flags = 0; - if (!s->readU8(&flags)) { - SkCodecPrintf("Failed to read ISO 21496-1 flags.\n"); - return false; - } - bool isMultiChannel = (flags & kIsMultiChannelMask) != 0; - bool useBaseColourSpace = (flags & kUseBaseColourSpaceMask) != 0; - - float baseHdrHeadroom = 0.f; - if (!read_positive_rational_be(s, &baseHdrHeadroom)) { - SkCodecPrintf("Failed to read ISO 21496-1 base HDR headroom.\n"); - return false; - } - float altrHdrHeadroom = 0.f; - if (!read_positive_rational_be(s, &altrHdrHeadroom)) { - SkCodecPrintf("Failed to read ISO 21496-1 altr HDR headroom.\n"); - return false; - } - - float gainMapMin[3] = {0.f}; - float gainMapMax[3] = {0.f}; - float gamma[3] = {0.f}; - float baseOffset[3] = {0.f}; - float altrOffset[3] = {0.f}; - - int channelCount = isMultiChannel ? 3 : 1; - for (int i = 0; i < channelCount; ++i) { - if (!read_rational_be(s, gainMapMin + i)) { - SkCodecPrintf("Failed to read ISO 21496-1 gainmap minimum.\n"); - return false; - } - if (!read_rational_be(s, gainMapMax + i)) { - SkCodecPrintf("Failed to read ISO 21496-1 gainmap maximum.\n"); - return false; - } - if (!read_positive_rational_be(s, gamma + i)) { - SkCodecPrintf("Failed to read ISO 21496-1 gamma.\n"); - return false; - } - if (!read_rational_be(s, baseOffset + i)) { - SkCodecPrintf("Failed to read ISO 21496-1 base offset.\n"); - return false; - } - if (!read_rational_be(s, altrOffset + i)) { - SkCodecPrintf("Failed to read ISO 21496-1 altr offset.\n"); - return false; - } - } - - info = SkGainmapInfo(); - if (!useBaseColourSpace) { - info.fGainmapMathColorSpace = SkColorSpace::MakeSRGB(); - } - if (baseHdrHeadroom < altrHdrHeadroom) { - info.fBaseImageType = SkGainmapInfo::BaseImageType::kSDR; - info.fDisplayRatioSdr = std::exp2(baseHdrHeadroom); - info.fDisplayRatioHdr = std::exp2(altrHdrHeadroom); - } else { - info.fBaseImageType = SkGainmapInfo::BaseImageType::kHDR; - info.fDisplayRatioHdr = std::exp2(baseHdrHeadroom); - info.fDisplayRatioSdr = std::exp2(altrHdrHeadroom); - } - for (int i = 0; i < 3; ++i) { - int j = i >= channelCount ? 0 : i; - info.fGainmapRatioMin[i] = std::exp2(gainMapMin[j]); - info.fGainmapRatioMax[i] = std::exp2(gainMapMax[j]); - info.fGainmapGamma[i] = 1.f / gamma[j]; - switch (info.fBaseImageType) { - case SkGainmapInfo::BaseImageType::kSDR: - info.fEpsilonSdr[i] = baseOffset[j]; - info.fEpsilonHdr[i] = altrOffset[j]; - break; - case SkGainmapInfo::BaseImageType::kHDR: - info.fEpsilonHdr[i] = baseOffset[j]; - info.fEpsilonSdr[i] = altrOffset[j]; - break; - } - } - return true; -} - -bool SkGainmapInfo::isUltraHDRv1Compatible() const { - // UltraHDR v1 supports having the base image be HDR in theory, but it is largely - // untested. - if (fBaseImageType == BaseImageType::kHDR) { - return false; - } - // UltraHDR v1 doesn't support a non-base gainmap math color space. - if (fGainmapMathColorSpace) { - return false; - } - return true; -} - -bool SkGainmapInfo::ParseVersion(const SkData* data) { - if (!data) { - return false; - } - auto s = SkMemoryStream::MakeDirect(data->data(), data->size()); - return read_iso_gainmap_version(s.get()); -} - -bool SkGainmapInfo::Parse(const SkData* data, SkGainmapInfo& info) { - if (!data) { - return false; - } - auto s = SkMemoryStream::MakeDirect(data->data(), data->size()); - return read_iso_gainmap_info(s.get(), info); -} - -sk_sp SkGainmapInfo::SerializeVersion() { - SkDynamicMemoryWStream s; - SkWStreamWriteU16BE(&s, 0); // Minimum reader version - SkWStreamWriteU16BE(&s, 0); // Writer version - return s.detachAsData(); -} - -static bool is_single_channel(SkColor4f c) { return c.fR == c.fG && c.fG == c.fB; }; - -sk_sp SkGainmapInfo::serialize() const { - SkDynamicMemoryWStream s; - // Version. - SkWStreamWriteU16BE(&s, 0); // Minimum reader version - SkWStreamWriteU16BE(&s, 0); // Writer version - - // Flags. - bool all_single_channel = is_single_channel(fGainmapRatioMin) && - is_single_channel(fGainmapRatioMax) && - is_single_channel(fGainmapGamma) && is_single_channel(fEpsilonSdr) && - is_single_channel(fEpsilonHdr); - uint8_t flags = 0; - if (!fGainmapMathColorSpace) { - flags |= kUseBaseColourSpaceMask; - } - if (!all_single_channel) { - flags |= kIsMultiChannelMask; - } - s.write8(flags); - - // Base and altr headroom. - switch (fBaseImageType) { - case SkGainmapInfo::BaseImageType::kSDR: - write_positive_rational_be(s, std::log2(fDisplayRatioSdr)); - write_positive_rational_be(s, std::log2(fDisplayRatioHdr)); - break; - case SkGainmapInfo::BaseImageType::kHDR: - write_positive_rational_be(s, std::log2(fDisplayRatioHdr)); - write_positive_rational_be(s, std::log2(fDisplayRatioSdr)); - break; - } - - // Per-channel information. - for (int i = 0; i < (all_single_channel ? 1 : 3); ++i) { - write_rational_be(s, std::log2(fGainmapRatioMin[i])); - write_rational_be(s, std::log2(fGainmapRatioMax[i])); - write_positive_rational_be(s, 1.f / fGainmapGamma[i]); - switch (fBaseImageType) { - case SkGainmapInfo::BaseImageType::kSDR: - write_rational_be(s, fEpsilonSdr[i]); - write_rational_be(s, fEpsilonHdr[i]); - break; - case SkGainmapInfo::BaseImageType::kHDR: - write_rational_be(s, fEpsilonHdr[i]); - write_rational_be(s, fEpsilonSdr[i]); - break; - } - } - return s.detachAsData(); -} diff --git a/gfx/skia/skia/src/codec/SkHeifCodec.cpp b/gfx/skia/skia/src/codec/SkHeifCodec.cpp deleted file mode 100644 index 81e016ff0811..000000000000 --- a/gfx/skia/skia/src/codec/SkHeifCodec.cpp +++ /dev/null @@ -1,568 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#include "src/codec/SkHeifCodec.h" - -#include "include/codec/SkCodec.h" -#include "include/codec/SkEncodedImageFormat.h" -#include "include/core/SkStream.h" -#include "include/core/SkTypes.h" -#include "include/private/SkColorData.h" -#include "include/private/base/SkTemplates.h" -#include "src/base/SkEndian.h" -#include "src/codec/SkCodecPriv.h" - -#define FOURCC(c1, c2, c3, c4) \ - ((c1) << 24 | (c2) << 16 | (c3) << 8 | (c4)) - -bool SkHeifCodec::IsSupported(const void* buffer, size_t bytesRead, - SkEncodedImageFormat* format) { - // Parse the ftyp box up to bytesRead to determine if this is HEIF or AVIF. - // Any valid ftyp box should have at least 8 bytes. - if (bytesRead < 8) { - return false; - } - - const uint32_t* ptr = (const uint32_t*)buffer; - uint64_t chunkSize = SkEndian_SwapBE32(ptr[0]); - uint32_t chunkType = SkEndian_SwapBE32(ptr[1]); - - if (chunkType != FOURCC('f', 't', 'y', 'p')) { - return false; - } - - int64_t offset = 8; - if (chunkSize == 1) { - // This indicates that the next 8 bytes represent the chunk size, - // and chunk data comes after that. - if (bytesRead < 16) { - return false; - } - auto* chunkSizePtr = SkTAddOffset(buffer, offset); - chunkSize = SkEndian_SwapBE64(*chunkSizePtr); - if (chunkSize < 16) { - // The smallest valid chunk is 16 bytes long in this case. - return false; - } - offset += 8; - } else if (chunkSize < 8) { - // The smallest valid chunk is 8 bytes long. - return false; - } - - if (chunkSize > bytesRead) { - chunkSize = bytesRead; - } - int64_t chunkDataSize = chunkSize - offset; - // It should at least have major brand (4-byte) and minor version (4-bytes). - // The rest of the chunk (if any) is a list of (4-byte) compatible brands. - if (chunkDataSize < 8) { - return false; - } - - uint32_t numCompatibleBrands = (chunkDataSize - 8) / 4; - bool isHeif = false; - for (size_t i = 0; i < numCompatibleBrands + 2; ++i) { - if (i == 1) { - // Skip this index, it refers to the minorVersion, - // not a brand. - continue; - } - auto* brandPtr = SkTAddOffset(buffer, offset + 4 * i); - uint32_t brand = SkEndian_SwapBE32(*brandPtr); - if (brand == FOURCC('m', 'i', 'f', '1') || brand == FOURCC('h', 'e', 'i', 'c') - || brand == FOURCC('m', 's', 'f', '1') || brand == FOURCC('h', 'e', 'v', 'c') - || brand == FOURCC('a', 'v', 'i', 'f') || brand == FOURCC('a', 'v', 'i', 's')) { - // AVIF files could have "mif1" as the major brand. So we cannot - // distinguish whether the image is AVIF or HEIC just based on the - // "mif1" brand. So wait until we see a specific avif brand to - // determine whether it is AVIF or HEIC. - isHeif = true; - if (brand == FOURCC('a', 'v', 'i', 'f') - || brand == FOURCC('a', 'v', 'i', 's')) { - if (format != nullptr) { - *format = SkEncodedImageFormat::kAVIF; - } - return true; - } - } - } - if (isHeif) { - if (format != nullptr) { - *format = SkEncodedImageFormat::kHEIF; - } - return true; - } - return false; -} - -static SkEncodedOrigin get_orientation(const HeifFrameInfo& frameInfo) { - switch (frameInfo.mRotationAngle) { - case 0: return kTopLeft_SkEncodedOrigin; - case 90: return kRightTop_SkEncodedOrigin; - case 180: return kBottomRight_SkEncodedOrigin; - case 270: return kLeftBottom_SkEncodedOrigin; - } - return kDefault_SkEncodedOrigin; -} - -struct SkHeifStreamWrapper : public HeifStream { - SkHeifStreamWrapper(SkStream* stream) : fStream(stream) {} - - ~SkHeifStreamWrapper() override {} - - size_t read(void* buffer, size_t size) override { - return fStream->read(buffer, size); - } - - bool rewind() override { - return fStream->rewind(); - } - - bool seek(size_t position) override { - return fStream->seek(position); - } - - bool hasLength() const override { - return fStream->hasLength(); - } - - size_t getLength() const override { - return fStream->getLength(); - } - -private: - std::unique_ptr fStream; -}; - -static void releaseProc(const void* ptr, void* context) { - delete reinterpret_cast*>(context); -} - -std::unique_ptr SkHeifCodec::MakeFromStream(std::unique_ptr stream, - SkCodec::SelectionPolicy selectionPolicy, Result* result) { - SkASSERT(result); - if (!stream) { - *result = SkCodec::kInvalidInput; - return nullptr; - } - std::unique_ptr heifDecoder(createHeifDecoder()); - if (heifDecoder == nullptr) { - *result = SkCodec::kInternalError; - return nullptr; - } - - constexpr size_t bytesToRead = MinBufferedBytesNeeded(); - char buffer[bytesToRead]; - size_t bytesRead = stream->peek(buffer, bytesToRead); - if (0 == bytesRead) { - // It is possible the stream does not support peeking, but does support rewinding. - // Attempt to read() and pass the actual amount read to the decoder. - bytesRead = stream->read(buffer, bytesToRead); - if (!stream->rewind()) { - SkCodecPrintf("Encoded image data could not peek or rewind to determine format!\n"); - *result = kCouldNotRewind; - return nullptr; - } - } - - SkEncodedImageFormat format; - if (!SkHeifCodec::IsSupported(buffer, bytesRead, &format)) { - SkCodecPrintf("Failed to get format despite earlier detecting it"); - *result = SkCodec::kInternalError; - return nullptr; - } - - HeifFrameInfo heifInfo; - if (!heifDecoder->init(new SkHeifStreamWrapper(stream.release()), &heifInfo)) { - *result = SkCodec::kInvalidInput; - return nullptr; - } - - size_t frameCount = 1; - if (selectionPolicy == SkCodec::SelectionPolicy::kPreferAnimation) { - HeifFrameInfo sequenceInfo; - if (heifDecoder->getSequenceInfo(&sequenceInfo, &frameCount) && - frameCount > 1) { - heifInfo = std::move(sequenceInfo); - } - } - - std::unique_ptr profile = nullptr; - if (heifInfo.mIccData.size() > 0) { - auto iccData = new std::vector(std::move(heifInfo.mIccData)); - auto icc = SkData::MakeWithProc(iccData->data(), iccData->size(), releaseProc, iccData); - profile = SkEncodedInfo::ICCProfile::Make(std::move(icc)); - } - if (profile && profile->profile()->data_color_space != skcms_Signature_RGB) { - // This will result in sRGB. - profile = nullptr; - } - - uint8_t colorDepth = heifDecoder->getColorDepth(); - - SkEncodedInfo info = SkEncodedInfo::Make(heifInfo.mWidth, heifInfo.mHeight, - SkEncodedInfo::kYUV_Color, SkEncodedInfo::kOpaque_Alpha, - /*bitsPerComponent*/ 8, std::move(profile), colorDepth); - SkEncodedOrigin orientation = get_orientation(heifInfo); - - *result = SkCodec::kSuccess; - return std::unique_ptr(new SkHeifCodec( - std::move(info), heifDecoder.release(), orientation, frameCount > 1, format)); -} - -SkHeifCodec::SkHeifCodec( - SkEncodedInfo&& info, - HeifDecoder* heifDecoder, - SkEncodedOrigin origin, - bool useAnimation, - SkEncodedImageFormat format) - : INHERITED(std::move(info), skcms_PixelFormat_RGBA_8888, nullptr, origin) - , fHeifDecoder(heifDecoder) - , fSwizzleSrcRow(nullptr) - , fColorXformSrcRow(nullptr) - , fUseAnimation(useAnimation) - , fFormat(format) -{} - -bool SkHeifCodec::conversionSupported(const SkImageInfo& dstInfo, bool srcIsOpaque, - bool needsColorXform) { - SkASSERT(srcIsOpaque); - - if (kUnknown_SkAlphaType == dstInfo.alphaType()) { - return false; - } - - if (kOpaque_SkAlphaType != dstInfo.alphaType()) { - SkCodecPrintf("Warning: an opaque image should be decoded as opaque " - "- it is being decoded as non-opaque, which will draw slower\n"); - } - - uint8_t colorDepth = fHeifDecoder->getColorDepth(); - switch (dstInfo.colorType()) { - case kRGBA_8888_SkColorType: - this->setSrcXformFormat(skcms_PixelFormat_RGBA_8888); - return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888); - - case kBGRA_8888_SkColorType: - this->setSrcXformFormat(skcms_PixelFormat_RGBA_8888); - return fHeifDecoder->setOutputColor(kHeifColorFormat_BGRA_8888); - - case kRGB_565_SkColorType: - this->setSrcXformFormat(skcms_PixelFormat_RGBA_8888); - if (needsColorXform) { - return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888); - } else { - return fHeifDecoder->setOutputColor(kHeifColorFormat_RGB565); - } - - case kRGBA_1010102_SkColorType: - this->setSrcXformFormat(skcms_PixelFormat_RGBA_1010102); - return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_1010102); - - case kRGBA_F16_SkColorType: - SkASSERT(needsColorXform); - if (srcIsOpaque && colorDepth == 10) { - this->setSrcXformFormat(skcms_PixelFormat_RGBA_1010102); - return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_1010102); - } else { - this->setSrcXformFormat(skcms_PixelFormat_RGBA_8888); - return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888); - } - - default: - return false; - } -} - -int SkHeifCodec::readRows(const SkImageInfo& dstInfo, void* dst, size_t rowBytes, int count, - const Options& opts) { - // When fSwizzleSrcRow is non-null, it means that we need to swizzle. In this case, - // we will always decode into fSwizzlerSrcRow before swizzling into the next buffer. - // We can never swizzle "in place" because the swizzler may perform sampling and/or - // subsetting. - // When fColorXformSrcRow is non-null, it means that we need to color xform and that - // we cannot color xform "in place" (many times we can, but not when the dst is F16). - // In this case, we will color xform from fColorXformSrcRow into the dst. - uint8_t* decodeDst = (uint8_t*) dst; - uint32_t* swizzleDst = (uint32_t*) dst; - size_t decodeDstRowBytes = rowBytes; - size_t swizzleDstRowBytes = rowBytes; - int dstWidth = opts.fSubset ? opts.fSubset->width() : dstInfo.width(); - if (fSwizzleSrcRow && fColorXformSrcRow) { - decodeDst = fSwizzleSrcRow; - swizzleDst = fColorXformSrcRow; - decodeDstRowBytes = 0; - swizzleDstRowBytes = 0; - dstWidth = fSwizzler->swizzleWidth(); - } else if (fColorXformSrcRow) { - decodeDst = (uint8_t*) fColorXformSrcRow; - swizzleDst = fColorXformSrcRow; - decodeDstRowBytes = 0; - swizzleDstRowBytes = 0; - } else if (fSwizzleSrcRow) { - decodeDst = fSwizzleSrcRow; - decodeDstRowBytes = 0; - dstWidth = fSwizzler->swizzleWidth(); - } - - for (int y = 0; y < count; y++) { - if (!fHeifDecoder->getScanline(decodeDst)) { - return y; - } - - if (fSwizzler) { - fSwizzler->swizzle(swizzleDst, decodeDst); - } - - if (this->colorXform()) { - this->applyColorXform(dst, swizzleDst, dstWidth); - dst = SkTAddOffset(dst, rowBytes); - } - - decodeDst = SkTAddOffset(decodeDst, decodeDstRowBytes); - swizzleDst = SkTAddOffset(swizzleDst, swizzleDstRowBytes); - } - - return count; -} - -int SkHeifCodec::onGetFrameCount() { - if (!fUseAnimation) { - return 1; - } - - if (fFrameHolder.size() == 0) { - size_t frameCount; - HeifFrameInfo frameInfo; - if (!fHeifDecoder->getSequenceInfo(&frameInfo, &frameCount) - || frameCount <= 1) { - fUseAnimation = false; - return 1; - } - fFrameHolder.reserve(frameCount); - for (size_t i = 0; i < frameCount; i++) { - Frame* frame = fFrameHolder.appendNewFrame(); - frame->setXYWH(0, 0, frameInfo.mWidth, frameInfo.mHeight); - frame->setDisposalMethod(SkCodecAnimation::DisposalMethod::kKeep); - // Currently we don't know the duration until the frame is actually - // decoded (onGetFrameInfo is also called before frame is decoded). - // For now, fill it base on the value reported for the sequence. - frame->setDuration(frameInfo.mDurationUs / 1000); - frame->setRequiredFrame(SkCodec::kNoFrame); - frame->setHasAlpha(false); - } - } - - return fFrameHolder.size(); -} - -const SkFrame* SkHeifCodec::FrameHolder::onGetFrame(int i) const { - return static_cast(this->frame(i)); -} - -SkHeifCodec::Frame* SkHeifCodec::FrameHolder::appendNewFrame() { - const int i = this->size(); - fFrames.emplace_back(i); // TODO: need to handle frame duration here - return &fFrames[i]; -} - -const SkHeifCodec::Frame* SkHeifCodec::FrameHolder::frame(int i) const { - SkASSERT(i >= 0 && i < this->size()); - return &fFrames[i]; -} - -SkHeifCodec::Frame* SkHeifCodec::FrameHolder::editFrameAt(int i) { - SkASSERT(i >= 0 && i < this->size()); - return &fFrames[i]; -} - -bool SkHeifCodec::onGetFrameInfo(int i, FrameInfo* frameInfo) const { - if (i >= fFrameHolder.size()) { - return false; - } - - const Frame* frame = fFrameHolder.frame(i); - if (!frame) { - return false; - } - - if (frameInfo) { - frame->fillIn(frameInfo, true); - } - - return true; -} - -int SkHeifCodec::onGetRepetitionCount() { - return kRepetitionCountInfinite; -} - -/* - * Performs the heif decode - */ -SkCodec::Result SkHeifCodec::onGetPixels(const SkImageInfo& dstInfo, - void* dst, size_t dstRowBytes, - const Options& options, - int* rowsDecoded) { - if (options.fSubset) { - // Not supporting subsets on this path for now. - // TODO: if the heif has tiles, we can support subset here, but - // need to retrieve tile config from metadata retriever first. - return kUnimplemented; - } - - bool success; - if (fUseAnimation) { - success = fHeifDecoder->decodeSequence(options.fFrameIndex, &fFrameInfo); - fFrameHolder.editFrameAt(options.fFrameIndex)->setDuration( - fFrameInfo.mDurationUs / 1000); - } else { - success = fHeifDecoder->decode(&fFrameInfo); - } - - if (!success) { - return kInvalidInput; - } - - fSwizzler.reset(nullptr); - this->allocateStorage(dstInfo); - - int rows = this->readRows(dstInfo, dst, dstRowBytes, dstInfo.height(), options); - if (rows < dstInfo.height()) { - *rowsDecoded = rows; - return kIncompleteInput; - } - - return kSuccess; -} - -void SkHeifCodec::allocateStorage(const SkImageInfo& dstInfo) { - int dstWidth = dstInfo.width(); - - size_t swizzleBytes = 0; - if (fSwizzler) { - swizzleBytes = fFrameInfo.mBytesPerPixel * fFrameInfo.mWidth; - dstWidth = fSwizzler->swizzleWidth(); - SkASSERT(!this->colorXform() || SkIsAlign4(swizzleBytes)); - } - - size_t xformBytes = 0; - if (this->colorXform() && (kRGBA_F16_SkColorType == dstInfo.colorType() || - kRGB_565_SkColorType == dstInfo.colorType())) { - xformBytes = dstWidth * sizeof(uint32_t); - } - - size_t totalBytes = swizzleBytes + xformBytes; - fStorage.reset(totalBytes); - if (totalBytes > 0) { - fSwizzleSrcRow = (swizzleBytes > 0) ? fStorage.get() : nullptr; - fColorXformSrcRow = (xformBytes > 0) ? - SkTAddOffset(fStorage.get(), swizzleBytes) : nullptr; - } -} - -void SkHeifCodec::initializeSwizzler( - const SkImageInfo& dstInfo, const Options& options) { - SkImageInfo swizzlerDstInfo = dstInfo; - switch (this->getSrcXformFormat()) { - case skcms_PixelFormat_RGBA_8888: - swizzlerDstInfo = swizzlerDstInfo.makeColorType(kRGBA_8888_SkColorType); - break; - case skcms_PixelFormat_RGBA_1010102: - swizzlerDstInfo = swizzlerDstInfo.makeColorType(kRGBA_1010102_SkColorType); - break; - default: - SkASSERT(false); - } - - int srcBPP = 4; - if (dstInfo.colorType() == kRGB_565_SkColorType && !this->colorXform()) { - srcBPP = 2; - } - - fSwizzler = SkSwizzler::MakeSimple(srcBPP, swizzlerDstInfo, options); - SkASSERT(fSwizzler); -} - -SkSampler* SkHeifCodec::getSampler(bool createIfNecessary) { - if (!createIfNecessary || fSwizzler) { - SkASSERT(!fSwizzler || (fSwizzleSrcRow && fStorage.get() == fSwizzleSrcRow)); - return fSwizzler.get(); - } - - this->initializeSwizzler(this->dstInfo(), this->options()); - this->allocateStorage(this->dstInfo()); - return fSwizzler.get(); -} - -bool SkHeifCodec::onRewind() { - fSwizzler.reset(nullptr); - fSwizzleSrcRow = nullptr; - fColorXformSrcRow = nullptr; - fStorage.reset(); - - return true; -} - -SkCodec::Result SkHeifCodec::onStartScanlineDecode( - const SkImageInfo& dstInfo, const Options& options) { - // TODO: For now, just decode the whole thing even when there is a subset. - // If the heif image has tiles, we could potentially do this much faster, - // but the tile configuration needs to be retrieved from the metadata. - if (!fHeifDecoder->decode(&fFrameInfo)) { - return kInvalidInput; - } - - if (options.fSubset) { - this->initializeSwizzler(dstInfo, options); - } else { - fSwizzler.reset(nullptr); - } - - this->allocateStorage(dstInfo); - - return kSuccess; -} - -int SkHeifCodec::onGetScanlines(void* dst, int count, size_t dstRowBytes) { - return this->readRows(this->dstInfo(), dst, dstRowBytes, count, this->options()); -} - -bool SkHeifCodec::onSkipScanlines(int count) { - return count == (int) fHeifDecoder->skipScanlines(count); -} - -namespace SkHeifDecoder { -bool IsHeif(const void* data, size_t len) { - return SkHeifCodec::IsSupported(data, len, nullptr); -} - -std::unique_ptr Decode(std::unique_ptr stream, - SkCodec::Result* outResult, - SkCodecs::DecodeContext ctx) { - SkASSERT(ctx); - SkCodec::Result resultStorage; - if (!outResult) { - outResult = &resultStorage; - } - auto policy = static_cast(ctx); - return SkHeifCodec::MakeFromStream(std::move(stream), *policy, outResult); -} - -std::unique_ptr Decode(sk_sp data, - SkCodec::Result* outResult, - SkCodecs::DecodeContext ctx) { - if (!data) { - if (outResult) { - *outResult = SkCodec::kInvalidInput; - } - return nullptr; - } - return Decode(SkMemoryStream::Make(std::move(data)), outResult, ctx); -} -} // namespace SkHeifDecoder - diff --git a/gfx/skia/skia/src/codec/SkHeifCodec.h b/gfx/skia/skia/src/codec/SkHeifCodec.h deleted file mode 100644 index 5bca89dfb078..000000000000 --- a/gfx/skia/skia/src/codec/SkHeifCodec.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkHeifCodec_DEFINED -#define SkHeifCodec_DEFINED - -#include "include/codec/SkCodec.h" -#include "include/codec/SkEncodedOrigin.h" -#include "include/core/SkImageInfo.h" -#include "include/core/SkStream.h" -#include "include/private/base/SkTemplates.h" -#include "src/codec/SkFrameHolder.h" -#include "src/codec/SkSwizzler.h" - -#if __has_include("HeifDecoderAPI.h") - #include "HeifDecoderAPI.h" -#else - #include "src/codec/SkStubHeifDecoderAPI.h" -#endif - -class SkHeifCodec : public SkCodec { -public: - /* - * Returns true if one of kHEIF or kAVIF images were detected. If |format| - * is not nullptr, it will contain the detected format. Returns false - * otherwise. - */ - static bool IsSupported(const void*, size_t, SkEncodedImageFormat* format); - - /* - * Assumes IsSupported was called and it returned a non-nullopt value. - */ - static std::unique_ptr MakeFromStream( - std::unique_ptr, SkCodec::SelectionPolicy selectionPolicy, - Result*); - -protected: - - Result onGetPixels( - const SkImageInfo& dstInfo, - void* dst, size_t dstRowBytes, - const Options& options, - int* rowsDecoded) override; - - SkEncodedImageFormat onGetEncodedFormat() const override { - return fFormat; - } - - int onGetFrameCount() override; - bool onGetFrameInfo(int, FrameInfo*) const override; - int onGetRepetitionCount() override; - const SkFrameHolder* getFrameHolder() const override { - return &fFrameHolder; - } - - bool conversionSupported(const SkImageInfo&, bool, bool) override; - - bool onRewind() override; - -private: - /* - * Creates an instance of the decoder - * Called only by NewFromStream - */ - SkHeifCodec(SkEncodedInfo&&, HeifDecoder*, SkEncodedOrigin, bool animation, - SkEncodedImageFormat); - - void initializeSwizzler(const SkImageInfo& dstInfo, const Options& options); - void allocateStorage(const SkImageInfo& dstInfo); - int readRows(const SkImageInfo& dstInfo, void* dst, - size_t rowBytes, int count, const Options&); - - /* - * Scanline decoding. - */ - SkSampler* getSampler(bool createIfNecessary) override; - Result onStartScanlineDecode(const SkImageInfo& dstInfo, - const Options& options) override; - int onGetScanlines(void* dst, int count, size_t rowBytes) override; - bool onSkipScanlines(int count) override; - - std::unique_ptr fHeifDecoder; - HeifFrameInfo fFrameInfo; - skia_private::AutoTMalloc fStorage; - uint8_t* fSwizzleSrcRow; - uint32_t* fColorXformSrcRow; - - std::unique_ptr fSwizzler; - bool fUseAnimation; - const SkEncodedImageFormat fFormat; - - class Frame : public SkFrame { - public: - Frame(int i) : INHERITED(i) {} - - protected: - SkEncodedInfo::Alpha onReportedAlpha() const override { - return SkEncodedInfo::Alpha::kOpaque_Alpha; - } - - private: - using INHERITED = SkFrame; - }; - - class FrameHolder : public SkFrameHolder { - public: - ~FrameHolder() override {} - void setScreenSize(int w, int h) { - fScreenWidth = w; - fScreenHeight = h; - } - Frame* appendNewFrame(); - const Frame* frame(int i) const; - Frame* editFrameAt(int i); - int size() const { - return static_cast(fFrames.size()); - } - void reserve(int size) { - fFrames.reserve(size); - } - - protected: - const SkFrame* onGetFrame(int i) const override; - - private: - std::vector fFrames; - }; - - FrameHolder fFrameHolder; - using INHERITED = SkCodec; -}; - -#endif // SkHeifCodec_DEFINED diff --git a/gfx/skia/skia/src/codec/SkIcoCodec.cpp b/gfx/skia/skia/src/codec/SkIcoCodec.cpp deleted file mode 100644 index 9418aabcae04..000000000000 --- a/gfx/skia/skia/src/codec/SkIcoCodec.cpp +++ /dev/null @@ -1,439 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "src/codec/SkIcoCodec.h" - -#include "include/codec/SkIcoDecoder.h" -#include "include/codec/SkPngDecoder.h" -#include "include/core/SkData.h" -#include "include/core/SkImageInfo.h" -#include "include/core/SkRefCnt.h" -#include "include/core/SkStream.h" -#include "include/private/SkEncodedInfo.h" -#include "include/private/base/SkMalloc.h" -#include "include/private/base/SkTemplates.h" -#include "src/base/SkTSort.h" -#include "src/codec/SkBmpCodec.h" -#include "src/codec/SkCodecPriv.h" -#include "src/core/SkStreamPriv.h" - -#include "modules/skcms/skcms.h" -#include -#include -#include -#include - -using namespace skia_private; - -class SkSampler; - -/* - * Checks the start of the stream to see if the image is an Ico or Cur - */ -bool SkIcoCodec::IsIco(const void* buffer, size_t bytesRead) { - const char icoSig[] = { '\x00', '\x00', '\x01', '\x00' }; - const char curSig[] = { '\x00', '\x00', '\x02', '\x00' }; - return bytesRead >= sizeof(icoSig) && - (!memcmp(buffer, icoSig, sizeof(icoSig)) || - !memcmp(buffer, curSig, sizeof(curSig))); -} - -std::unique_ptr SkIcoCodec::MakeFromStream(std::unique_ptr stream, - Result* result) { - SkASSERT(result); - if (!stream) { - *result = SkCodec::kInvalidInput; - return nullptr; - } - // It is helpful to have the entire stream in a contiguous buffer. In some cases, - // this is already the case anyway, so this method is faster. In others, this is - // safer than the old method, which required allocating a block of memory whose - // byte size is stored in the stream as a uint32_t, and may result in a large or - // failed allocation. - sk_sp data = nullptr; - if (stream->getMemoryBase()) { - // It is safe to make without copy because we'll hold onto the stream. - data = SkData::MakeWithoutCopy(stream->getMemoryBase(), stream->getLength()); - } else { - data = SkCopyStreamToData(stream.get()); - - // If we are forced to copy the stream to a data, we can go ahead and delete the stream. - stream.reset(nullptr); - } - - // Header size constants - constexpr uint32_t kIcoDirectoryBytes = 6; - constexpr uint32_t kIcoDirEntryBytes = 16; - - // Read the directory header - if (data->size() < kIcoDirectoryBytes) { - SkCodecPrintf("Error: unable to read ico directory header.\n"); - *result = kIncompleteInput; - return nullptr; - } - - // Process the directory header - const uint16_t numImages = get_short(data->bytes(), 4); - if (0 == numImages) { - SkCodecPrintf("Error: No images embedded in ico.\n"); - *result = kInvalidInput; - return nullptr; - } - - // This structure is used to represent the vital information about entries - // in the directory header. We will obtain this information for each - // directory entry. - struct Entry { - uint32_t offset; - uint32_t size; - }; - UniqueVoidPtr dirEntryBuffer(sk_malloc_canfail(sizeof(Entry) * numImages)); - if (!dirEntryBuffer) { - SkCodecPrintf("Error: OOM allocating ICO directory for %i images.\n", - numImages); - *result = kInternalError; - return nullptr; - } - auto* directoryEntries = reinterpret_cast(dirEntryBuffer.get()); - - // Iterate over directory entries - for (uint32_t i = 0; i < numImages; i++) { - const uint8_t* entryBuffer = data->bytes() + kIcoDirectoryBytes + i * kIcoDirEntryBytes; - if (data->size() < kIcoDirectoryBytes + (i+1) * kIcoDirEntryBytes) { - SkCodecPrintf("Error: Dir entries truncated in ico.\n"); - *result = kIncompleteInput; - return nullptr; - } - - // The directory entry contains information such as width, height, - // bits per pixel, and number of colors in the color palette. We will - // ignore these fields since they are repeated in the header of the - // embedded image. In the event of an inconsistency, we would always - // defer to the value in the embedded header anyway. - - // Specifies the size of the embedded image, including the header - uint32_t size = get_int(entryBuffer, 8); - - // Specifies the offset of the embedded image from the start of file. - // It does not indicate the start of the pixel data, but rather the - // start of the embedded image header. - uint32_t offset = get_int(entryBuffer, 12); - - // Save the vital fields - directoryEntries[i].offset = offset; - directoryEntries[i].size = size; - } - - // Default Result, if no valid embedded codecs are found. - *result = kInvalidInput; - - // It is "customary" that the embedded images will be stored in order of - // increasing offset. However, the specification does not indicate that - // they must be stored in this order, so we will not trust that this is the - // case. Here we sort the embedded images by increasing offset. - struct EntryLessThan { - bool operator() (Entry a, Entry b) const { - return a.offset < b.offset; - } - }; - EntryLessThan lessThan; - SkTQSort(directoryEntries, directoryEntries + numImages, lessThan); - - // Now will construct a candidate codec for each of the embedded images - uint32_t bytesRead = kIcoDirectoryBytes + numImages * kIcoDirEntryBytes; - auto codecs = std::make_unique>>(numImages); - for (uint32_t i = 0; i < numImages; i++) { - uint32_t offset = directoryEntries[i].offset; - uint32_t size = directoryEntries[i].size; - - // Ensure that the offset is valid - if (offset < bytesRead) { - SkCodecPrintf("Warning: invalid ico offset.\n"); - continue; - } - - // If we cannot skip, assume we have reached the end of the stream and - // stop trying to make codecs - if (offset >= data->size()) { - SkCodecPrintf("Warning: could not skip to ico offset.\n"); - break; - } - bytesRead = offset; - - if (offset + size > data->size()) { - SkCodecPrintf("Warning: could not create embedded stream.\n"); - *result = kIncompleteInput; - break; - } - - sk_sp embeddedData(SkData::MakeSubset(data.get(), offset, size)); - auto embeddedStream = SkMemoryStream::Make(embeddedData); - bytesRead += size; - - // Check if the embedded codec is bmp or png and create the codec - std::unique_ptr codec; - Result ignoredResult; - if (SkPngDecoder::IsPng(embeddedData->bytes(), embeddedData->size())) { - codec = SkPngDecoder::Decode(std::move(embeddedStream), &ignoredResult); - } else { - codec = SkBmpCodec::MakeFromIco(std::move(embeddedStream), &ignoredResult); - } - - if (nullptr != codec) { - codecs->push_back(std::move(codec)); - } - } - - if (codecs->empty()) { - SkCodecPrintf("Error: could not find any valid embedded ico codecs.\n"); - return nullptr; - } - - // Use the largest codec as a "suggestion" for image info - size_t maxSize = 0; - int maxIndex = 0; - for (int i = 0; i < codecs->size(); i++) { - SkImageInfo info = codecs->at(i)->getInfo(); - size_t size = info.computeMinByteSize(); - - if (size > maxSize) { - maxSize = size; - maxIndex = i; - } - } - - auto maxInfo = codecs->at(maxIndex)->getEncodedInfo().copy(); - - *result = kSuccess; - return std::unique_ptr( - new SkIcoCodec(std::move(maxInfo), std::move(stream), std::move(codecs))); -} - -SkIcoCodec::SkIcoCodec(SkEncodedInfo&& info, - std::unique_ptr stream, - std::unique_ptr>> codecs) - // The source skcms_PixelFormat will not be used. The embedded - // codec's will be used instead. - : INHERITED(std::move(info), skcms_PixelFormat(), std::move(stream)) - , fEmbeddedCodecs(std::move(codecs)) - , fCurrCodec(nullptr) {} - -/* - * Chooses the best dimensions given the desired scale - */ -SkISize SkIcoCodec::onGetScaledDimensions(float desiredScale) const { - // We set the dimensions to the largest candidate image by default. - // Regardless of the scale request, this is the largest image that we - // will decode. - int origWidth = this->dimensions().width(); - int origHeight = this->dimensions().height(); - float desiredSize = desiredScale * origWidth * origHeight; - // At least one image will have smaller error than this initial value - float minError = ((float) (origWidth * origHeight)) - desiredSize + 1.0f; - int32_t minIndex = -1; - for (int32_t i = 0; i < fEmbeddedCodecs->size(); i++) { - auto dimensions = fEmbeddedCodecs->at(i)->dimensions(); - int width = dimensions.width(); - int height = dimensions.height(); - float error = SkTAbs(((float) (width * height)) - desiredSize); - if (error < minError) { - minError = error; - minIndex = i; - } - } - SkASSERT(minIndex >= 0); - - return fEmbeddedCodecs->at(minIndex)->dimensions(); -} - -int SkIcoCodec::chooseCodec(const SkISize& requestedSize, int startIndex) { - SkASSERT(startIndex >= 0); - - // FIXME: Cache the index from onGetScaledDimensions? - for (int i = startIndex; i < fEmbeddedCodecs->size(); i++) { - if (fEmbeddedCodecs->at(i)->dimensions() == requestedSize) { - return i; - } - } - - return -1; -} - -bool SkIcoCodec::onDimensionsSupported(const SkISize& dim) { - return this->chooseCodec(dim, 0) >= 0; -} - -/* - * Initiates the Ico decode - */ -SkCodec::Result SkIcoCodec::onGetPixels(const SkImageInfo& dstInfo, - void* dst, size_t dstRowBytes, - const Options& opts, - int* rowsDecoded) { - if (opts.fSubset) { - // Subsets are not supported. - return kUnimplemented; - } - - int index = 0; - SkCodec::Result result = kInvalidScale; - while (true) { - index = this->chooseCodec(dstInfo.dimensions(), index); - if (index < 0) { - break; - } - - SkCodec* embeddedCodec = fEmbeddedCodecs->at(index).get(); - result = embeddedCodec->getPixels(dstInfo, dst, dstRowBytes, &opts); - switch (result) { - case kSuccess: - case kIncompleteInput: - // The embedded codec will handle filling incomplete images, so we will indicate - // that all of the rows are initialized. - *rowsDecoded = dstInfo.height(); - return result; - default: - // Continue trying to find a valid embedded codec on a failed decode. - break; - } - - index++; - } - - SkCodecPrintf("Error: No matching candidate image in ico.\n"); - return result; -} - -SkCodec::Result SkIcoCodec::onStartScanlineDecode(const SkImageInfo& dstInfo, - const SkCodec::Options& options) { - int index = 0; - SkCodec::Result result = kInvalidScale; - while (true) { - index = this->chooseCodec(dstInfo.dimensions(), index); - if (index < 0) { - break; - } - - SkCodec* embeddedCodec = fEmbeddedCodecs->at(index).get(); - result = embeddedCodec->startScanlineDecode(dstInfo, &options); - if (kSuccess == result) { - fCurrCodec = embeddedCodec; - return result; - } - - index++; - } - - SkCodecPrintf("Error: No matching candidate image in ico.\n"); - return result; -} - -int SkIcoCodec::onGetScanlines(void* dst, int count, size_t rowBytes) { - SkASSERT(fCurrCodec); - return fCurrCodec->getScanlines(dst, count, rowBytes); -} - -bool SkIcoCodec::onSkipScanlines(int count) { - SkASSERT(fCurrCodec); - return fCurrCodec->skipScanlines(count); -} - -SkCodec::Result SkIcoCodec::onStartIncrementalDecode(const SkImageInfo& dstInfo, - void* pixels, size_t rowBytes, const SkCodec::Options& options) { - int index = 0; - while (true) { - index = this->chooseCodec(dstInfo.dimensions(), index); - if (index < 0) { - break; - } - - SkCodec* embeddedCodec = fEmbeddedCodecs->at(index).get(); - switch (embeddedCodec->startIncrementalDecode(dstInfo, - pixels, rowBytes, &options)) { - case kSuccess: - fCurrCodec = embeddedCodec; - return kSuccess; - case kUnimplemented: - // FIXME: embeddedCodec is a BMP. If scanline decoding would work, - // return kUnimplemented so that SkSampledCodec will fall through - // to use the scanline decoder. - // Note that calling startScanlineDecode will require an extra - // rewind. The embedded codec has an SkMemoryStream, which is - // cheap to rewind, though it will do extra work re-reading the - // header. - // Also note that we pass nullptr for Options. This is because - // Options that are valid for incremental decoding may not be - // valid for scanline decoding. - // Once BMP supports incremental decoding this workaround can go - // away. - if (embeddedCodec->startScanlineDecode(dstInfo) == kSuccess) { - return kUnimplemented; - } - // Move on to the next embedded codec. - break; - default: - break; - } - - index++; - } - - SkCodecPrintf("Error: No matching candidate image in ico.\n"); - return kInvalidScale; -} - -SkCodec::Result SkIcoCodec::onIncrementalDecode(int* rowsDecoded) { - SkASSERT(fCurrCodec); - return fCurrCodec->incrementalDecode(rowsDecoded); -} - -SkCodec::SkScanlineOrder SkIcoCodec::onGetScanlineOrder() const { - // FIXME: This function will possibly return the wrong value if it is called - // before startScanlineDecode()/startIncrementalDecode(). - if (fCurrCodec) { - return fCurrCodec->getScanlineOrder(); - } - - return INHERITED::onGetScanlineOrder(); -} - -SkSampler* SkIcoCodec::getSampler(bool createIfNecessary) { - if (fCurrCodec) { - return fCurrCodec->getSampler(createIfNecessary); - } - - return nullptr; -} - -namespace SkIcoDecoder { -bool IsIco(const void* data, size_t len) { - return SkIcoCodec::IsIco(data, len); -} - -std::unique_ptr Decode(std::unique_ptr stream, - SkCodec::Result* outResult, - SkCodecs::DecodeContext) { - SkCodec::Result resultStorage; - if (!outResult) { - outResult = &resultStorage; - } - return SkIcoCodec::MakeFromStream(std::move(stream), outResult); -} - -std::unique_ptr Decode(sk_sp data, - SkCodec::Result* outResult, - SkCodecs::DecodeContext) { - if (!data) { - if (outResult) { - *outResult = SkCodec::kInvalidInput; - } - return nullptr; - } - return Decode(SkMemoryStream::Make(std::move(data)), outResult, nullptr); -} -} // namespace SkIcoDecoder - diff --git a/gfx/skia/skia/src/codec/SkIcoCodec.h b/gfx/skia/skia/src/codec/SkIcoCodec.h deleted file mode 100644 index c966517df83f..000000000000 --- a/gfx/skia/skia/src/codec/SkIcoCodec.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#ifndef SkIcoCodec_DEFINED -#define SkIcoCodec_DEFINED - -#include "include/codec/SkCodec.h" -#include "include/codec/SkEncodedImageFormat.h" -#include "include/core/SkSize.h" -#include "include/core/SkTypes.h" -#include "include/private/base/SkTArray.h" - -#include -#include - -class SkSampler; -class SkStream; -struct SkEncodedInfo; -struct SkImageInfo; - -/* - * This class implements the decoding for bmp images - */ -class SkIcoCodec : public SkCodec { -public: - static bool IsIco(const void*, size_t); - - /* - * Assumes IsIco was called and returned true - * Creates an Ico decoder - * Reads enough of the stream to determine the image format - */ - static std::unique_ptr MakeFromStream(std::unique_ptr, Result*); - -protected: - - /* - * Chooses the best dimensions given the desired scale - */ - SkISize onGetScaledDimensions(float desiredScale) const override; - - bool onDimensionsSupported(const SkISize&) override; - - /* - * Initiates the Ico decode - */ - Result onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options&, - int*) override; - - SkEncodedImageFormat onGetEncodedFormat() const override { - return SkEncodedImageFormat::kICO; - } - - SkScanlineOrder onGetScanlineOrder() const override; - - bool conversionSupported(const SkImageInfo&, bool, bool) override { - // This will be checked by the embedded codec. - return true; - } - - // Handled by the embedded codec. - bool usesColorXform() const override { return false; } -private: - - Result onStartScanlineDecode(const SkImageInfo& dstInfo, - const SkCodec::Options& options) override; - - int onGetScanlines(void* dst, int count, size_t rowBytes) override; - - bool onSkipScanlines(int count) override; - - Result onStartIncrementalDecode(const SkImageInfo& dstInfo, void* pixels, size_t rowBytes, - const SkCodec::Options&) override; - - Result onIncrementalDecode(int* rowsDecoded) override; - - SkSampler* getSampler(bool createIfNecessary) override; - - /* - * Searches fEmbeddedCodecs for a codec that matches requestedSize. - * The search starts at startIndex and ends when an appropriate codec - * is found, or we have reached the end of the array. - * - * @return the index of the matching codec or -1 if there is no - * matching codec between startIndex and the end of - * the array. - */ - int chooseCodec(const SkISize& requestedSize, int startIndex); - - /* - * Constructor called by NewFromStream - * @param embeddedCodecs codecs for the embedded images, takes ownership - */ - SkIcoCodec(SkEncodedInfo&& info, - std::unique_ptr, - std::unique_ptr>> embeddedCodecs); - - std::unique_ptr>> fEmbeddedCodecs; - - // fCurrCodec is owned by this class, but should not be an - // std::unique_ptr. It will be deleted by the destructor of fEmbeddedCodecs. - SkCodec* fCurrCodec; - - using INHERITED = SkCodec; -}; -#endif // SkIcoCodec_DEFINED diff --git a/gfx/skia/skia/src/codec/SkJpegCodec.cpp b/gfx/skia/skia/src/codec/SkJpegCodec.cpp deleted file mode 100644 index d1fe79481262..000000000000 --- a/gfx/skia/skia/src/codec/SkJpegCodec.cpp +++ /dev/null @@ -1,971 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "src/codec/SkJpegCodec.h" - -#include "include/codec/SkCodec.h" -#include "include/codec/SkJpegDecoder.h" -#include "include/core/SkAlphaType.h" -#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/SkStream.h" -#include "include/core/SkTypes.h" -#include "include/core/SkYUVAInfo.h" -#include "include/private/base/SkAlign.h" -#include "include/private/base/SkMalloc.h" -#include "include/private/base/SkTemplates.h" -#include "modules/skcms/skcms.h" -#include "src/codec/SkCodecPriv.h" -#include "src/codec/SkJpegConstants.h" -#include "src/codec/SkJpegDecoderMgr.h" -#include "src/codec/SkJpegMetadataDecoderImpl.h" -#include "src/codec/SkJpegPriv.h" -#include "src/codec/SkParseEncodedOrigin.h" -#include "src/codec/SkSwizzler.h" - -#ifdef SK_CODEC_DECODES_JPEG_GAINMAPS -#include "include/private/SkGainmapInfo.h" -#endif // SK_CODEC_DECODES_JPEG_GAINMAPS - -#include -#include -#include -#include - -using namespace skia_private; - -class SkSampler; -struct SkGainmapInfo; - -// This warning triggers false postives way too often in here. -#if defined(__GNUC__) && !defined(__clang__) - #pragma GCC diagnostic ignored "-Wclobbered" -#endif - -extern "C" { - #include "jpeglib.h" // NO_G3_REWRITE -} - -bool SkJpegCodec::IsJpeg(const void* buffer, size_t bytesRead) { - return bytesRead >= sizeof(kJpegSig) && !memcmp(buffer, kJpegSig, sizeof(kJpegSig)); -} - -SkJpegMarkerList get_sk_marker_list(jpeg_decompress_struct* dinfo) { - SkJpegMarkerList markerList; - for (auto* marker = dinfo->marker_list; marker; marker = marker->next) { - markerList.emplace_back(marker->marker, - SkData::MakeWithoutCopy(marker->data, marker->data_length)); - } - return markerList; -} - -static SkEncodedOrigin get_exif_orientation(sk_sp exifData) { - SkEncodedOrigin origin = kDefault_SkEncodedOrigin; - if (exifData && SkParseEncodedOrigin(exifData->bytes(), exifData->size(), &origin)) { - return origin; - } - return kDefault_SkEncodedOrigin; -} - -SkCodec::Result SkJpegCodec::ReadHeader( - SkStream* stream, - SkCodec** codecOut, - JpegDecoderMgr** decoderMgrOut, - std::unique_ptr defaultColorProfile) { - // Create a JpegDecoderMgr to own all of the decompress information - std::unique_ptr decoderMgr(new JpegDecoderMgr(stream)); - - // libjpeg errors will be caught and reported here - skjpeg_error_mgr::AutoPushJmpBuf jmp(decoderMgr->errorMgr()); - if (setjmp(jmp)) { - return decoderMgr->returnFailure("ReadHeader", kInvalidInput); - } - - // Initialize the decompress info and the source manager - decoderMgr->init(); - auto* dinfo = decoderMgr->dinfo(); - - // Instruct jpeg library to save the markers that we care about. Since - // the orientation and color profile will not change, we can skip this - // step on rewinds. - if (codecOut) { - jpeg_save_markers(dinfo, kExifMarker, 0xFFFF); - jpeg_save_markers(dinfo, kICCMarker, 0xFFFF); - jpeg_save_markers(dinfo, kMpfMarker, 0xFFFF); - } - - // Read the jpeg header - switch (jpeg_read_header(dinfo, TRUE)) { - case JPEG_HEADER_OK: - break; - case JPEG_SUSPENDED: - return decoderMgr->returnFailure("ReadHeader", kIncompleteInput); - default: - return decoderMgr->returnFailure("ReadHeader", kInvalidInput); - } - - if (codecOut) { - // Get the encoded color type - SkEncodedInfo::Color color; - if (!decoderMgr->getEncodedColor(&color)) { - return kInvalidInput; - } - - auto metadataDecoder = - std::make_unique(get_sk_marker_list(dinfo)); - - SkEncodedOrigin orientation = - get_exif_orientation(metadataDecoder->getExifMetadata(/*copyData=*/false)); - - std::unique_ptr profile; - if (auto iccProfileData = metadataDecoder->getICCProfileData(/*copyData=*/true)) { - profile = SkEncodedInfo::ICCProfile::Make(std::move(iccProfileData)); - } - if (profile) { - auto type = profile->profile()->data_color_space; - switch (decoderMgr->dinfo()->jpeg_color_space) { - case JCS_CMYK: - case JCS_YCCK: - if (type != skcms_Signature_CMYK) { - profile = nullptr; - } - break; - case JCS_GRAYSCALE: - if (type != skcms_Signature_Gray && - type != skcms_Signature_RGB) - { - profile = nullptr; - } - break; - default: - if (type != skcms_Signature_RGB) { - profile = nullptr; - } - break; - } - } - if (!profile) { - profile = std::move(defaultColorProfile); - } - - SkEncodedInfo info = SkEncodedInfo::Make(dinfo->image_width, dinfo->image_height, - color, SkEncodedInfo::kOpaque_Alpha, 8, - std::move(profile)); - - SkJpegCodec* codec = new SkJpegCodec(std::move(info), - std::unique_ptr(stream), - decoderMgr.release(), - orientation); - *codecOut = codec; - } else { - SkASSERT(nullptr != decoderMgrOut); - *decoderMgrOut = decoderMgr.release(); - } - return kSuccess; -} - -std::unique_ptr SkJpegCodec::MakeFromStream(std::unique_ptr stream, - Result* result) { - return SkJpegCodec::MakeFromStream(std::move(stream), result, nullptr); -} - -std::unique_ptr SkJpegCodec::MakeFromStream(std::unique_ptr stream, - Result* result, std::unique_ptr defaultColorProfile) { - SkASSERT(result); - if (!stream) { - *result = SkCodec::kInvalidInput; - return nullptr; - } - SkCodec* codec = nullptr; - *result = ReadHeader(stream.get(), &codec, nullptr, std::move(defaultColorProfile)); - if (kSuccess == *result) { - // Codec has taken ownership of the stream, we do not need to delete it - SkASSERT(codec); - stream.release(); - return std::unique_ptr(codec); - } - return nullptr; -} - -SkJpegCodec::SkJpegCodec(SkEncodedInfo&& info, - std::unique_ptr stream, - JpegDecoderMgr* decoderMgr, - SkEncodedOrigin origin) - : INHERITED(std::move(info), skcms_PixelFormat_RGBA_8888, std::move(stream), origin) - , fDecoderMgr(decoderMgr) - , fReadyState(decoderMgr->dinfo()->global_state) {} -SkJpegCodec::~SkJpegCodec() = default; - -/* - * Return the row bytes of a particular image type and width - */ -static size_t get_row_bytes(const j_decompress_ptr dinfo) { - const size_t colorBytes = (dinfo->out_color_space == JCS_RGB565) ? 2 : - dinfo->out_color_components; - return dinfo->output_width * colorBytes; - -} - -/* - * Calculate output dimensions based on the provided factors. - * - * Not to be used on the actual jpeg_decompress_struct used for decoding, since it will - * incorrectly modify num_components. - */ -void calc_output_dimensions(jpeg_decompress_struct* dinfo, unsigned int num, unsigned int denom) { - dinfo->num_components = 0; - dinfo->scale_num = num; - dinfo->scale_denom = denom; - jpeg_calc_output_dimensions(dinfo); -} - -/* - * Return a valid set of output dimensions for this decoder, given an input scale - */ -SkISize SkJpegCodec::onGetScaledDimensions(float desiredScale) const { - // libjpeg-turbo supports scaling by 1/8, 1/4, 3/8, 1/2, 5/8, 3/4, 7/8, and 1/1, so we will - // support these as well - unsigned int num; - unsigned int denom = 8; - if (desiredScale >= 0.9375) { - num = 8; - } else if (desiredScale >= 0.8125) { - num = 7; - } else if (desiredScale >= 0.6875f) { - num = 6; - } else if (desiredScale >= 0.5625f) { - num = 5; - } else if (desiredScale >= 0.4375f) { - num = 4; - } else if (desiredScale >= 0.3125f) { - num = 3; - } else if (desiredScale >= 0.1875f) { - num = 2; - } else { - num = 1; - } - - // Set up a fake decompress struct in order to use libjpeg to calculate output dimensions - jpeg_decompress_struct dinfo; - sk_bzero(&dinfo, sizeof(dinfo)); - dinfo.image_width = this->dimensions().width(); - dinfo.image_height = this->dimensions().height(); - dinfo.global_state = fReadyState; - calc_output_dimensions(&dinfo, num, denom); - - // Return the calculated output dimensions for the given scale - return SkISize::Make(dinfo.output_width, dinfo.output_height); -} - -bool SkJpegCodec::onRewind() { - JpegDecoderMgr* decoderMgr = nullptr; - if (kSuccess != ReadHeader(this->stream(), nullptr, &decoderMgr, nullptr)) { - return fDecoderMgr->returnFalse("onRewind"); - } - SkASSERT(nullptr != decoderMgr); - fDecoderMgr.reset(decoderMgr); - - fSwizzler.reset(nullptr); - fSwizzleSrcRow = nullptr; - fColorXformSrcRow = nullptr; - fStorage.reset(); - - return true; -} - -bool SkJpegCodec::conversionSupported(const SkImageInfo& dstInfo, bool srcIsOpaque, - bool needsColorXform) { - SkASSERT(srcIsOpaque); - - if (kUnknown_SkAlphaType == dstInfo.alphaType()) { - return false; - } - - if (kOpaque_SkAlphaType != dstInfo.alphaType()) { - SkCodecPrintf("Warning: an opaque image should be decoded as opaque " - "- it is being decoded as non-opaque, which will draw slower\n"); - } - - J_COLOR_SPACE encodedColorType = fDecoderMgr->dinfo()->jpeg_color_space; - - // Check for valid color types and set the output color space - switch (dstInfo.colorType()) { - case kRGBA_8888_SkColorType: - fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA; - break; - case kBGRA_8888_SkColorType: - if (needsColorXform) { - // Always using RGBA as the input format for color xforms makes the - // implementation a little simpler. - fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA; - } else { - fDecoderMgr->dinfo()->out_color_space = JCS_EXT_BGRA; - } - break; - case kRGB_565_SkColorType: - if (needsColorXform) { - fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA; - } else { - fDecoderMgr->dinfo()->dither_mode = JDITHER_NONE; - fDecoderMgr->dinfo()->out_color_space = JCS_RGB565; - } - break; - case kGray_8_SkColorType: - if (JCS_GRAYSCALE != encodedColorType) { - return false; - } - - if (needsColorXform) { - fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA; - } else { - fDecoderMgr->dinfo()->out_color_space = JCS_GRAYSCALE; - } - break; - case kBGRA_10101010_XR_SkColorType: - case kBGR_101010x_XR_SkColorType: - case kRGBA_F16_SkColorType: - SkASSERT(needsColorXform); - fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA; - break; - default: - return false; - } - - // Check if we will decode to CMYK. libjpeg-turbo does not convert CMYK to RGBA, so - // we must do it ourselves. - if (JCS_CMYK == encodedColorType || JCS_YCCK == encodedColorType) { - fDecoderMgr->dinfo()->out_color_space = JCS_CMYK; - } - - return true; -} - -/* - * Checks if we can natively scale to the requested dimensions and natively scales the - * dimensions if possible - */ -bool SkJpegCodec::onDimensionsSupported(const SkISize& size) { - skjpeg_error_mgr::AutoPushJmpBuf jmp(fDecoderMgr->errorMgr()); - if (setjmp(jmp)) { - return fDecoderMgr->returnFalse("onDimensionsSupported"); - } - - const unsigned int dstWidth = size.width(); - const unsigned int dstHeight = size.height(); - - // Set up a fake decompress struct in order to use libjpeg to calculate output dimensions - // FIXME: Why is this necessary? - jpeg_decompress_struct dinfo; - sk_bzero(&dinfo, sizeof(dinfo)); - dinfo.image_width = this->dimensions().width(); - dinfo.image_height = this->dimensions().height(); - dinfo.global_state = fReadyState; - - // libjpeg-turbo can scale to 1/8, 1/4, 3/8, 1/2, 5/8, 3/4, 7/8, and 1/1 - unsigned int num = 8; - const unsigned int denom = 8; - calc_output_dimensions(&dinfo, num, denom); - while (dinfo.output_width != dstWidth || dinfo.output_height != dstHeight) { - - // Return a failure if we have tried all of the possible scales - if (1 == num || dstWidth > dinfo.output_width || dstHeight > dinfo.output_height) { - return false; - } - - // Try the next scale - num -= 1; - calc_output_dimensions(&dinfo, num, denom); - } - - fDecoderMgr->dinfo()->scale_num = num; - fDecoderMgr->dinfo()->scale_denom = denom; - return true; -} - -int SkJpegCodec::readRows(const SkImageInfo& dstInfo, void* dst, size_t rowBytes, int count, - const Options& opts) { - // Set the jump location for libjpeg-turbo errors - skjpeg_error_mgr::AutoPushJmpBuf jmp(fDecoderMgr->errorMgr()); - if (setjmp(jmp)) { - return 0; - } - - // When fSwizzleSrcRow is non-null, it means that we need to swizzle. In this case, - // we will always decode into fSwizzlerSrcRow before swizzling into the next buffer. - // We can never swizzle "in place" because the swizzler may perform sampling and/or - // subsetting. - // When fColorXformSrcRow is non-null, it means that we need to color xform and that - // we cannot color xform "in place" (many times we can, but not when the src and dst - // are different sizes). - // In this case, we will color xform from fColorXformSrcRow into the dst. - JSAMPLE* decodeDst = (JSAMPLE*) dst; - uint32_t* swizzleDst = (uint32_t*) dst; - size_t decodeDstRowBytes = rowBytes; - size_t swizzleDstRowBytes = rowBytes; - int dstWidth = opts.fSubset ? opts.fSubset->width() : dstInfo.width(); - if (fSwizzleSrcRow && fColorXformSrcRow) { - decodeDst = (JSAMPLE*) fSwizzleSrcRow; - swizzleDst = fColorXformSrcRow; - decodeDstRowBytes = 0; - swizzleDstRowBytes = 0; - dstWidth = fSwizzler->swizzleWidth(); - } else if (fColorXformSrcRow) { - decodeDst = (JSAMPLE*) fColorXformSrcRow; - swizzleDst = fColorXformSrcRow; - decodeDstRowBytes = 0; - swizzleDstRowBytes = 0; - } else if (fSwizzleSrcRow) { - decodeDst = (JSAMPLE*) fSwizzleSrcRow; - decodeDstRowBytes = 0; - dstWidth = fSwizzler->swizzleWidth(); - } - - for (int y = 0; y < count; y++) { - uint32_t lines = jpeg_read_scanlines(fDecoderMgr->dinfo(), &decodeDst, 1); - if (0 == lines) { - return y; - } - - if (fSwizzler) { - fSwizzler->swizzle(swizzleDst, decodeDst); - } - - if (this->colorXform()) { - this->applyColorXform(dst, swizzleDst, dstWidth); - dst = SkTAddOffset(dst, rowBytes); - } - - decodeDst = SkTAddOffset(decodeDst, decodeDstRowBytes); - swizzleDst = SkTAddOffset(swizzleDst, swizzleDstRowBytes); - } - - return count; -} - -/* - * This is a bit tricky. We only need the swizzler to do format conversion if the jpeg is - * encoded as CMYK. - * And even then we still may not need it. If the jpeg has a CMYK color profile and a color - * xform, the color xform will handle the CMYK->RGB conversion. - */ -static inline bool needs_swizzler_to_convert_from_cmyk(J_COLOR_SPACE jpegColorType, - const skcms_ICCProfile* srcProfile, - bool hasColorSpaceXform) { - if (JCS_CMYK != jpegColorType) { - return false; - } - - bool hasCMYKColorSpace = srcProfile && srcProfile->data_color_space == skcms_Signature_CMYK; - return !hasCMYKColorSpace || !hasColorSpaceXform; -} - -/* - * Performs the jpeg decode - */ -SkCodec::Result SkJpegCodec::onGetPixels(const SkImageInfo& dstInfo, - void* dst, size_t dstRowBytes, - const Options& options, - int* rowsDecoded) { - if (options.fSubset) { - // Subsets are not supported. - return kUnimplemented; - } - - // Get a pointer to the decompress info since we will use it quite frequently - jpeg_decompress_struct* dinfo = fDecoderMgr->dinfo(); - - // Set the jump location for libjpeg errors - skjpeg_error_mgr::AutoPushJmpBuf jmp(fDecoderMgr->errorMgr()); - if (setjmp(jmp)) { - return fDecoderMgr->returnFailure("setjmp", kInvalidInput); - } - - if (!jpeg_start_decompress(dinfo)) { - return fDecoderMgr->returnFailure("startDecompress", kInvalidInput); - } - - // The recommended output buffer height should always be 1 in high quality modes. - // If it's not, we want to know because it means our strategy is not optimal. - SkASSERT(1 == dinfo->rec_outbuf_height); - - if (needs_swizzler_to_convert_from_cmyk(dinfo->out_color_space, - this->getEncodedInfo().profile(), this->colorXform())) { - this->initializeSwizzler(dstInfo, options, true); - } - - if (!this->allocateStorage(dstInfo)) { - return kInternalError; - } - - int rows = this->readRows(dstInfo, dst, dstRowBytes, dstInfo.height(), options); - if (rows < dstInfo.height()) { - *rowsDecoded = rows; - return fDecoderMgr->returnFailure("Incomplete image data", kIncompleteInput); - } - - return kSuccess; -} - -bool SkJpegCodec::allocateStorage(const SkImageInfo& dstInfo) { - int dstWidth = dstInfo.width(); - - size_t swizzleBytes = 0; - if (fSwizzler) { - swizzleBytes = get_row_bytes(fDecoderMgr->dinfo()); - dstWidth = fSwizzler->swizzleWidth(); - SkASSERT(!this->colorXform() || SkIsAlign4(swizzleBytes)); - } - - size_t xformBytes = 0; - - if (this->colorXform() && sizeof(uint32_t) != dstInfo.bytesPerPixel()) { - xformBytes = dstWidth * sizeof(uint32_t); - } - - size_t totalBytes = swizzleBytes + xformBytes; - if (totalBytes > 0) { - if (!fStorage.reset(totalBytes)) { - return false; - } - fSwizzleSrcRow = (swizzleBytes > 0) ? fStorage.get() : nullptr; - fColorXformSrcRow = (xformBytes > 0) ? - SkTAddOffset(fStorage.get(), swizzleBytes) : nullptr; - } - return true; -} - -void SkJpegCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& options, - bool needsCMYKToRGB) { - Options swizzlerOptions = options; - if (options.fSubset) { - // Use fSwizzlerSubset if this is a subset decode. This is necessary in the case - // where libjpeg-turbo provides a subset and then we need to subset it further. - // Also, verify that fSwizzlerSubset is initialized and valid. - SkASSERT(!fSwizzlerSubset.isEmpty() && fSwizzlerSubset.x() <= options.fSubset->x() && - fSwizzlerSubset.width() == options.fSubset->width()); - swizzlerOptions.fSubset = &fSwizzlerSubset; - } - - SkImageInfo swizzlerDstInfo = dstInfo; - if (this->colorXform()) { - // The color xform will be expecting RGBA 8888 input. - swizzlerDstInfo = swizzlerDstInfo.makeColorType(kRGBA_8888_SkColorType); - } - - if (needsCMYKToRGB) { - // The swizzler is used to convert to from CMYK. - // The swizzler does not use the width or height on SkEncodedInfo. - auto swizzlerInfo = SkEncodedInfo::Make(0, 0, SkEncodedInfo::kInvertedCMYK_Color, - SkEncodedInfo::kOpaque_Alpha, 8); - fSwizzler = SkSwizzler::Make(swizzlerInfo, nullptr, swizzlerDstInfo, swizzlerOptions); - } else { - int srcBPP = 0; - switch (fDecoderMgr->dinfo()->out_color_space) { - case JCS_EXT_RGBA: - case JCS_EXT_BGRA: - case JCS_CMYK: - srcBPP = 4; - break; - case JCS_RGB565: - srcBPP = 2; - break; - case JCS_GRAYSCALE: - srcBPP = 1; - break; - default: - SkASSERT(false); - break; - } - fSwizzler = SkSwizzler::MakeSimple(srcBPP, swizzlerDstInfo, swizzlerOptions); - } - SkASSERT(fSwizzler); -} - -SkSampler* SkJpegCodec::getSampler(bool createIfNecessary) { - if (!createIfNecessary || fSwizzler) { - SkASSERT(!fSwizzler || (fSwizzleSrcRow && fStorage.get() == fSwizzleSrcRow)); - return fSwizzler.get(); - } - - bool needsCMYKToRGB = needs_swizzler_to_convert_from_cmyk( - fDecoderMgr->dinfo()->out_color_space, this->getEncodedInfo().profile(), - this->colorXform()); - this->initializeSwizzler(this->dstInfo(), this->options(), needsCMYKToRGB); - if (!this->allocateStorage(this->dstInfo())) { - return nullptr; - } - return fSwizzler.get(); -} - -SkCodec::Result SkJpegCodec::onStartScanlineDecode(const SkImageInfo& dstInfo, - const Options& options) { - // Set the jump location for libjpeg errors - skjpeg_error_mgr::AutoPushJmpBuf jmp(fDecoderMgr->errorMgr()); - if (setjmp(jmp)) { - SkCodecPrintf("setjmp: Error from libjpeg\n"); - return kInvalidInput; - } - - if (!jpeg_start_decompress(fDecoderMgr->dinfo())) { - SkCodecPrintf("start decompress failed\n"); - return kInvalidInput; - } - - bool needsCMYKToRGB = needs_swizzler_to_convert_from_cmyk( - fDecoderMgr->dinfo()->out_color_space, this->getEncodedInfo().profile(), - this->colorXform()); - if (options.fSubset) { - uint32_t startX = options.fSubset->x(); - uint32_t width = options.fSubset->width(); - - // libjpeg-turbo may need to align startX to a multiple of the IDCT - // block size. If this is the case, it will decrease the value of - // startX to the appropriate alignment and also increase the value - // of width so that the right edge of the requested subset remains - // the same. - jpeg_crop_scanline(fDecoderMgr->dinfo(), &startX, &width); - - SkASSERT(startX <= (uint32_t) options.fSubset->x()); - SkASSERT(width >= (uint32_t) options.fSubset->width()); - SkASSERT(startX + width >= (uint32_t) options.fSubset->right()); - - // Instruct the swizzler (if it is necessary) to further subset the - // output provided by libjpeg-turbo. - // - // We set this here (rather than in the if statement below), so that - // if (1) we don't need a swizzler for the subset, and (2) we need a - // swizzler for CMYK, the swizzler will still use the proper subset - // dimensions. - // - // Note that the swizzler will ignore the y and height parameters of - // the subset. Since the scanline decoder (and the swizzler) handle - // one row at a time, only the subsetting in the x-dimension matters. - fSwizzlerSubset.setXYWH(options.fSubset->x() - startX, 0, - options.fSubset->width(), options.fSubset->height()); - - // We will need a swizzler if libjpeg-turbo cannot provide the exact - // subset that we request. - if (startX != (uint32_t) options.fSubset->x() || - width != (uint32_t) options.fSubset->width()) { - this->initializeSwizzler(dstInfo, options, needsCMYKToRGB); - } - } - - // Make sure we have a swizzler if we are converting from CMYK. - if (!fSwizzler && needsCMYKToRGB) { - this->initializeSwizzler(dstInfo, options, true); - } - - if (!this->allocateStorage(dstInfo)) { - return kInternalError; - } - - return kSuccess; -} - -int SkJpegCodec::onGetScanlines(void* dst, int count, size_t dstRowBytes) { - int rows = this->readRows(this->dstInfo(), dst, dstRowBytes, count, this->options()); - if (rows < count) { - // This allows us to skip calling jpeg_finish_decompress(). - fDecoderMgr->dinfo()->output_scanline = this->dstInfo().height(); - } - - return rows; -} - -bool SkJpegCodec::onSkipScanlines(int count) { - // Set the jump location for libjpeg errors - skjpeg_error_mgr::AutoPushJmpBuf jmp(fDecoderMgr->errorMgr()); - if (setjmp(jmp)) { - return fDecoderMgr->returnFalse("onSkipScanlines"); - } - - return (uint32_t) count == jpeg_skip_scanlines(fDecoderMgr->dinfo(), count); -} - -static bool is_yuv_supported(const jpeg_decompress_struct* dinfo, - const SkJpegCodec& codec, - const SkYUVAPixmapInfo::SupportedDataTypes* supportedDataTypes, - SkYUVAPixmapInfo* yuvaPixmapInfo) { - // Scaling is not supported in raw data mode. - SkASSERT(dinfo->scale_num == dinfo->scale_denom); - - // I can't imagine that this would ever change, but we do depend on it. - static_assert(8 == DCTSIZE, "DCTSIZE (defined in jpeg library) should always be 8."); - - if (JCS_YCbCr != dinfo->jpeg_color_space) { - return false; - } - - SkASSERT(3 == dinfo->num_components); - SkASSERT(dinfo->comp_info); - - // It is possible to perform a YUV decode for any combination of - // horizontal and vertical sampling that is supported by - // libjpeg/libjpeg-turbo. However, we will start by supporting only the - // common cases (where U and V have samp_factors of one). - // - // The definition of samp_factor is kind of the opposite of what SkCodec - // thinks of as a sampling factor. samp_factor is essentially a - // multiplier, and the larger the samp_factor is, the more samples that - // there will be. Ex: - // U_plane_width = image_width * (U_h_samp_factor / max_h_samp_factor) - // - // Supporting cases where the samp_factors for U or V were larger than - // that of Y would be an extremely difficult change, given that clients - // allocate memory as if the size of the Y plane is always the size of the - // image. However, this case is very, very rare. - if ((1 != dinfo->comp_info[1].h_samp_factor) || - (1 != dinfo->comp_info[1].v_samp_factor) || - (1 != dinfo->comp_info[2].h_samp_factor) || - (1 != dinfo->comp_info[2].v_samp_factor)) - { - return false; - } - - // Support all common cases of Y samp_factors. - // TODO (msarett): As mentioned above, it would be possible to support - // more combinations of samp_factors. The issues are: - // (1) Are there actually any images that are not covered - // by these cases? - // (2) How much complexity would be added to the - // implementation in order to support these rare - // cases? - int hSampY = dinfo->comp_info[0].h_samp_factor; - int vSampY = dinfo->comp_info[0].v_samp_factor; - SkASSERT(hSampY == dinfo->max_h_samp_factor); - SkASSERT(vSampY == dinfo->max_v_samp_factor); - - SkYUVAInfo::Subsampling tempSubsampling; - if (1 == hSampY && 1 == vSampY) { - tempSubsampling = SkYUVAInfo::Subsampling::k444; - } else if (2 == hSampY && 1 == vSampY) { - tempSubsampling = SkYUVAInfo::Subsampling::k422; - } else if (2 == hSampY && 2 == vSampY) { - tempSubsampling = SkYUVAInfo::Subsampling::k420; - } else if (1 == hSampY && 2 == vSampY) { - tempSubsampling = SkYUVAInfo::Subsampling::k440; - } else if (4 == hSampY && 1 == vSampY) { - tempSubsampling = SkYUVAInfo::Subsampling::k411; - } else if (4 == hSampY && 2 == vSampY) { - tempSubsampling = SkYUVAInfo::Subsampling::k410; - } else { - return false; - } - if (supportedDataTypes && - !supportedDataTypes->supported(SkYUVAInfo::PlaneConfig::kY_U_V, - SkYUVAPixmapInfo::DataType::kUnorm8)) { - return false; - } - if (yuvaPixmapInfo) { - SkColorType colorTypes[SkYUVAPixmapInfo::kMaxPlanes]; - size_t rowBytes[SkYUVAPixmapInfo::kMaxPlanes]; - for (int i = 0; i < 3; ++i) { - colorTypes[i] = kAlpha_8_SkColorType; - rowBytes[i] = dinfo->comp_info[i].width_in_blocks * DCTSIZE; - } - SkYUVAInfo yuvaInfo(codec.dimensions(), - SkYUVAInfo::PlaneConfig::kY_U_V, - tempSubsampling, - kJPEG_Full_SkYUVColorSpace, - codec.getOrigin(), - SkYUVAInfo::Siting::kCentered, - SkYUVAInfo::Siting::kCentered); - *yuvaPixmapInfo = SkYUVAPixmapInfo(yuvaInfo, colorTypes, rowBytes); - } - return true; -} - -bool SkJpegCodec::onQueryYUVAInfo(const SkYUVAPixmapInfo::SupportedDataTypes& supportedDataTypes, - SkYUVAPixmapInfo* yuvaPixmapInfo) const { - jpeg_decompress_struct* dinfo = fDecoderMgr->dinfo(); - return is_yuv_supported(dinfo, *this, &supportedDataTypes, yuvaPixmapInfo); -} - -SkCodec::Result SkJpegCodec::onGetYUVAPlanes(const SkYUVAPixmaps& yuvaPixmaps) { - // Get a pointer to the decompress info since we will use it quite frequently - jpeg_decompress_struct* dinfo = fDecoderMgr->dinfo(); - if (!is_yuv_supported(dinfo, *this, nullptr, nullptr)) { - return fDecoderMgr->returnFailure("onGetYUVAPlanes", kInvalidInput); - } - // Set the jump location for libjpeg errors - skjpeg_error_mgr::AutoPushJmpBuf jmp(fDecoderMgr->errorMgr()); - if (setjmp(jmp)) { - return fDecoderMgr->returnFailure("setjmp", kInvalidInput); - } - - dinfo->raw_data_out = TRUE; - if (!jpeg_start_decompress(dinfo)) { - return fDecoderMgr->returnFailure("startDecompress", kInvalidInput); - } - - const std::array& planes = yuvaPixmaps.planes(); - -#ifdef SK_DEBUG - { - // A previous implementation claims that the return value of is_yuv_supported() - // may change after calling jpeg_start_decompress(). It looks to me like this - // was caused by a bug in the old code, but we'll be safe and check here. - // Also check that pixmap properties agree with expectations. - SkYUVAPixmapInfo info; - SkASSERT(is_yuv_supported(dinfo, *this, nullptr, &info)); - SkASSERT(info.yuvaInfo() == yuvaPixmaps.yuvaInfo()); - for (int i = 0; i < info.numPlanes(); ++i) { - SkASSERT(planes[i].colorType() == kAlpha_8_SkColorType); - SkASSERT(info.planeInfo(i) == planes[i].info()); - } - } -#endif - - // Build a JSAMPIMAGE to handle output from libjpeg-turbo. A JSAMPIMAGE has - // a 2-D array of pixels for each of the components (Y, U, V) in the image. - // Cheat Sheet: - // JSAMPIMAGE == JSAMPLEARRAY* == JSAMPROW** == JSAMPLE*** - JSAMPARRAY yuv[3]; - - // Set aside enough space for pointers to rows of Y, U, and V. - JSAMPROW rowptrs[2 * DCTSIZE + DCTSIZE + DCTSIZE]; - yuv[0] = &rowptrs[0]; // Y rows (DCTSIZE or 2 * DCTSIZE) - yuv[1] = &rowptrs[2 * DCTSIZE]; // U rows (DCTSIZE) - yuv[2] = &rowptrs[3 * DCTSIZE]; // V rows (DCTSIZE) - - // Initialize rowptrs. - int numYRowsPerBlock = DCTSIZE * dinfo->comp_info[0].v_samp_factor; - static_assert(sizeof(JSAMPLE) == 1); - for (int i = 0; i < numYRowsPerBlock; i++) { - rowptrs[i] = static_cast(planes[0].writable_addr()) + i* planes[0].rowBytes(); - } - for (int i = 0; i < DCTSIZE; i++) { - rowptrs[i + 2 * DCTSIZE] = - static_cast(planes[1].writable_addr()) + i* planes[1].rowBytes(); - rowptrs[i + 3 * DCTSIZE] = - static_cast(planes[2].writable_addr()) + i* planes[2].rowBytes(); - } - - // After each loop iteration, we will increment pointers to Y, U, and V. - size_t blockIncrementY = numYRowsPerBlock * planes[0].rowBytes(); - size_t blockIncrementU = DCTSIZE * planes[1].rowBytes(); - size_t blockIncrementV = DCTSIZE * planes[2].rowBytes(); - - uint32_t numRowsPerBlock = numYRowsPerBlock; - - // We intentionally round down here, as this first loop will only handle - // full block rows. As a special case at the end, we will handle any - // remaining rows that do not make up a full block. - const int numIters = dinfo->output_height / numRowsPerBlock; - for (int i = 0; i < numIters; i++) { - JDIMENSION linesRead = jpeg_read_raw_data(dinfo, yuv, numRowsPerBlock); - if (linesRead < numRowsPerBlock) { - // FIXME: Handle incomplete YUV decodes without signalling an error. - return kInvalidInput; - } - - // Update rowptrs. - for (int j = 0; j < numYRowsPerBlock; j++) { - rowptrs[j] += blockIncrementY; - } - for (int j = 0; j < DCTSIZE; j++) { - rowptrs[j + 2 * DCTSIZE] += blockIncrementU; - rowptrs[j + 3 * DCTSIZE] += blockIncrementV; - } - } - - uint32_t remainingRows = dinfo->output_height - dinfo->output_scanline; - SkASSERT(remainingRows == dinfo->output_height % numRowsPerBlock); - SkASSERT(dinfo->output_scanline == numIters * numRowsPerBlock); - if (remainingRows > 0) { - // libjpeg-turbo needs memory to be padded by the block sizes. We will fulfill - // this requirement using an extra row buffer. - // FIXME: Should SkCodec have an extra memory buffer that can be shared among - // all of the implementations that use temporary/garbage memory? - AutoTMalloc extraRow(planes[0].rowBytes()); - for (int i = remainingRows; i < numYRowsPerBlock; i++) { - rowptrs[i] = extraRow.get(); - } - int remainingUVRows = dinfo->comp_info[1].downsampled_height - DCTSIZE * numIters; - for (int i = remainingUVRows; i < DCTSIZE; i++) { - rowptrs[i + 2 * DCTSIZE] = extraRow.get(); - rowptrs[i + 3 * DCTSIZE] = extraRow.get(); - } - - JDIMENSION linesRead = jpeg_read_raw_data(dinfo, yuv, numRowsPerBlock); - if (linesRead < remainingRows) { - // FIXME: Handle incomplete YUV decodes without signalling an error. - return kInvalidInput; - } - } - - return kSuccess; -} - -bool SkJpegCodec::onGetGainmapCodec(SkGainmapInfo* info, std::unique_ptr* gainmapCodec) { - std::unique_ptr stream; - if (!this->onGetGainmapInfo(info, &stream)) { - return false; - } - if (gainmapCodec) { - Result result; - *gainmapCodec = MakeFromStream(std::move(stream), &result); - if (!*gainmapCodec) { - return false; - } - } - return true; -} - -bool SkJpegCodec::onGetGainmapInfo(SkGainmapInfo* info, - std::unique_ptr* gainmapImageStream) { -#ifdef SK_CODEC_DECODES_JPEG_GAINMAPS - sk_sp gainmap_data; - SkGainmapInfo gainmap_info; - - auto metadataDecoder = - std::make_unique(get_sk_marker_list(fDecoderMgr->dinfo())); - if (!metadataDecoder->findGainmapImage( - fDecoderMgr->getSourceMgr(), gainmap_data, gainmap_info)) { - return false; - } - - *info = gainmap_info; - *gainmapImageStream = SkMemoryStream::Make(gainmap_data); - return true; -#else - return false; -#endif // SK_CODEC_DECODES_JPEG_GAINMAPS -} - -namespace SkJpegDecoder { -bool IsJpeg(const void* data, size_t len) { - return SkJpegCodec::IsJpeg(data, len); -} - -std::unique_ptr Decode(std::unique_ptr stream, - SkCodec::Result* outResult, - SkCodecs::DecodeContext) { - SkCodec::Result resultStorage; - if (!outResult) { - outResult = &resultStorage; - } - return SkJpegCodec::MakeFromStream(std::move(stream), outResult); -} - -std::unique_ptr Decode(sk_sp data, - SkCodec::Result* outResult, - SkCodecs::DecodeContext) { - if (!data) { - if (outResult) { - *outResult = SkCodec::kInvalidInput; - } - return nullptr; - } - return Decode(SkMemoryStream::Make(std::move(data)), outResult, nullptr); -} - -} // namespace SkJpegDecoder diff --git a/gfx/skia/skia/src/codec/SkJpegCodec.h b/gfx/skia/skia/src/codec/SkJpegCodec.h deleted file mode 100644 index 73047ee66d7d..000000000000 --- a/gfx/skia/skia/src/codec/SkJpegCodec.h +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkJpegCodec_DEFINED -#define SkJpegCodec_DEFINED - -#include "include/codec/SkCodec.h" -#include "include/codec/SkEncodedImageFormat.h" -#include "include/codec/SkEncodedOrigin.h" -#include "include/core/SkRect.h" -#include "include/core/SkSize.h" -#include "include/core/SkTypes.h" -#include "include/core/SkYUVAPixmaps.h" -#include "include/private/SkEncodedInfo.h" -#include "include/private/base/SkTemplates.h" - -#include -#include -#include - -class JpegDecoderMgr; -class SkSampler; -class SkStream; -class SkSwizzler; -struct SkGainmapInfo; -struct SkImageInfo; - -/* - * - * This class implements the decoding for jpeg images - * - */ -class SkJpegCodec : public SkCodec { -public: - ~SkJpegCodec() override; - - static bool IsJpeg(const void*, size_t); - - /* - * Assumes IsJpeg was called and returned true - * Takes ownership of the stream - */ - static std::unique_ptr MakeFromStream(std::unique_ptr, Result*); - -protected: - - /* - * Recommend a set of destination dimensions given a requested scale - */ - SkISize onGetScaledDimensions(float desiredScale) const override; - - /* - * Initiates the jpeg decode - */ - Result onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options&, - int*) override; - - bool onQueryYUVAInfo(const SkYUVAPixmapInfo::SupportedDataTypes&, - SkYUVAPixmapInfo*) const override; - - Result onGetYUVAPlanes(const SkYUVAPixmaps& yuvaPixmaps) override; - - SkEncodedImageFormat onGetEncodedFormat() const override { - return SkEncodedImageFormat::kJPEG; - } - - bool onRewind() override; - - bool onDimensionsSupported(const SkISize&) override; - - bool conversionSupported(const SkImageInfo&, bool, bool) override; - - bool onGetGainmapCodec(SkGainmapInfo* info, std::unique_ptr* gainmapCodec) override; - bool onGetGainmapInfo(SkGainmapInfo* info, - std::unique_ptr* gainmapImageStream) override; - -private: - /* - * Allows SkRawCodec to communicate the color profile from the exif data. - */ - static std::unique_ptr MakeFromStream(std::unique_ptr, Result*, - std::unique_ptr defaultColorProfile); - - /* - * Read enough of the stream to initialize the SkJpegCodec. - * Returns a bool representing success or failure. - * - * @param codecOut - * If this returns true, and codecOut was not nullptr, - * codecOut will be set to a new SkJpegCodec. - * - * @param decoderMgrOut - * If this returns true, and codecOut was nullptr, - * decoderMgrOut must be non-nullptr and decoderMgrOut will be set to a new - * JpegDecoderMgr pointer. - * - * @param stream - * Deleted on failure. - * codecOut will take ownership of it in the case where we created a codec. - * Ownership is unchanged when we set decoderMgrOut. - * - * @param defaultColorProfile - * If the jpeg does not have an embedded color profile, the image data should - * be tagged with this color profile. - */ - static Result ReadHeader(SkStream* stream, SkCodec** codecOut, - JpegDecoderMgr** decoderMgrOut, - std::unique_ptr defaultColorProfile); - - /* - * Creates an instance of the decoder - * Called only by NewFromStream - * - * @param info contains properties of the encoded data - * @param stream the encoded image data - * @param decoderMgr holds decompress struct, src manager, and error manager - * takes ownership - * @param origin indicates the image orientation as specified in Exif metadata. - * @param xmpMetadata holds the XMP metadata included in the image, if any. - */ - SkJpegCodec(SkEncodedInfo&& info, - std::unique_ptr stream, - JpegDecoderMgr* decoderMgr, - SkEncodedOrigin origin); - - void initializeSwizzler(const SkImageInfo& dstInfo, const Options& options, - bool needsCMYKToRGB); - [[nodiscard]] bool allocateStorage(const SkImageInfo& dstInfo); - int readRows(const SkImageInfo& dstInfo, void* dst, size_t rowBytes, int count, const Options&); - - /* - * Scanline decoding. - */ - SkSampler* getSampler(bool createIfNecessary) override; - Result onStartScanlineDecode(const SkImageInfo& dstInfo, - const Options& options) override; - int onGetScanlines(void* dst, int count, size_t rowBytes) override; - bool onSkipScanlines(int count) override; - - std::unique_ptr fDecoderMgr; - - // We will save the state of the decompress struct after reading the header. - // This allows us to safely call onGetScaledDimensions() at any time. - const int fReadyState; - - - skia_private::AutoTMalloc fStorage; - uint8_t* fSwizzleSrcRow = nullptr; - uint32_t* fColorXformSrcRow = nullptr; - - // libjpeg-turbo provides some subsetting. In the case that libjpeg-turbo - // cannot take the exact the subset that we need, we will use the swizzler - // to further subset the output from libjpeg-turbo. - SkIRect fSwizzlerSubset = SkIRect::MakeEmpty(); - - std::unique_ptr fSwizzler; - - friend class SkRawCodec; - - using INHERITED = SkCodec; -}; - -#endif diff --git a/gfx/skia/skia/src/codec/SkJpegConstants.h b/gfx/skia/skia/src/codec/SkJpegConstants.h deleted file mode 100644 index f2be02f16578..000000000000 --- a/gfx/skia/skia/src/codec/SkJpegConstants.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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 SkJpegConstants_codec_DEFINED -#define SkJpegConstants_codec_DEFINED - -#include -#include - -// The first marker of all JPEG files is StartOfImage. -static constexpr uint8_t kJpegMarkerStartOfImage = 0xD8; - -// The last marker of all JPEG files (excluding auxiliary data, e.g, MPF images) is EndOfImage. -static constexpr uint8_t kJpegMarkerEndOfImage = 0xD9; - -// The header of a JPEG file is the data in all segments before the first StartOfScan. -static constexpr uint8_t kJpegMarkerStartOfScan = 0xDA; - -// Metadata and auxiliary images are stored in the APP1 through APP15 markers. -static constexpr uint8_t kJpegMarkerAPP0 = 0xE0; - -// The number of bytes in a marker code is two. The first byte is all marker codes is 0xFF. -static constexpr size_t kJpegMarkerCodeSize = 2; - -// The number of bytes used to specify the length of a segment's parameters is two. This length -// value includes these two bytes. -static constexpr size_t kJpegSegmentParameterLengthSize = 2; - -// The first three bytes of all JPEG files is a StartOfImage marker (two bytes) followed by the -// first byte of the next marker. -static constexpr uint8_t kJpegSig[] = {0xFF, kJpegMarkerStartOfImage, 0xFF}; - -// ICC profile segment marker and signature. -static constexpr uint32_t kICCMarker = kJpegMarkerAPP0 + 2; -static constexpr uint32_t kICCMarkerHeaderSize = 14; -static constexpr uint32_t kICCMarkerIndexSize = 1; -static constexpr uint8_t kICCSig[] = { - 'I', 'C', 'C', '_', 'P', 'R', 'O', 'F', 'I', 'L', 'E', '\0', -}; - -// XMP segment marker and signature. -static constexpr uint32_t kXMPMarker = kJpegMarkerAPP0 + 1; -static constexpr uint8_t kXMPStandardSig[] = { - 'h', 't', 't', 'p', ':', '/', '/', 'n', 's', '.', 'a', 'd', 'o', 'b', 'e', '.', 'c', 'o', - 'm', '/', 'x', 'a', 'p', '/', '1', '.', '0', '/', '\0'}; -static constexpr uint8_t kXMPExtendedSig[] = { - 'h', 't', 't', 'p', ':', '/', '/', 'n', 's', '.', 'a', 'd', 'o', 'b', 'e', '.', 'c', 'o', - 'm', '/', 'x', 'm', 'p', '/', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', '/', '\0'}; - -// EXIF segment marker and signature. -static constexpr uint32_t kExifMarker = kJpegMarkerAPP0 + 1; -constexpr uint8_t kExifSig[] = {'E', 'x', 'i', 'f', '\0'}; - -// MPF segment marker and signature. -static constexpr uint32_t kMpfMarker = kJpegMarkerAPP0 + 2; -static constexpr uint8_t kMpfSig[] = {'M', 'P', 'F', '\0'}; - -// ISO 21496-1 marker and signature. -static constexpr uint32_t kISOGainmapMarker = kJpegMarkerAPP0 + 2; -static constexpr uint8_t kISOGainmapSig[] = {'u', 'r', 'n', ':', 'i', 's', 'o', ':', 's', 't', - 'd', ':', 'i', 's', 'o', ':', 't', 's', ':', '2', - '1', '4', '9', '6', ':', '-', '1', '\0'}; - -#endif diff --git a/gfx/skia/skia/src/codec/SkJpegDecoderMgr.cpp b/gfx/skia/skia/src/codec/SkJpegDecoderMgr.cpp deleted file mode 100644 index c905ee188ce1..000000000000 --- a/gfx/skia/skia/src/codec/SkJpegDecoderMgr.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#include "src/codec/SkJpegDecoderMgr.h" - -#include "include/core/SkTypes.h" -#include "src/codec/SkCodecPriv.h" -#include "src/codec/SkJpegSourceMgr.h" -#include "src/codec/SkJpegUtility.h" - -#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK - #include "include/android/SkAndroidFrameworkUtils.h" -#endif - -#include -#include - -class SkStream; - -/* - * Print information, warning, and error messages - */ -static void print_message(const j_common_ptr info, const char caller[]) { - char buffer[JMSG_LENGTH_MAX]; - info->err->format_message(info, buffer); - SkCodecPrintf("libjpeg error %d <%s> from %s\n", info->err->msg_code, buffer, caller); -} - -/* - * Reporting function for error and warning messages. - */ -static void output_message(j_common_ptr info) { - print_message(info, "output_message"); -} - -static void progress_monitor(j_common_ptr info) { - int scan = ((j_decompress_ptr)info)->input_scan_number; - // Progressive images with a very large number of scans can cause the - // decoder to hang. Here we use the progress monitor to abort on - // a very large number of scans. 100 is arbitrary, but much larger - // than the number of scans we might expect in a normal image. - if (scan >= 100) { - skjpeg_err_exit(info); - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// JpegDecoderMgr - -bool JpegDecoderMgr::returnFalse(const char caller[]) { - print_message((j_common_ptr) &fDInfo, caller); - return false; -} - -SkCodec::Result JpegDecoderMgr::returnFailure(const char caller[], SkCodec::Result result) { - print_message((j_common_ptr) &fDInfo, caller); - return result; -} - -bool JpegDecoderMgr::getEncodedColor(SkEncodedInfo::Color* outColor) { - switch (fDInfo.jpeg_color_space) { - case JCS_GRAYSCALE: - *outColor = SkEncodedInfo::kGray_Color; - return true; - case JCS_YCbCr: - *outColor = SkEncodedInfo::kYUV_Color; - return true; - case JCS_RGB: -#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK - SkAndroidFrameworkUtils::SafetyNetLog("118372692"); -#endif - *outColor = SkEncodedInfo::kRGB_Color; - return true; - case JCS_YCCK: - *outColor = SkEncodedInfo::kYCCK_Color; - return true; - case JCS_CMYK: - *outColor = SkEncodedInfo::kInvertedCMYK_Color; - return true; - default: - return false; - } -} - -SkJpegSourceMgr* JpegDecoderMgr::getSourceMgr() { - return fSrcMgr.fSourceMgr.get(); -} - -JpegDecoderMgr::JpegDecoderMgr(SkStream* stream) - : fSrcMgr(SkJpegSourceMgr::Make(stream)), fInit(false) { - // An error manager must be set before any calls to libjpeg, in order to handle failures. - fDInfo.err = jpeg_std_error(&fErrorMgr); - fErrorMgr.error_exit = skjpeg_err_exit; -} - -void JpegDecoderMgr::init() { - jpeg_create_decompress(&fDInfo); - fInit = true; - fDInfo.src = &fSrcMgr; - fDInfo.err->output_message = &output_message; - fDInfo.progress = &fProgressMgr; - fProgressMgr.progress_monitor = &progress_monitor; -} - -JpegDecoderMgr::~JpegDecoderMgr() { - if (fInit) { - jpeg_destroy_decompress(&fDInfo); - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// JpegDecoderMgr::SourceMgr - -// static -void JpegDecoderMgr::SourceMgr::InitSource(j_decompress_ptr dinfo) { - JpegDecoderMgr::SourceMgr* src = (JpegDecoderMgr::SourceMgr*)dinfo->src; - src->fSourceMgr->initSource(src->next_input_byte, src->bytes_in_buffer); -} - -// static -void JpegDecoderMgr::SourceMgr::SkipInputData(j_decompress_ptr dinfo, long num_bytes_long) { - JpegDecoderMgr::SourceMgr* src = (JpegDecoderMgr::SourceMgr*)dinfo->src; - size_t num_bytes = static_cast(num_bytes_long); - if (!src->fSourceMgr->skipInputBytes(num_bytes, src->next_input_byte, src->bytes_in_buffer)) { - SkCodecPrintf("Failure to skip.\n"); - src->next_input_byte = nullptr; - src->bytes_in_buffer = 0; - dinfo->err->error_exit((j_common_ptr)dinfo); - } -} - -// static -boolean JpegDecoderMgr::SourceMgr::FillInputBuffer(j_decompress_ptr dinfo) { - JpegDecoderMgr::SourceMgr* src = (JpegDecoderMgr::SourceMgr*)dinfo->src; - if (!src->fSourceMgr->fillInputBuffer(src->next_input_byte, src->bytes_in_buffer)) { - SkCodecPrintf("Failure to fill input buffer.\n"); - src->next_input_byte = nullptr; - src->bytes_in_buffer = 0; - return false; - } - return true; -} - -// static -void JpegDecoderMgr::SourceMgr::TermSource(j_decompress_ptr dinfo) {} - -JpegDecoderMgr::SourceMgr::SourceMgr(std::unique_ptr sourceMgr) - : fSourceMgr(std::move(sourceMgr)) { - init_source = JpegDecoderMgr::SourceMgr::InitSource; - fill_input_buffer = JpegDecoderMgr::SourceMgr::FillInputBuffer; - skip_input_data = JpegDecoderMgr::SourceMgr::SkipInputData; - resync_to_restart = jpeg_resync_to_restart; - term_source = JpegDecoderMgr::SourceMgr::TermSource; -} diff --git a/gfx/skia/skia/src/codec/SkJpegDecoderMgr.h b/gfx/skia/skia/src/codec/SkJpegDecoderMgr.h deleted file mode 100644 index 52d297532de4..000000000000 --- a/gfx/skia/skia/src/codec/SkJpegDecoderMgr.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkJpegDecoderMgr_DEFINED -#define SkJpegDecoderMgr_DEFINED - -#include "include/codec/SkCodec.h" -#include "include/private/SkEncodedInfo.h" -#include "include/private/base/SkNoncopyable.h" -#include "src/codec/SkJpegPriv.h" -#include "src/codec/SkJpegSourceMgr.h" - -extern "C" { - #include "jpeglib.h" // NO_G3_REWRITE -} - -#include - -class SkStream; - -class JpegDecoderMgr : SkNoncopyable { -public: - - /* - * Print a useful error message and return false - */ - bool returnFalse(const char caller[]); - - /* - * Print a useful error message and return a decode failure - */ - SkCodec::Result returnFailure(const char caller[], SkCodec::Result result); - - /* - * Create the decode manager - * Does not take ownership of stream - */ - JpegDecoderMgr(SkStream* stream); - - /* - * Initialize decompress struct - * Initialize the source manager - */ - void init(); - - /* - * Returns true if it successfully sets outColor to the encoded color, - * and false otherwise. - */ - bool getEncodedColor(SkEncodedInfo::Color* outColor); - - /* - * Free memory used by the decode manager - */ - ~JpegDecoderMgr(); - - /* - * Get the skjpeg_error_mgr in order to set an error return jmp_buf - */ - skjpeg_error_mgr* errorMgr() { return &fErrorMgr; } - - /* - * Get function for the decompress info struct - */ - jpeg_decompress_struct* dinfo() { return &fDInfo; } - - // Get the source manager. - SkJpegSourceMgr* getSourceMgr(); - -private: - // Wrapper that calls into the full SkJpegSourceMgr interface. - struct SourceMgr : jpeg_source_mgr { - static void InitSource(j_decompress_ptr dinfo); - static void SkipInputData(j_decompress_ptr dinfo, long num_bytes_long); - static boolean FillInputBuffer(j_decompress_ptr dinfo); - static void TermSource(j_decompress_ptr dinfo); - - SourceMgr(std::unique_ptr mgr); - std::unique_ptr fSourceMgr; - }; - - jpeg_decompress_struct fDInfo; - SourceMgr fSrcMgr; - skjpeg_error_mgr fErrorMgr; - jpeg_progress_mgr fProgressMgr; - bool fInit; -}; - -#endif diff --git a/gfx/skia/skia/src/codec/SkJpegMetadataDecoderImpl.cpp b/gfx/skia/skia/src/codec/SkJpegMetadataDecoderImpl.cpp deleted file mode 100644 index e8dcfb30d60d..000000000000 --- a/gfx/skia/skia/src/codec/SkJpegMetadataDecoderImpl.cpp +++ /dev/null @@ -1,495 +0,0 @@ -/* - * Copyright 2024 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "src/codec/SkJpegMetadataDecoderImpl.h" - -#include "include/core/SkData.h" -#include "include/private/base/SkTemplates.h" -#include "src/codec/SkCodecPriv.h" -#include "src/codec/SkJpegConstants.h" - -#include -#include -#include -#include - -#ifdef SK_CODEC_DECODES_JPEG_GAINMAPS -#include "include/core/SkStream.h" -#include "include/private/SkExif.h" -#include "include/private/SkGainmapInfo.h" -#include "include/private/SkXmp.h" -#include "src/base/SkEndian.h" -#include "src/codec/SkJpegMultiPicture.h" -#include "src/codec/SkJpegSegmentScan.h" -#include "src/codec/SkJpegSourceMgr.h" -#include "src/codec/SkJpegXmp.h" -#else -struct SkGainmapInfo; -#endif // SK_CODEC_DECODES_JPEG_GAINMAPS - -#ifdef SK_CODEC_DECODES_JPEG_GAINMAPS -std::unique_ptr SkJpegMetadataDecoderImpl::getXmpMetadata() const { - std::vector> decoderApp1Params; - for (const auto& marker : fMarkerList) { - if (marker.fMarker == kXMPMarker) { - decoderApp1Params.push_back(marker.fData); - } - } - return SkJpegMakeXmp(decoderApp1Params); -} - -// Extract the SkJpegMultiPictureParameters from this image (if they exist). If |sourceMgr| and -// |outMpParamsSegment| are non-nullptr, then also return the SkJpegSegment that the parameters came -// from (and return nullptr if one cannot be found). -static std::unique_ptr find_mp_params( - const SkJpegMarkerList& markerList, - SkJpegSourceMgr* sourceMgr, - SkJpegSegment* outMpParamsSegment) { - std::unique_ptr mpParams; - size_t skippedSegmentCount = 0; - - // Search though the libjpeg segments until we find a segment that parses as MP parameters. Keep - // track of how many segments with the MPF marker we skipped over to get there. - for (const auto& marker : markerList) { - if (marker.fMarker != kMpfMarker) { - continue; - } - mpParams = SkJpegMultiPictureParameters::Make(marker.fData); - if (mpParams) { - break; - } - ++skippedSegmentCount; - } - if (!mpParams) { - return nullptr; - } - - // If |sourceMgr| is not specified, then do not try to find the SkJpegSegment. - if (!sourceMgr) { - SkASSERT(!outMpParamsSegment); - return mpParams; - } - - // Now, find the SkJpegSegmentScanner segment that corresponds to the libjpeg marker. - // TODO(ccameron): It may be preferable to make SkJpegSourceMgr save segments with certain - // markers to avoid this strangeness. - for (const auto& segment : sourceMgr->getAllSegments()) { - if (segment.marker != kMpfMarker) { - continue; - } - if (skippedSegmentCount == 0) { - *outMpParamsSegment = segment; - return mpParams; - } - skippedSegmentCount--; - } - return nullptr; -} - -// Attempt to extract a gainmap image from a specified offset and size within the decoder's stream. -// Returns true only if the extracted gainmap image includes XMP metadata that specifies HDR gainmap -// rendering parameters. -static bool extract_gainmap(SkJpegSourceMgr* decoderSource, - size_t offset, - size_t size, - bool baseImageHasIsoVersion, - bool baseImageHasAdobeXmp, - std::optional baseImageAppleHdrHeadroom, - SkGainmapInfo& outInfo, - sk_sp& outData) { - // Extract the SkData for this image. - bool imageDataWasCopied = false; - auto imageData = decoderSource->getSubsetData(offset, size, &imageDataWasCopied); - if (!imageData) { - SkCodecPrintf("Failed to extract MP image.\n"); - return false; - } - - // Parse the potential gainmap image's metadata. - SkJpegMetadataDecoderImpl metadataDecoder(imageData); - - // If this image identifies itself as a gainmap, then populate |info|. - bool didPopulateInfo = false; - SkGainmapInfo info; - - // Check for ISO 21496-1 gain map metadata. - if (baseImageHasIsoVersion) { - didPopulateInfo = SkGainmapInfo::Parse( - metadataDecoder.getISOGainmapMetadata(/*copyData=*/false).get(), info); - if (didPopulateInfo && info.fGainmapMathColorSpace) { - auto iccData = metadataDecoder.getICCProfileData(/*copyData=*/false); - skcms_ICCProfile iccProfile; - if (iccData && skcms_Parse(iccData->data(), iccData->size(), &iccProfile)) { - auto iccProfileSpace = SkColorSpace::Make(iccProfile); - if (iccProfileSpace) { - info.fGainmapMathColorSpace = std::move(iccProfileSpace); - } - } - } - } - - if (!didPopulateInfo) { - // The Adobe and Apple gain map metadata require XMP. Parse it now. - auto xmp = metadataDecoder.getXmpMetadata(); - if (!xmp) { - return false; - } - - // Check for Adobe gain map metadata only if the base image specified hdrgm:Version="1.0". - if (!didPopulateInfo && baseImageHasAdobeXmp) { - didPopulateInfo = xmp->getGainmapInfoAdobe(&info); - } - - // Next try for Apple gain map metadata. This does not require anything specific from the - // base image. - if (!didPopulateInfo && baseImageAppleHdrHeadroom.has_value()) { - didPopulateInfo = xmp->getGainmapInfoApple(baseImageAppleHdrHeadroom.value(), &info); - } - } - - // If none of the formats identified itself as a gainmap and populated |info| then fail. - if (!didPopulateInfo) { - return false; - } - - // This image is a gainmap. - outInfo = info; - if (imageDataWasCopied) { - outData = imageData; - } else { - outData = SkData::MakeWithCopy(imageData->data(), imageData->size()); - } - return true; -} -#endif - -bool SkJpegMetadataDecoderImpl::findGainmapImage(SkJpegSourceMgr* sourceMgr, - sk_sp& outData, - SkGainmapInfo& outInfo) const { -#ifdef SK_CODEC_DECODES_JPEG_GAINMAPS - SkExif::Metadata baseExif; - SkExif::Parse(baseExif, getExifMetadata(/*copyData=*/false).get()); - auto xmp = getXmpMetadata(); - - // Determine if a support ISO 21496-1 gain map version is present in the base image. - bool isoGainmapPresent = - SkGainmapInfo::ParseVersion(getISOGainmapMetadata(/*copyData=*/false).get()); - - // Determine if Adobe HDR gain map is indicated in the base image. - bool adobeGainmapPresent = xmp && xmp->getGainmapInfoAdobe(nullptr); - - // Attempt to locate the gainmap from the container XMP. - size_t containerGainmapOffset = 0; - size_t containerGainmapSize = 0; - if (xmp && xmp->getContainerGainmapLocation(&containerGainmapOffset, &containerGainmapSize)) { - const auto& segments = sourceMgr->getAllSegments(); - if (!segments.empty()) { - const auto& lastSegment = segments.back(); - if (lastSegment.marker == kJpegMarkerEndOfImage) { - containerGainmapOffset += lastSegment.offset + kJpegMarkerCodeSize; - } - } - } - - // Attempt to find MultiPicture parameters. - SkJpegSegment mpParamsSegment; - auto mpParams = find_mp_params(fMarkerList, sourceMgr, &mpParamsSegment); - - // First, search through the Multi-Picture images. - if (mpParams) { - for (size_t mpImageIndex = 1; mpImageIndex < mpParams->images.size(); ++mpImageIndex) { - size_t mpImageOffset = SkJpegMultiPictureParameters::GetImageAbsoluteOffset( - mpParams->images[mpImageIndex].dataOffset, mpParamsSegment.offset); - size_t mpImageSize = mpParams->images[mpImageIndex].size; - - if (extract_gainmap(sourceMgr, - mpImageOffset, - mpImageSize, - isoGainmapPresent, - adobeGainmapPresent, - baseExif.fHdrHeadroom, - outInfo, - outData)) { - // If the GContainer also suggested an offset and size, assert that we found the - // image that the GContainer suggested. - if (containerGainmapOffset) { - SkASSERT(containerGainmapOffset == mpImageOffset); - SkASSERT(containerGainmapSize == mpImageSize); - } - return true; - } - } - } - - // Next, try the location suggested by the container XMP. - if (containerGainmapOffset) { - if (extract_gainmap(sourceMgr, - containerGainmapOffset, - containerGainmapSize, - /*baseImageHasIsoVersion=*/false, - adobeGainmapPresent, - /*baseImageAppleHdrHeadroom=*/std::nullopt, - outInfo, - outData)) { - return true; - } - SkCodecPrintf("Failed to extract container-specified gainmap.\n"); - } -#endif - return false; -} - -/** - * Return true if the specified SkJpegMarker has marker |targetMarker| and begins with the specified - * signature. - */ -static bool marker_has_signature(const SkJpegMarker& marker, - const uint32_t targetMarker, - const uint8_t* signature, - size_t signatureSize) { - if (targetMarker != marker.fMarker) { - return false; - } - if (marker.fData->size() <= signatureSize) { - return false; - } - if (memcmp(marker.fData->bytes(), signature, signatureSize) != 0) { - return false; - } - return true; -} - -/* - * Return metadata with a specific marker and signature. - * - * Search for segments that start with the specified targetMarker, followed by the specified - * signature, followed by (optional) padding. - * - * Some types of metadata (e.g, ICC profiles) are too big to fit into a single segment's data (which - * is limited to 64k), and come in multiple parts. For this type of data, bytesInIndex is >0. After - * the signature comes bytesInIndex bytes (big endian) for the index of the segment's part, followed - * by bytesInIndex bytes (big endian) for the total number of parts. If all parts are present, - * stitch them together and return the combined result. Return failure if parts are absent, there - * are duplicate parts, or parts disagree on the total number of parts. - * - * Visually, each segment is: - * [|signatureSize| bytes containing |signature|] - * [|signaturePadding| bytes that are unexamined] - * [|bytesInIndex] bytes listing the segment index for multi-segment metadata] - * [|bytesInIndex] bytes listing the segment count for multi-segment metadata] - * [the returned data] - * - * If alwaysCopyData is true, then return a copy of the data. If alwaysCopyData is false, then - * return a direct reference to the data pointed to by dinfo, if possible. - */ -static sk_sp read_metadata(const SkJpegMarkerList& markerList, - const uint32_t targetMarker, - const uint8_t* signature, - size_t signatureSize, - size_t signaturePadding, - size_t bytesInIndex, - bool alwaysCopyData) { - // Compute the total size of the entire header (signature plus padding plus index plus count), - // since we'll use it often. - const size_t headerSize = signatureSize + signaturePadding + 2 * bytesInIndex; - - // A map from part index to the data in each part. - std::vector> parts; - - // Running total of number of data in all parts. - size_t partsTotalSize = 0; - - // Running total number of parts found. - uint32_t foundPartCount = 0; - - // The expected number of parts (initialized at the first part we encounter). - uint32_t expectedPartCount = 0; - - // Iterate through the image's segments. - for (const auto& marker : markerList) { - // Skip segments that don't have the right marker or signature. - if (!marker_has_signature(marker, targetMarker, signature, signatureSize)) { - continue; - } - - // Skip segments that are too small to include the index and count. - const size_t dataLength = marker.fData->size(); - if (dataLength <= headerSize) { - continue; - } - - // Read this part's index and count as big-endian (if they are present, otherwise hard-code - // them to 1). - const uint8_t* data = marker.fData->bytes(); - uint32_t partIndex = 0; - uint32_t partCount = 0; - if (bytesInIndex == 0) { - partIndex = 1; - partCount = 1; - } else { - for (size_t i = 0; i < bytesInIndex; ++i) { - const size_t offset = signatureSize + signaturePadding; - partIndex = (partIndex << 8) + data[offset + i]; - partCount = (partCount << 8) + data[offset + bytesInIndex + i]; - } - } - - // A part count of 0 is invalid. - if (!partCount) { - SkCodecPrintf("Invalid marker part count zero\n"); - return nullptr; - } - - // The indices must in the range 1, ..., count. - if (partIndex <= 0 || partIndex > partCount) { - SkCodecPrintf("Invalid marker index %u for count %u\n", partIndex, partCount); - return nullptr; - } - - // If this is the first marker we've encountered set the expected part count to its count. - if (expectedPartCount == 0) { - expectedPartCount = partCount; - parts.resize(expectedPartCount); - } - - // If this does not match the expected part count, then fail. - if (partCount != expectedPartCount) { - SkCodecPrintf("Conflicting marker counts %u vs %u\n", partCount, expectedPartCount); - return nullptr; - } - - // Make an SkData directly referencing the decoder's data for this part. - auto partData = SkData::MakeWithoutCopy(data + headerSize, dataLength - headerSize); - - // Fail if duplicates are found. - if (parts[partIndex - 1]) { - SkCodecPrintf("Duplicate parts for index %u of %u\n", partIndex, expectedPartCount); - return nullptr; - } - - // Save part in the map. - partsTotalSize += partData->size(); - parts[partIndex - 1] = std::move(partData); - foundPartCount += 1; - - // Stop as soon as we find all of the parts. - if (foundPartCount == expectedPartCount) { - break; - } - } - - // Return nullptr if we don't find the data (this is not an error). - if (expectedPartCount == 0) { - return nullptr; - } - - // Fail if we don't have all of the parts. - if (foundPartCount != expectedPartCount) { - SkCodecPrintf("Incomplete set of markers (expected %u got %u)\n", - expectedPartCount, - foundPartCount); - return nullptr; - } - - // Return a direct reference to the data if there is only one part and we're allowed to. - if (!alwaysCopyData && expectedPartCount == 1) { - return std::move(parts[0]); - } - - // Copy all of the markers and stitch them together. - auto result = SkData::MakeUninitialized(partsTotalSize); - void* copyDest = result->writable_data(); - for (const auto& part : parts) { - memcpy(copyDest, part->data(), part->size()); - copyDest = SkTAddOffset(copyDest, part->size()); - } - return result; -} - -SkJpegMetadataDecoderImpl::SkJpegMetadataDecoderImpl(SkJpegMarkerList markerList) - : fMarkerList(std::move(markerList)) {} - -SkJpegMetadataDecoderImpl::SkJpegMetadataDecoderImpl(sk_sp data) { -#ifdef SK_CODEC_DECODES_JPEG_GAINMAPS - SkJpegSegmentScanner scan(kJpegMarkerStartOfScan); - scan.onBytes(data->data(), data->size()); - if (scan.hadError() || !scan.isDone()) { - SkCodecPrintf("Failed to scan header of MP image.\n"); - return; - } - for (const auto& segment : scan.getSegments()) { - // Save the APP1 and APP2 parameters (which includes Exif, XMP, ICC, and MPF). - if (segment.marker != kJpegMarkerAPP0 + 1 && segment.marker != kJpegMarkerAPP0 + 2) { - continue; - } - auto parameters = SkJpegSegmentScanner::GetParameters(data.get(), segment); - if (!parameters) { - continue; - } - fMarkerList.emplace_back(segment.marker, std::move(parameters)); - } -#endif -} - -sk_sp SkJpegMetadataDecoderImpl::getExifMetadata(bool copyData) const { - return read_metadata(fMarkerList, - kExifMarker, - kExifSig, - sizeof(kExifSig), - /*signaturePadding=*/1, - /*bytesInIndex=*/0, - copyData); -} - -sk_sp SkJpegMetadataDecoderImpl::getICCProfileData(bool copyData) const { - return read_metadata(fMarkerList, - kICCMarker, - kICCSig, - sizeof(kICCSig), - /*signaturePadding=*/0, - kICCMarkerIndexSize, - copyData); -} - -sk_sp SkJpegMetadataDecoderImpl::getISOGainmapMetadata(bool copyData) const { - return read_metadata(fMarkerList, - kISOGainmapMarker, - kISOGainmapSig, - sizeof(kISOGainmapSig), - /*signaturePadding=*/0, - /*bytesInIndex=*/0, - copyData); -} - -bool SkJpegMetadataDecoderImpl::mightHaveGainmapImage() const { -#ifdef SK_CODEC_DECODES_JPEG_GAINMAPS - // All supported gainmap formats require MPF. Reject images that do not have MPF. - return find_mp_params(fMarkerList, nullptr, nullptr) != nullptr; -#else - return false; -#endif -} - -bool SkJpegMetadataDecoderImpl::findGainmapImage(sk_sp baseImageData, - sk_sp& outGainmapImageData, - SkGainmapInfo& outGainmapInfo) { -#ifdef SK_CODEC_DECODES_JPEG_GAINMAPS - auto baseImageStream = SkMemoryStream::Make(baseImageData); - auto sourceMgr = SkJpegSourceMgr::Make(baseImageStream.get()); - return findGainmapImage(sourceMgr.get(), outGainmapImageData, outGainmapInfo); -#else - return false; -#endif -} - -std::unique_ptr SkJpegMetadataDecoder::Make(std::vector segments) { - return std::make_unique(std::move(segments)); -} - -std::unique_ptr SkJpegMetadataDecoder::Make(sk_sp data) { - return std::make_unique(std::move(data)); -} diff --git a/gfx/skia/skia/src/codec/SkJpegMetadataDecoderImpl.h b/gfx/skia/skia/src/codec/SkJpegMetadataDecoderImpl.h deleted file mode 100644 index 284b02a43771..000000000000 --- a/gfx/skia/skia/src/codec/SkJpegMetadataDecoderImpl.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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 SkJpegMetadataDecoderImpl_DEFINED -#define SkJpegMetadataDecoderImpl_DEFINED - -#include "include/core/SkRefCnt.h" -#include "include/private/SkJpegMetadataDecoder.h" - -#include - -#ifdef SK_CODEC_DECODES_JPEG_GAINMAPS -#include "include/private/SkXmp.h" -#endif // SK_CODEC_DECODES_JPEG_GAINMAPS - -class SkData; -class SkJpegSourceMgr; -struct SkGainmapInfo; - -using SkJpegMarker = SkJpegMetadataDecoder::Segment; -using SkJpegMarkerList = std::vector; - -class SkJpegMetadataDecoderImpl : public SkJpegMetadataDecoder { -public: - SkJpegMetadataDecoderImpl(SkJpegMarkerList markerList); - SkJpegMetadataDecoderImpl(sk_sp data); - - bool findGainmapImage(SkJpegSourceMgr* sourceMgr, - sk_sp& outGainmapImageData, - SkGainmapInfo& outGainmapInfo) const; - -#ifdef SK_CODEC_DECODES_JPEG_GAINMAPS - std::unique_ptr getXmpMetadata() const; -#endif // SK_CODEC_DECODES_JPEG_GAINMAPS - - // SkJpegMetadataDecoder implementation: - sk_sp getExifMetadata(bool copyData) const override; - sk_sp getICCProfileData(bool copyData) const override; - sk_sp getISOGainmapMetadata(bool copyData) const override; - bool mightHaveGainmapImage() const override; - bool findGainmapImage(sk_sp baseImageData, - sk_sp& outGainmapImageData, - SkGainmapInfo& outGainmapInfo) override; - -private: - SkJpegMarkerList fMarkerList; -}; - -#endif // SkJpegMetadataDecoderImpl_DEFINED diff --git a/gfx/skia/skia/src/codec/SkJpegMultiPicture.cpp b/gfx/skia/skia/src/codec/SkJpegMultiPicture.cpp deleted file mode 100644 index 666a69ca55b9..000000000000 --- a/gfx/skia/skia/src/codec/SkJpegMultiPicture.cpp +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Copyright 2023 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "src/codec/SkJpegMultiPicture.h" - -#include "include/core/SkData.h" -#include "include/core/SkStream.h" -#include "src/base/SkEndian.h" -#include "src/codec/SkCodecPriv.h" -#include "src/codec/SkJpegConstants.h" -#include "src/codec/SkJpegSegmentScan.h" -#include "src/codec/SkTiffUtility.h" -#include "src/core/SkStreamPriv.h" - -#include - -constexpr uint16_t kVersionTag = 0xB000; -constexpr uint32_t kVersionCount = 4; -constexpr size_t kVersionSize = 4; -constexpr uint8_t kVersionExpected[kVersionSize] = {'0', '1', '0', '0'}; - -constexpr uint16_t kNumberOfImagesTag = 0xB001; -constexpr uint32_t kNumberOfImagesCount = 1; - -constexpr uint16_t kMPEntryTag = 0xB002; -constexpr uint32_t kMPEntrySize = 16; - -constexpr uint32_t kMPEntryAttributeFormatMask = 0x7000000; -constexpr uint32_t kMPEntryAttributeFormatJpeg = 0x0000000; - -constexpr uint32_t kMPEntryAttributeTypeMask = 0xFFFFFF; -constexpr uint32_t kMPEntryAttributeTypePrimary = 0x030000; - -constexpr uint16_t kIndividualImageUniqueIDTag = 0xB003; -constexpr uint32_t kIndividualImageUniqueIDSize = 33; - -constexpr uint16_t kTotalNumberCapturedFramesTag = 0xB004; - -std::unique_ptr SkJpegMultiPictureParameters::Make( - const sk_sp& segmentParameters) { - // Read the MP Format identifier starting after the APP2 Field Length. See Figure 4 of CIPA - // DC-x007-2009. - if (segmentParameters->size() < sizeof(kMpfSig)) { - return nullptr; - } - if (memcmp(segmentParameters->data(), kMpfSig, sizeof(kMpfSig)) != 0) { - return nullptr; - } - auto ifdData = SkData::MakeSubset( - segmentParameters.get(), sizeof(kMpfSig), segmentParameters->size() - sizeof(kMpfSig)); - SkASSERT(ifdData); - - // The rest of this function reads the structure described in Figure 6 of CIPA DC-x007-2009. - // Determine the endianness of the values in the structure. See Figure 5 (MP endian tag - // structure), and read the Index IFD offset. - bool littleEndian = false; - uint32_t ifdOffset = 0; - if (!SkTiff::ImageFileDirectory::ParseHeader(ifdData.get(), &littleEndian, &ifdOffset)) { - SkCodecPrintf("Failed to parse endian-ness and index IFD offset.\n"); - return nullptr; - } - - // Create the Index Image File Directory (Index IFD). - auto ifd = SkTiff::ImageFileDirectory::MakeFromOffset(ifdData, littleEndian, ifdOffset); - if (!ifd) { - SkCodecPrintf("Failed to create MP Index IFD offset.\n"); - return nullptr; - } - - // Read the number of tags in the Index IFD. See Table 3 (MP Index IFD Tags) for a description - // of all possible tags. - uint16_t tagCount = ifd->getNumEntries(); - - // We will extract the number of images from the tags. - uint32_t numberOfImages = 0; - - // The data for the MP entries. - sk_sp mpEntriesData; - - // The MP Index IFD tags shall be specified in the order of their tag IDs (text from - // section 5.2.3), so keep track of the previous tag id read. - uint16_t previousTag = 0; - for (uint16_t idfEntryIndex = 0; idfEntryIndex < tagCount; ++idfEntryIndex) { - uint16_t tag = ifd->getEntryTag(idfEntryIndex); - if (previousTag >= tag) { - SkCodecPrintf("MPF tags not in order.\n"); - return nullptr; - } - previousTag = tag; - - switch (tag) { - case kVersionTag: { - // See 5.2.3.1: MP Format Version. - sk_sp data = ifd->getEntryUndefinedData(idfEntryIndex); - if (!data) { - SkCodecPrintf("Version must be undefined type.\n"); - return nullptr; - } - if (data->size() != kVersionSize) { - SkCodecPrintf("Version must be 4 bytes.\n"); - return nullptr; - } - if (memcmp(data->data(), kVersionExpected, kVersionSize) != 0) { - SkCodecPrintf("Version value is not 0100.\n"); - return nullptr; - } - break; - } - case kNumberOfImagesTag: - // See 5.2.3.2: Number of Images. - if (!ifd->getEntryUnsignedLong(idfEntryIndex, 1, &numberOfImages)) { - SkCodecPrintf("Number of Images was not 1 unsigned long.\n"); - } - if (numberOfImages < 1) { - SkCodecPrintf("Invalid number of images.\n"); - return nullptr; - } - break; - case kMPEntryTag: { - // See 5.2.3.3: MP Entry. - mpEntriesData = ifd->getEntryUndefinedData(idfEntryIndex); - if (!mpEntriesData) { - SkCodecPrintf("MP entries data could not be extracted.\n"); - return nullptr; - } - if (mpEntriesData->size() != kMPEntrySize * numberOfImages) { - SkCodecPrintf("MP entries data should be %ux%u bytes, was %u.\n", - kMPEntrySize, - numberOfImages, - static_cast(mpEntriesData->size())); - return nullptr; - } - break; - } - case kIndividualImageUniqueIDTag: { - // See 5.2.3.4: Individual Image Unique ID List. - // Validate that the count parameter is correct, but do not extract any other - // information. - sk_sp data = ifd->getEntryUndefinedData(idfEntryIndex); - if (!data) { - SkCodecPrintf("Image Unique ID must be undefined type.\n"); - return nullptr; - } - if (data->size() != kIndividualImageUniqueIDSize * numberOfImages) { - SkCodecPrintf("Invalid Image Unique ID count.\n"); - return nullptr; - } - break; - } - case kTotalNumberCapturedFramesTag: { - // See 5.2.3.5: Total Number of Captured Frames. - uint32_t totalNumCapturedFrames = 0; - if (!ifd->getEntryUnsignedLong(idfEntryIndex, 1, &totalNumCapturedFrames)) { - SkCodecPrintf("Total Number of Captures Frames was not 1 unsigned long.\n"); - } - break; - } - default: - return nullptr; - } - } - if (!numberOfImages) { - SkCodecPrintf("Number of images must be greater than zero.\n"); - return nullptr; - } - if (!mpEntriesData) { - SkCodecPrintf("MP Entry data was not present.\n"); - return nullptr; - } - - // Start to prepare the result that we will return. - auto result = std::make_unique(numberOfImages); - - // The next IFD is the Attribute IFD offset. We will not read or validate the Attribute IFD. - - // Parse the MP Entries data. - for (uint32_t i = 0; i < numberOfImages; ++i) { - const uint8_t* mpEntryData = mpEntriesData->bytes() + kMPEntrySize * i; - const uint32_t attribute = get_endian_int(mpEntryData + 0, littleEndian); - const uint32_t size = get_endian_int(mpEntryData + 4, littleEndian); - const uint32_t dataOffset = get_endian_int(mpEntryData + 8, littleEndian); - - const bool isPrimary = - (attribute & kMPEntryAttributeTypeMask) == kMPEntryAttributeTypePrimary; - const bool isJpeg = - (attribute & kMPEntryAttributeFormatMask) == kMPEntryAttributeFormatJpeg; - - if (isPrimary != (i == 0)) { - SkCodecPrintf("Image must be primary iff it is the first image..\n"); - return nullptr; - } - if (!isJpeg) { - SkCodecPrintf("Image format must be 0 (JPEG).\n"); - return nullptr; - } - - if (i == 0 && dataOffset != 0) { - SkCodecPrintf("First individual Image offset must be NULL.\n"); - return nullptr; - } - - result->images[i].dataOffset = dataOffset; - result->images[i].size = size; - } - - return result; -} - -sk_sp SkJpegMultiPictureParameters::serialize(uint32_t individualImageNumber) const { - SkDynamicMemoryWStream s; - - const uint32_t numberOfImages = static_cast(images.size()); - - // Write the MPF signature. - s.write(kMpfSig, sizeof(kMpfSig)); - - // We will always write as big-endian. - s.write(SkTiff::kEndianBig, sizeof(SkTiff::kEndianBig)); - - // Set the first IFD offset be the position after the endianness value and this offset. This - // will be the MP Index IFD for the first individual image and the MP Attribute IFD for all - // other images. - constexpr uint32_t firstIfdOffset = sizeof(SkTiff::kEndianBig) + // Endian-ness - sizeof(uint32_t); // Index IFD offset - SkWStreamWriteU32BE(&s, firstIfdOffset); - SkASSERT(s.bytesWritten() - sizeof(kMpfSig) == firstIfdOffset); - - if (individualImageNumber == 0) { - // The MP Index IFD will write 3 tags (version, number of images, and MP entries). See - // in Table 6 (MP Index IFD Tag Support Level) that these are the only mandatory entries. - const uint32_t mpIndexIfdNumberOfTags = 3; - SkWStreamWriteU16BE(&s, mpIndexIfdNumberOfTags); - } else { - // The MP Attribute IFD will write 1 tags (version). See in Table 7 (MP Attribute IFD Tag - // Support Level for Baseline MP Files) that no tags are required. If gainmap images support - // is added to CIPA DC-007, then some tags may be added and become mandatory. - const uint16_t mpAttributeIfdNumberOfTags = 1; - SkWStreamWriteU16BE(&s, mpAttributeIfdNumberOfTags); - } - - // Write the version. - SkWStreamWriteU16BE(&s, kVersionTag); - SkWStreamWriteU16BE(&s, SkTiff::kTypeUndefined); - SkWStreamWriteU32BE(&s, kVersionCount); - s.write(kVersionExpected, kVersionSize); - - if (individualImageNumber == 0) { - // Write the number of images. - SkWStreamWriteU16BE(&s, kNumberOfImagesTag); - SkWStreamWriteU16BE(&s, SkTiff::kTypeUnsignedLong); - SkWStreamWriteU32BE(&s, kNumberOfImagesCount); - SkWStreamWriteU32BE(&s, numberOfImages); - - // Write the MP entries tag. - SkWStreamWriteU16BE(&s, kMPEntryTag); - SkWStreamWriteU16BE(&s, SkTiff::kTypeUndefined); - const uint32_t mpEntriesSize = kMPEntrySize * numberOfImages; - SkWStreamWriteU32BE(&s, mpEntriesSize); - const uint32_t mpEntryOffset = static_cast( - s.bytesWritten() - // The bytes written so far - sizeof(kMpfSig) + // Excluding the MPF signature - sizeof(uint32_t) + // The 4 bytes for this offset - sizeof(uint32_t)); // The 4 bytes for the attribute IFD offset. - SkWStreamWriteU32BE(&s, mpEntryOffset); - - // Write the attribute IFD offset (zero because there is none). - SkWStreamWriteU32BE(&s, 0); - - // Write the MP entries data. - SkASSERT(s.bytesWritten() - sizeof(kMpfSig) == mpEntryOffset); - for (size_t i = 0; i < images.size(); ++i) { - const auto& image = images[i]; - - uint32_t attribute = kMPEntryAttributeFormatJpeg; - if (i == 0) { - attribute |= kMPEntryAttributeTypePrimary; - } - - SkWStreamWriteU32BE(&s, attribute); - SkWStreamWriteU32BE(&s, image.size); - SkWStreamWriteU32BE(&s, image.dataOffset); - // Dependent image 1 and 2 entries are zero. - SkWStreamWriteU16BE(&s, 0); - SkWStreamWriteU16BE(&s, 0); - } - } else { - // The non-first-individual-images do not have any further IFDs. - SkWStreamWriteU32BE(&s, 0); - } - - return s.detachAsData(); -} - -static size_t mp_header_absolute_offset(size_t mpSegmentOffset) { - return mpSegmentOffset + // The offset to the segment's marker - kJpegMarkerCodeSize + // The marker itself - kJpegSegmentParameterLengthSize + // The segment parameter length - sizeof(kMpfSig); // The {'M','P','F',0} signature -} - -size_t SkJpegMultiPictureParameters::GetImageAbsoluteOffset(uint32_t dataOffset, - size_t mpSegmentOffset) { - // The value of zero is used by the primary image. - if (dataOffset == 0) { - return 0; - } - return mp_header_absolute_offset(mpSegmentOffset) + dataOffset; -} - -uint32_t SkJpegMultiPictureParameters::GetImageDataOffset(size_t imageAbsoluteOffset, - size_t mpSegmentOffset) { - // The value of zero is used by the primary image. - if (imageAbsoluteOffset == 0) { - return 0; - } - return static_cast(imageAbsoluteOffset - mp_header_absolute_offset(mpSegmentOffset)); -} diff --git a/gfx/skia/skia/src/codec/SkJpegMultiPicture.h b/gfx/skia/skia/src/codec/SkJpegMultiPicture.h deleted file mode 100644 index 9d527e0613d3..000000000000 --- a/gfx/skia/skia/src/codec/SkJpegMultiPicture.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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 SkJpegMultiPicture_codec_DEFINED -#define SkJpegMultiPicture_codec_DEFINED - -#include "include/core/SkRefCnt.h" - -#include -#include -#include - -class SkData; - -/* - * Parsed Jpeg Multi-Picture Format structure as specified in CIPA DC-x007-2009. An introduction to - * the format can be found in Figure 1 (Basic MP File format data structure) and Figure 6 (Internal - * Structure of the MP Index IFD) in that document. This parsing will extract only the size and - * offset parameters from the images in the Index Image File Directory. - */ -struct SkJpegMultiPictureParameters { - explicit SkJpegMultiPictureParameters(size_t numberOfImages) : images(numberOfImages) {} - - // An individual image. - struct Image { - // The size of the image in bytes. - uint32_t size = 0; - // The offset of the image in bytes. This offset is specified relative to the address of - // the MP Endian field in the MP Header, unless the image is a First Individual Image, in - // which case the value of the offest [sic] shall be NULL (from section 5.2.3.3). - uint32_t dataOffset = 0; - }; - - // The images listed in the Index Image File Directory. - std::vector images; - - /* - * Parse Jpeg Multi-Picture Format parameters. The specified data should be APP2 segment - * parameters, which, if they are MPF parameter, should start with the {'M', 'P', 'F', 0} - * signature. Returns nullptr the parameters do not start with the MPF signature, or if there - * is an error in parsing the parameters. - */ - static std::unique_ptr Make( - const sk_sp& segmentParameters); - - /* - * Serialize Jpeg Multi-Picture Format segment parameters for the indicated individual image. - * This segment will start with the {'M', 'P', 'F', 0} signature (it will not include the - * segment marker or parameter length). - */ - sk_sp serialize(uint32_t individualImageNumber) const; - - /* - * Compute the absolute offset (from the start of the image) for the offset in the multi-picture - * parameters, given the absolute offset of the MPF segment (the offset of the {0xFF, 0xE2} - * marker from the start of the image. - */ - static size_t GetImageAbsoluteOffset(uint32_t dataOffset, size_t mpSegmentOffset); - - /* - * Compute the data offset (as stored in the multi-picture params) for an image given its - * absolute offset (from the start of the first individual image), and the absolute offset - * of the MPF segment in the first individual image. This will return 0 for an image at - * absolute offset 0. - */ - static uint32_t GetImageDataOffset(size_t imageAbsoluteOffset, size_t mpSegmentOffset); -}; - -#endif diff --git a/gfx/skia/skia/src/codec/SkJpegPriv.h b/gfx/skia/skia/src/codec/SkJpegPriv.h deleted file mode 100644 index 1c5770c30c6b..000000000000 --- a/gfx/skia/skia/src/codec/SkJpegPriv.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#ifndef SkJpegPriv_DEFINED -#define SkJpegPriv_DEFINED - -#include "include/codec/SkEncodedOrigin.h" -#include "include/core/SkStream.h" -#include "include/private/base/SkTArray.h" - -#include -// stdio is needed for jpeglib -#include - -extern "C" { - #include "jpeglib.h" // NO_G3_REWRITE - #include "jerror.h" // NO_G3_REWRITE -} - -/* - * Error handling struct - */ -struct skjpeg_error_mgr : public jpeg_error_mgr { - class AutoPushJmpBuf { - public: - AutoPushJmpBuf(skjpeg_error_mgr* mgr) : fMgr(mgr) { fMgr->push(&fJmpBuf); } - ~AutoPushJmpBuf() { fMgr->pop(&fJmpBuf); } - operator jmp_buf&() { return fJmpBuf; } - - private: - skjpeg_error_mgr* const fMgr; - jmp_buf fJmpBuf; - }; - - // When libjpeg initializes the fields of a `jpeg_error_mgr` (in `jpeg_std_error`), it - // leaves the msg_parm fields uninitialized. This is safe, but confuses MSAN, so we - // explicitly zero out the structure when constructing it. (crbug.com/oss-fuzz/68691) - skjpeg_error_mgr() : jpeg_error_mgr({}) {} - - void push(jmp_buf* j) { - SkASSERT(fStack[3] == nullptr); // if we assert here, the stack has overflowed - fStack[3] = fStack[2]; - fStack[2] = fStack[1]; - fStack[1] = fStack[0]; - fStack[0] = j; - } - - void pop(jmp_buf* j) { - SkASSERT(fStack[0] == j); // if we assert here, the pushes and pops were unbalanced - fStack[0] = fStack[1]; - fStack[1] = fStack[2]; - fStack[2] = fStack[3]; - fStack[3] = nullptr; - } - - jmp_buf* fStack[4] = {}; -}; - -#endif diff --git a/gfx/skia/skia/src/codec/SkJpegSegmentScan.cpp b/gfx/skia/skia/src/codec/SkJpegSegmentScan.cpp deleted file mode 100644 index ddd84fae409c..000000000000 --- a/gfx/skia/skia/src/codec/SkJpegSegmentScan.cpp +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright 2023 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "src/codec/SkJpegSegmentScan.h" - -#include "include/core/SkData.h" -#include "include/core/SkStream.h" -#include "include/private/base/SkAssert.h" -#include "src/codec/SkCodecPriv.h" -#include "src/codec/SkJpegConstants.h" - -#include -#include - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// SkJpegSegmentScanner - -SkJpegSegmentScanner::SkJpegSegmentScanner(uint8_t stopMarker) : fStopMarker(stopMarker) {} - -const std::vector& SkJpegSegmentScanner::getSegments() const { return fSegments; } - -sk_sp SkJpegSegmentScanner::GetParameters(const SkData* scannedData, - const SkJpegSegment& segment) { - return SkData::MakeSubset( - scannedData, - segment.offset + kJpegMarkerCodeSize + kJpegSegmentParameterLengthSize, - segment.parameterLength - kJpegSegmentParameterLengthSize); -} - -void SkJpegSegmentScanner::onBytes(const void* data, size_t size) { - const uint8_t* bytes = reinterpret_cast(data); - size_t bytesRemaining = size; - - while (bytesRemaining > 0) { - // Process the data byte-by-byte, unless we are in kSegmentParam or kEntropyCodedData, in - // which case, perform some optimizations to avoid examining every byte. - size_t bytesToMoveForward = 0; - switch (fState) { - case State::kSegmentParam: { - // Skip forward through payloads. - SkASSERT(fSegmentParamBytesRemaining > 0); - bytesToMoveForward = std::min(fSegmentParamBytesRemaining, bytesRemaining); - fSegmentParamBytesRemaining -= bytesToMoveForward; - if (fSegmentParamBytesRemaining == 0) { - fState = State::kEntropyCodedData; - } - break; - } - case State::kEntropyCodedData: { - // Skip through entropy-coded data, only looking at sentinel characters. - const uint8_t* sentinel = - reinterpret_cast(memchr(bytes, 0xFF, bytesRemaining)); - if (sentinel) { - bytesToMoveForward = (sentinel - bytes) + 1; - fState = State::kEntropyCodedDataSentinel; - } else { - bytesToMoveForward = bytesRemaining; - } - break; - } - case State::kDone: - // Skip all data after we have hit our stop marker. - bytesToMoveForward = bytesRemaining; - break; - default: { - onByte(*bytes); - bytesToMoveForward = 1; - break; - } - } - SkASSERT(bytesToMoveForward > 0); - fOffset += bytesToMoveForward; - bytes += bytesToMoveForward; - bytesRemaining -= bytesToMoveForward; - } -} - -void SkJpegSegmentScanner::saveCurrentSegment(uint16_t length) { - SkJpegSegment s = {fCurrentSegmentOffset, fCurrentSegmentMarker, length}; - fSegments.push_back(s); - - fCurrentSegmentMarker = 0; - fCurrentSegmentOffset = 0; -} - -void SkJpegSegmentScanner::onMarkerSecondByte(uint8_t byte) { - SkASSERT(fState == State::kStartOfImageByte1 || fState == State::kSecondMarkerByte1 || - fState == State::kEntropyCodedDataSentinel || - fState == State::kPostEntropyCodedDataFill); - - fCurrentSegmentMarker = byte; - fCurrentSegmentOffset = fOffset - 1; - - if (byte == fStopMarker) { - saveCurrentSegment(0); - fState = State::kDone; - } else if (byte == kJpegMarkerStartOfImage) { - saveCurrentSegment(0); - fState = State::kSecondMarkerByte0; - } else if (MarkerStandsAlone(byte)) { - saveCurrentSegment(0); - fState = State::kEntropyCodedData; - } else { - fCurrentSegmentMarker = byte; - fState = State::kSegmentParamLengthByte0; - } -} - -void SkJpegSegmentScanner::onByte(uint8_t byte) { - switch (fState) { - case State::kStartOfImageByte0: - if (byte != 0xFF) { - SkCodecPrintf("First byte was %02x, not 0xFF", byte); - fState = State::kError; - return; - } - fState = State::kStartOfImageByte1; - break; - case State::kStartOfImageByte1: - if (byte != kJpegMarkerStartOfImage) { - SkCodecPrintf("Second byte was %02x, not %02x", byte, kJpegMarkerStartOfImage); - fState = State::kError; - return; - } - onMarkerSecondByte(byte); - break; - case State::kSecondMarkerByte0: - if (byte != 0xFF) { - SkCodecPrintf("Third byte was %02x, not 0xFF", byte); - fState = State::kError; - return; - } - fState = State::kSecondMarkerByte1; - break; - case State::kSecondMarkerByte1: - // See section B.1.1.3: All markers are assigned two-byte codes: a 0xFF byte followed by - // a byte which is not equal to 0x00 or 0xFF. - if (byte == 0xFF || byte == 0x00) { - SkCodecPrintf("SkJpegSegment marker was 0xFF,0xFF or 0xFF,0x00"); - fState = State::kError; - return; - } - onMarkerSecondByte(byte); - break; - case State::kSegmentParamLengthByte0: - fSegmentParamLengthByte0 = byte; - fState = State::kSegmentParamLengthByte1; - break; - case State::kSegmentParamLengthByte1: { - uint16_t paramLength = 256u * fSegmentParamLengthByte0 + byte; - fSegmentParamLengthByte0 = 0; - - // See section B.1.1.4: A marker segment consists of a marker followed by a sequence - // of related parameters. The first parameter in a marker segment is the two-byte length - // parameter. This length parameter encodes the number of bytes in the marker segment, - // including the length parameter and excluding the two-byte marker. - if (paramLength < kJpegSegmentParameterLengthSize) { - SkCodecPrintf("SkJpegSegment payload length was %u < 2 bytes", paramLength); - fState = State::kError; - return; - } - saveCurrentSegment(paramLength); - fSegmentParamBytesRemaining = paramLength - kJpegSegmentParameterLengthSize; - if (fSegmentParamBytesRemaining > 0) { - fState = State::kSegmentParam; - } else { - fState = State::kEntropyCodedData; - } - break; - } - case State::kSegmentParam: - SkASSERT(fSegmentParamBytesRemaining > 0); - fSegmentParamBytesRemaining -= 1; - if (fSegmentParamBytesRemaining == 0) { - fState = State::kEntropyCodedData; - } - break; - case State::kEntropyCodedData: - if (byte == 0xFF) { - fState = State::kEntropyCodedDataSentinel; - } - break; - case State::kEntropyCodedDataSentinel: - if (byte == 0x00) { - fState = State::kEntropyCodedData; - } else if (byte == 0xFF) { - fState = State::kPostEntropyCodedDataFill; - } else { - onMarkerSecondByte(byte); - } - break; - case State::kPostEntropyCodedDataFill: - // See section B.1.1.3: Any marker may optionally be preceded by any number of fill - // bytes, which are bytes assigned code 0xFF. Skip past any 0xFF fill bytes that may be - // present at the end of the entropy-coded data. - if (byte == 0xFF) { - fState = State::kPostEntropyCodedDataFill; - } else if (byte == 0x00) { - SkCodecPrintf("Post entropy coded data had 0xFF,0x00"); - fState = State::kError; - return; - } else { - onMarkerSecondByte(byte); - } - break; - case State::kDone: - break; - case State::kError: - break; - } -} diff --git a/gfx/skia/skia/src/codec/SkJpegSegmentScan.h b/gfx/skia/skia/src/codec/SkJpegSegmentScan.h deleted file mode 100644 index 5ae3fc71f003..000000000000 --- a/gfx/skia/skia/src/codec/SkJpegSegmentScan.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * 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 SkJpegSegmentScan_codec_DEFINED -#define SkJpegSegmentScan_codec_DEFINED - -#include "include/core/SkRefCnt.h" -#include "src/codec/SkJpegConstants.h" - -#include -#include -#include -#include - -class SkData; -class SkStream; - -/* - * A JPEG segment. - */ -struct SkJpegSegment { - // The offset in bytes from the initial position to where this segment starts. - size_t offset = 0; - // The second byte of the marker code, which determines the segment type. - uint8_t marker = 0; - // The length of the parameters for this segment (including the two bytes used to specify - // the length). - uint16_t parameterLength = 0; -}; - -/* - * Class for scanning JPEG data. The JPEG format consists of a sequence of segments that begin with - * a marker, with entropy-coded data in between. - */ -class SkJpegSegmentScanner { -public: - SkJpegSegmentScanner(uint8_t stopMarker = kJpegMarkerEndOfImage); - - bool isDone() const { return fState == State::kDone; } - bool hadError() const { return fState == State::kError; } - - // Provide more bytes of data to the state machine. - void onBytes(const void* data, size_t size); - - // Return the segments that have been retrieved so far. If an error was encountered, this will - // include all segments up to the error. - const std::vector& getSegments() const; - - // Helper function to retrieve the parameters for a segment. ScannedData must be the data that - // was scanned to produce segment, starting at StartOfImage. - static sk_sp GetParameters(const SkData* scannedData, const SkJpegSegment& segment); - -private: - // The scanner is a state machine. State transitions happen when a byte is read. - enum class State { - // The initial state, before we read the 0xFF,0xD8,0xFF JPEG signature. - kStartOfImageByte0, - // We have read the 0xFF of the JPEG signature. - kStartOfImageByte1, - // We have read the 0xFF,0xD8 of the JPEG signature. The next byte should be the 0xFF that - // completes the JPEG signature and starts the second marker (the one after StartOfImage). - kSecondMarkerByte0, - // We have read the full JPEG signature. The next byte should be the the second byte of the - // second marker. - kSecondMarkerByte1, - // We have read a marker that does not stand alone. The next byte is the first byte of the - // length of the parameters, - kSegmentParamLengthByte0, - // We have read the first byte of the length of the parameters (and it is stored in - // |fSegmentParamLengthByte0|). The next byte we read is the second byte of the length of - // the parameters. - kSegmentParamLengthByte1, - // We have read the full length of the parameters. The next |fSegmentParamBytesRemaining| - // bytes are the parameters. - kSegmentParam, - // We have read a marker and (optionally) its parameters, and we are now reading entropy- - // coded data. All subsequent bytes until we reach 0xFF are entropy-coded data. - kEntropyCodedData, - // We reached an 0xFF in entropy-coded data. If the next byte is 0x00 then we continue - // reading entropy-coded data. If the next byte is 0xFF then we are reading fill data. - // If the next byte is anything else then it is the second byte of a marker. - kEntropyCodedDataSentinel, - // We are reading fill data. If the next byte is 0xFF then we are still reading fill data, - // otherwise the next byte is the second byte of a marker. - kPostEntropyCodedDataFill, - // We reached |fStopMarker| and have stopped tracking our state. - kDone, - // We hit an error somewhere and have given up. - kError, - }; - State fState = State::kStartOfImageByte0; - - // Update state transition when a single byte is read. - void onByte(uint8_t byte); - - // Perform the appropriate state transition for when a marker is read. This will set - // |fCurrentSegmentMarker| and |fCurrentSegmentOffset|, and potentially call saveCurrentSegment. - void onMarkerSecondByte(uint8_t byte); - - // Add a new entry in |segments| for |fCurrentSegmentMarker| and offset |fCurrentSegmentOffset| - // and the specified length. - void saveCurrentSegment(uint16_t length); - - static bool MarkerStandsAlone(uint8_t marker) { - // These markers are TEM (0x01), RSTm (0xD0 through 0xD7), SOI (0xD8), and - // EOI (0xD9). See section B.1.1.3, Marker assignments. - return marker == 0x01 || (marker >= 0xD0 && marker <= 0xD9); - } - - // Stop tracking state when we hit this marker. If this is 0x00, then never stop. - const uint8_t fStopMarker; - - // The number of bytes that have been processed so far. - size_t fOffset = 0; - - // If |fState| is kSegmentParamLengthByte1, then this is the value of the the previous byte. - uint8_t fSegmentParamLengthByte0 = 0; - - // If |fState| is kSegmentParam, then this is the number of bytes reamining in the current - // segment. - size_t fSegmentParamBytesRemaining = 0; - - // The offset and marker for the segment started by the previous call to OnMarkerSecondByte. - // These are re-set when SaveCurrentSegment is called. - size_t fCurrentSegmentOffset = 0; - uint8_t fCurrentSegmentMarker = 0; - - std::vector fSegments; -}; - -#endif diff --git a/gfx/skia/skia/src/codec/SkJpegSourceMgr.cpp b/gfx/skia/skia/src/codec/SkJpegSourceMgr.cpp deleted file mode 100644 index bc139b49d6ef..000000000000 --- a/gfx/skia/skia/src/codec/SkJpegSourceMgr.cpp +++ /dev/null @@ -1,439 +0,0 @@ -/* - * Copyright 2023 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "src/codec/SkJpegSourceMgr.h" - -#include "include/core/SkData.h" -#include "include/core/SkRefCnt.h" -#include "include/core/SkStream.h" -#include "include/core/SkTypes.h" -#include "src/codec/SkCodecPriv.h" - -#ifdef SK_CODEC_DECODES_JPEG_GAINMAPS -#include "src/codec/SkJpegConstants.h" -#include "src/codec/SkJpegSegmentScan.h" -#endif // SK_CODEC_DECODES_JPEG_GAINMAPS - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// SkStream helpers. - -/* - * Class that will will rewind an SkStream, and then restore it to its original position when it - * goes out of scope. If the SkStream is not seekable, then the stream will not be altered at all, - * and will return false from canRestore. - */ - -class ScopedSkStreamRestorer { -public: - ScopedSkStreamRestorer(SkStream* stream) : fStream(stream), fPosition(stream->getPosition()) { - if (!fStream->rewind()) { - SkCodecPrintf("Failed to rewind decoder stream.\n"); - } - } - ~ScopedSkStreamRestorer() { - if (!fStream->seek(fPosition)) { - SkCodecPrintf("Failed to restore decoder stream.\n"); - } - } - -private: - SkStream* const fStream; - const size_t fPosition; -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// SkJpegMemorySourceMgr - -class SkJpegMemorySourceMgr : public SkJpegSourceMgr { -public: - SkJpegMemorySourceMgr(SkStream* stream) : SkJpegSourceMgr(stream) {} - ~SkJpegMemorySourceMgr() override {} - - void initSource(const uint8_t*& nextInputByte, size_t& bytesInBuffer) override { - nextInputByte = reinterpret_cast(fStream->getMemoryBase()); - bytesInBuffer = static_cast(fStream->getLength()); - } - bool fillInputBuffer(const uint8_t*& nextInputByte, size_t& bytesInBuffer) override { - // The whole JPEG data is expected to reside in the supplied memory buffer, so any request - // for more data beyond the given buffer size is treated as an error. - SkCodecPrintf("Asked to re-fill a memory-mapped stream.\n"); - return false; - } - bool skipInputBytes(size_t bytesToSkip, - const uint8_t*& nextInputByte, - size_t& bytesInBuffer) override { - if (bytesToSkip > bytesInBuffer) { - SkCodecPrintf("Asked to read past end of a memory-mapped stream.\n"); - return false; - } - nextInputByte += bytesToSkip; - bytesInBuffer -= bytesToSkip; - return true; - } -#ifdef SK_CODEC_DECODES_JPEG_GAINMAPS - const std::vector& getAllSegments() override { - if (fScanner) { - return fScanner->getSegments(); - } - fScanner = std::make_unique(kJpegMarkerEndOfImage); - fScanner->onBytes(fStream->getMemoryBase(), fStream->getLength()); - return fScanner->getSegments(); - } - sk_sp getSubsetData(size_t offset, size_t size, bool* wasCopied) override { - if (offset > fStream->getLength() || size > fStream->getLength() - offset) { - return nullptr; - } - if (wasCopied) { - *wasCopied = false; - } - return SkData::MakeWithoutCopy( - reinterpret_cast(fStream->getMemoryBase()) + offset, size); - } - sk_sp getSegmentParameters(const SkJpegSegment& segment) override { - const uint8_t* base = - reinterpret_cast(fStream->getMemoryBase()) + segment.offset; - SkASSERT(segment.offset < fStream->getLength()); - SkASSERT(kJpegMarkerCodeSize + segment.parameterLength <= - fStream->getLength() - segment.offset); - - // Read the marker and verify it matches `segment`. - SkASSERT(base[0] == 0xFF); - SkASSERT(base[1] == segment.marker); - - // Read the parameter length and verify it matches `segment`. - SkASSERT(256 * base[2] + base[3] == segment.parameterLength); - if (segment.parameterLength <= kJpegSegmentParameterLengthSize) { - return nullptr; - } - - // Read the remainder of the segment. - return SkData::MakeWithoutCopy(base + kJpegMarkerCodeSize + kJpegSegmentParameterLengthSize, - segment.parameterLength - kJpegSegmentParameterLengthSize); - } -#endif // SK_CODEC_DECODES_JPEG_GAINMAPS -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// SkJpegBufferedSourceMgr - -class SkJpegBufferedSourceMgr : public SkJpegSourceMgr { -public: - SkJpegBufferedSourceMgr(SkStream* stream, size_t bufferSize) : SkJpegSourceMgr(stream) { - fBuffer = SkData::MakeUninitialized(bufferSize); - } - ~SkJpegBufferedSourceMgr() override {} - - void initSource(const uint8_t*& nextInputByte, size_t& bytesInBuffer) override { - nextInputByte = fBuffer->bytes(); - bytesInBuffer = 0; - } - bool fillInputBuffer(const uint8_t*& nextInputByte, size_t& bytesInBuffer) override { - size_t bytesRead = fStream->read(fBuffer->writable_data(), fBuffer->size()); - if (bytesRead == 0) { - // Fail if we read zero bytes (libjpeg will accept any non-zero number of bytes). - SkCodecPrintf("Hit end of file reading a buffered stream.\n"); - return false; - } - nextInputByte = fBuffer->bytes(); - bytesInBuffer = bytesRead; - return true; - } - bool skipInputBytes(size_t bytesToSkip, - const uint8_t*& nextInputByte, - size_t& bytesInBuffer) override { - // Skip through the already-read (or already in memory) buffer. - if (bytesToSkip <= bytesInBuffer) { - nextInputByte += bytesToSkip; - bytesInBuffer -= bytesToSkip; - return true; - } - bytesToSkip -= bytesInBuffer; - - // Fail if we skip past the end of the stream. - if (fStream->skip(bytesToSkip) != bytesToSkip) { - SkCodecPrintf("Failed to skip through buffered stream.\n"); - return false; - } - - bytesInBuffer = 0; - nextInputByte = fBuffer->bytes(); - return true; - } -#ifdef SK_CODEC_DECODES_JPEG_GAINMAPS - const std::vector& getAllSegments() override { - if (fScanner) { - return fScanner->getSegments(); - } - ScopedSkStreamRestorer streamRestorer(fStream); - fScanner = std::make_unique(kJpegMarkerEndOfImage); - while (!fScanner->isDone() && !fScanner->hadError()) { - constexpr size_t kBufferSize = 1024; - uint8_t buffer[kBufferSize]; - size_t bytesRead = fStream->read(buffer, kBufferSize); - if (bytesRead == 0) { - SkCodecPrintf("Unexpected EOF.\n"); - break; - } - fScanner->onBytes(buffer, bytesRead); - } - return fScanner->getSegments(); - } - sk_sp getSubsetData(size_t offset, size_t size, bool* wasCopied) override { - ScopedSkStreamRestorer streamRestorer(fStream); - if (!fStream->seek(offset)) { - SkCodecPrintf("Failed to seek to subset stream position.\n"); - return nullptr; - } - sk_sp data = SkData::MakeUninitialized(size); - if (fStream->read(data->writable_data(), size) != size) { - SkCodecPrintf("Failed to read subset stream data.\n"); - return nullptr; - } - if (wasCopied) { - *wasCopied = true; - } - return data; - } - sk_sp getSegmentParameters(const SkJpegSegment& segment) override { - // If the segment's parameter length isn't longer than the two bytes for the length, - // early-out early-out. - if (segment.parameterLength <= kJpegSegmentParameterLengthSize) { - return nullptr; - } - - // Seek to the start of the segment. - ScopedSkStreamRestorer streamRestorer(fStream); - if (!fStream->seek(segment.offset)) { - SkCodecPrintf("Failed to seek to segment\n"); - return nullptr; - } - - // Read the marker and verify it matches `segment`. - uint8_t markerCode[kJpegMarkerCodeSize] = {0}; - if (fStream->read(markerCode, kJpegMarkerCodeSize) != kJpegMarkerCodeSize) { - SkCodecPrintf("Failed to read segment marker code\n"); - return nullptr; - } - SkASSERT(markerCode[0] == 0xFF); - SkASSERT(markerCode[1] == segment.marker); - - // Read the parameter length and verify it matches `segment`. - uint8_t parameterLength[kJpegSegmentParameterLengthSize] = {0}; - if (fStream->read(parameterLength, kJpegSegmentParameterLengthSize) != - kJpegSegmentParameterLengthSize) { - SkCodecPrintf("Failed to read parameter length\n"); - return nullptr; - } - SkASSERT(256 * parameterLength[0] + parameterLength[1] == segment.parameterLength); - - // Read the remainder of the segment. - size_t sizeToRead = segment.parameterLength - kJpegSegmentParameterLengthSize; - auto result = SkData::MakeUninitialized(sizeToRead); - if (fStream->read(result->writable_data(), sizeToRead) != sizeToRead) { - return nullptr; - } - - return result; - } -#endif // SK_CODEC_DECODES_JPEG_GAINMAPS - -private: - sk_sp fBuffer; -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// SkJpegUnseekableSourceMgr - -#ifdef SK_CODEC_DECODES_JPEG_GAINMAPS -/* - * This class implements SkJpegSourceMgr for a stream that cannot seek or rewind. It scans the data - * as it is presented to the decoder. This allows it to track the position of segments, so that it - * can extract subsets at a specific offset (e.g, relative to the EndOfImage segment for JpegR or - * relative to an MPF segment for MPF). - */ -class SkJpegUnseekableSourceMgr : public SkJpegSourceMgr { -public: - SkJpegUnseekableSourceMgr(SkStream* stream, size_t bufferSize) : SkJpegSourceMgr(stream) { - fBuffer = SkData::MakeUninitialized(bufferSize); - fScanner = std::make_unique(kJpegMarkerEndOfImage); - } - ~SkJpegUnseekableSourceMgr() override {} - - void initSource(const uint8_t*& nextInputByte, size_t& bytesInBuffer) override { - nextInputByte = fBuffer->bytes(); - bytesInBuffer = 0; - } - bool fillInputBuffer(const uint8_t*& nextInputByte, size_t& bytesInBuffer) override { - if (!readToBufferAndScan(fBuffer->size())) { - SkCodecPrintf("Failure filling unseekable input buffer.\n"); - return false; - } - nextInputByte = fBuffer->bytes(); - bytesInBuffer = fLastReadSize; - return true; - } - bool skipInputBytes(size_t bytesToSkip, - const uint8_t*& nextInputByte, - size_t& bytesInBuffer) override { - // Skip through the already-read (or already in memory) buffer. - if (bytesToSkip <= bytesInBuffer) { - nextInputByte += bytesToSkip; - bytesInBuffer -= bytesToSkip; - return true; - } - bytesToSkip -= bytesInBuffer; - - // Read the remaining bytes to skip into fBuffer and feed them into fScanner. - while (bytesToSkip > 0) { - if (!readToBufferAndScan(std::min(bytesToSkip, fBuffer->size()))) { - SkCodecPrintf("Failure filling unseekable input buffer.\n"); - return false; - } - bytesToSkip -= fLastReadSize; - } - - // Indicate to libjpeg that it it needs to call fillInputBuffer. - bytesInBuffer = 0; - nextInputByte = fBuffer->bytes(); - return true; - } - const std::vector& getAllSegments() override { - while (!fScanner->isDone() && !fScanner->hadError()) { - if (!readToBufferAndScan(fBuffer->size())) { - SkCodecPrintf("Failure finishing unseekable input buffer.\n"); - break; - } - } - return fScanner->getSegments(); - } - sk_sp getSubsetData(size_t offset, size_t size, bool* wasCopied) override { - // If we haven't reached the EndOfImage, then we are throwing away the base image before - // decoding it. This is only reasonable for tests. - if (!fScanner->isDone()) { - SkCodecPrintf("getSubsetData is prematurely terminating scan.\n"); - } - - // If we have read past offset, we can never get that data back again. - if (offset < fLastReadOffset) { - SkCodecPrintf("Requested that is gone.\n"); - return nullptr; - } - - // Allocate the memory to return, and indicate that the result is a copy. - sk_sp subsetData = SkData::MakeUninitialized(size); - uint8_t* subsetDataCurrent = reinterpret_cast(subsetData->writable_data()); - - // Determine the relationship between the offset we're reading from and |fBuffer|. - size_t offsetIntoBuffer = offset - fLastReadOffset; - if (offsetIntoBuffer >= fLastReadSize) { - // We have to skip past |fBuffer| to get to |offset|. - fLastReadOffset += fLastReadSize; - fLastReadSize = 0; - - // Skip any additional bytes needed to get to |offset|. - size_t bytesToSkip = offset - fLastReadOffset; - while (bytesToSkip > 0) { - size_t bytesSkipped = fStream->skip(bytesToSkip); - if (bytesSkipped == 0) { - SkCodecPrintf("Failed to skip bytes before subset.\n"); - return nullptr; - } - bytesToSkip -= bytesSkipped; - fLastReadOffset += bytesSkipped; - } - } else { - // This assert is to emphatically document the side of the branch we're on. - SkASSERT(offsetIntoBuffer < fLastReadSize); - - // Some of the data we want to copy has already been read into |fBuffer|. Copy that data - // to |subsetData| - size_t bytesToReadFromBuffer = std::min(fLastReadSize - offsetIntoBuffer, size); - memcpy(subsetDataCurrent, fBuffer->bytes() + offsetIntoBuffer, bytesToReadFromBuffer); - size -= bytesToReadFromBuffer; - subsetDataCurrent += bytesToReadFromBuffer; - - // If all of the data that we needed was in |fBuffer|, then return early. - if (size == 0) { - if (wasCopied) { - *wasCopied = true; - } - return subsetData; - } - // We will now have to read beyond |fBuffer|, so reset it. - fLastReadOffset += fLastReadSize; - fLastReadSize = 0; - } - - // Read the remaining data from |fStream|. - while (size > 0) { - size_t bytesRead = fStream->read(subsetDataCurrent, size); - if (bytesRead == 0) { - SkCodecPrintf("Failed to read subset stream data.\n"); - return nullptr; - } - size -= bytesRead; - subsetDataCurrent += bytesRead; - fLastReadOffset += bytesRead; - } - - if (wasCopied) { - *wasCopied = true; - } - return subsetData; - } - sk_sp getSegmentParameters(const SkJpegSegment& segment) override { - // The only way to implement this for an unseekable stream is to record the parameters as - // they are scanned. - return nullptr; - } - -private: - // Read the specified number of bytes into fBuffer and feed them to fScanner. The number of - // bytes must not be larger than fBuffer's size. - bool readToBufferAndScan(size_t bytesToRead) { - SkASSERT(bytesToRead <= fBuffer->size()); - fLastReadOffset += fLastReadSize; - fLastReadSize = fStream->read(fBuffer->writable_data(), bytesToRead); - if (fLastReadSize == 0) { - SkCodecPrintf("Hit end of file reading an unseekable stream.\n"); - return false; - } - fScanner->onBytes(fBuffer->bytes(), fLastReadSize); - return true; - } - - sk_sp fBuffer; - - // The number of bytes that were most recently read into fBuffer (this can be less than the size - // of fBuffer). - size_t fLastReadSize = 0; - - // The offset into the stream (total number of bytes read) at the time of our most recent read - // into fBuffer. - size_t fLastReadOffset = 0; -}; -#endif // SK_CODEC_DECODES_JPEG_GAINMAPS - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// SkJpegSourceMgr - -// static -std::unique_ptr SkJpegSourceMgr::Make(SkStream* stream, size_t bufferSize) { -#ifdef SK_CODEC_DECODES_JPEG_GAINMAPS - if (!stream->hasPosition()) { - return std::make_unique(stream, bufferSize); - } -#endif - if (stream->hasLength() && stream->getMemoryBase()) { - return std::make_unique(stream); - } - return std::make_unique(stream, bufferSize); -} - -SkJpegSourceMgr::SkJpegSourceMgr(SkStream* stream) : fStream(stream) {} - -SkJpegSourceMgr::~SkJpegSourceMgr() = default; diff --git a/gfx/skia/skia/src/codec/SkJpegSourceMgr.h b/gfx/skia/skia/src/codec/SkJpegSourceMgr.h deleted file mode 100644 index e6d408d6ec26..000000000000 --- a/gfx/skia/skia/src/codec/SkJpegSourceMgr.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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 SkJpegSourceMgr_codec_DEFINED -#define SkJpegSourceMgr_codec_DEFINED - -#include "include/core/SkTypes.h" - -#ifdef SK_CODEC_DECODES_JPEG_GAINMAPS -#include "include/core/SkRefCnt.h" -#endif // SK_CODEC_DECODES_JPEG_GAINMAPS - -#include -#include -#include - -#ifdef SK_CODEC_DECODES_JPEG_GAINMAPS -#include -#endif // SK_CODEC_DECODES_JPEG_GAINMAPS - -class SkStream; - -#ifdef SK_CODEC_DECODES_JPEG_GAINMAPS -class SkData; -class SkJpegSegmentScanner; -struct SkJpegSegment; -#endif // SK_CODEC_DECODES_JPEG_GAINMAPS - -/* - * Interface to adapt an SkStream to the jpeg_source_mgr interface. This interface has different - * implementations for SkStreams with different capabilities. - */ -class SkJpegSourceMgr { -public: - // Create a source manager. If the source manager will buffer data, |bufferSize| specifies - // the size of that buffer. - static std::unique_ptr Make(SkStream* stream, size_t bufferSize = 1024); - virtual ~SkJpegSourceMgr(); - - // Interface called by libjpeg via its jpeg_source_mgr interface. - virtual void initSource(const uint8_t*& nextInputByte, size_t& bytesInBuffer) = 0; - virtual bool fillInputBuffer(const uint8_t*& nextInputByte, size_t& bytesInBuffer) = 0; - virtual bool skipInputBytes(size_t bytes, - const uint8_t*& nextInputByte, - size_t& bytesInBuffer) = 0; - -#ifdef SK_CODEC_DECODES_JPEG_GAINMAPS - // Parse this stream all the way through its EndOfImage marker and return the list of segments. - // Return false if there is an error or if no EndOfImage marker is found. - virtual const std::vector& getAllSegments() = 0; - - // Return an the data for a subset of this source's stream, with the specified offset and size. - // If the returned SkData is a copy (it does not refer directly to memory owned by |fStream|), - // then |wasCopied| is set to true. - virtual sk_sp getSubsetData(size_t offset, size_t size, bool* wasCopied = nullptr) = 0; - - // Segments start with a 2 byte marker, followed by a 2 byte parameter length (which includes - // those two bytes, followed by parameters. Return the parameters portion of the specified - // segment. If possible, the returned SkData will refer to memory owned by |fStream|. - virtual sk_sp getSegmentParameters(const SkJpegSegment& segment) = 0; -#endif // SK_CODEC_DECODES_JPEG_GAINMAPS - -protected: - SkJpegSourceMgr(SkStream* stream); - SkStream* const fStream; // unowned - -#ifdef SK_CODEC_DECODES_JPEG_GAINMAPS - // The segment scanner is lazily creatd only when needed. - std::unique_ptr fScanner; -#endif // SK_CODEC_DECODES_JPEG_GAINMAPS -}; - -#endif diff --git a/gfx/skia/skia/src/codec/SkJpegUtility.cpp b/gfx/skia/skia/src/codec/SkJpegUtility.cpp deleted file mode 100644 index 01cb2c6a0f91..000000000000 --- a/gfx/skia/skia/src/codec/SkJpegUtility.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "src/codec/SkJpegUtility.h" - -#include "include/core/SkStream.h" -#include "include/core/SkTypes.h" -#include "src/codec/SkCodecPriv.h" -#include "src/codec/SkJpegPriv.h" - -#include -#include - -extern "C" { - #include "jpeglib.h" // NO_G3_REWRITE -} - -/* - * Call longjmp to continue execution on an error - */ -void skjpeg_err_exit(j_common_ptr dinfo) { - // Simply return to Skia client code - // JpegDecoderMgr will take care of freeing memory - skjpeg_error_mgr* error = static_cast(dinfo->err); - (*error->output_message)(dinfo); - if (error->fStack[0] == nullptr) { - SK_ABORT("JPEG error with no jmp_buf set."); - } - longjmp(*error->fStack[0], 1); -} - -// Functions for buffered sources // - -/* - * Initialize the buffered source manager - */ -static void sk_init_buffered_source(j_decompress_ptr dinfo) { - skjpeg_source_mgr* src = (skjpeg_source_mgr*) dinfo->src; - src->next_input_byte = (const JOCTET*) src->fBuffer; - src->bytes_in_buffer = 0; -} - -/* - * Fill the input buffer from the stream - */ -static boolean sk_fill_buffered_input_buffer(j_decompress_ptr dinfo) { - skjpeg_source_mgr* src = (skjpeg_source_mgr*) dinfo->src; - size_t bytes = src->fStream->read(src->fBuffer, skjpeg_source_mgr::kBufferSize); - - // libjpeg is still happy with a less than full read, as long as the result is non-zero - if (bytes == 0) { - // Let libjpeg know that the buffer needs to be refilled - src->next_input_byte = nullptr; - src->bytes_in_buffer = 0; - return FALSE; - } - - src->next_input_byte = (const JOCTET*) src->fBuffer; - src->bytes_in_buffer = bytes; - return TRUE; -} - -/* - * Skip a certain number of bytes in the stream - */ -static void sk_skip_buffered_input_data(j_decompress_ptr dinfo, long numBytes) { - skjpeg_source_mgr* src = (skjpeg_source_mgr*) dinfo->src; - size_t bytes = (size_t) numBytes; - - if (bytes > src->bytes_in_buffer) { - size_t bytesToSkip = bytes - src->bytes_in_buffer; - if (bytesToSkip != src->fStream->skip(bytesToSkip)) { - SkCodecPrintf("Failure to skip.\n"); - dinfo->err->error_exit((j_common_ptr) dinfo); - return; - } - - src->next_input_byte = (const JOCTET*) src->fBuffer; - src->bytes_in_buffer = 0; - } else { - src->next_input_byte += numBytes; - src->bytes_in_buffer -= numBytes; - } -} - -/* - * We do not need to do anything to terminate our stream - */ -static void sk_term_source(j_decompress_ptr dinfo) -{ - // The current implementation of SkJpegCodec does not call - // jpeg_finish_decompress(), so this function is never called. - // If we want to modify this function to do something, we also - // need to modify SkJpegCodec to call jpeg_finish_decompress(). -} - -// Functions for memory backed sources // - -/* - * Initialize the mem backed source manager - */ -static void sk_init_mem_source(j_decompress_ptr dinfo) { - /* no work necessary here, all things are done in constructor */ -} - -static void sk_skip_mem_input_data (j_decompress_ptr cinfo, long num_bytes) { - jpeg_source_mgr* src = cinfo->src; - size_t bytes = static_cast(num_bytes); - if(bytes > src->bytes_in_buffer) { - src->next_input_byte = nullptr; - src->bytes_in_buffer = 0; - } else { - src->next_input_byte += bytes; - src->bytes_in_buffer -= bytes; - } -} - -static boolean sk_fill_mem_input_buffer (j_decompress_ptr cinfo) { - /* The whole JPEG data is expected to reside in the supplied memory, - * buffer, so any request for more data beyond the given buffer size - * is treated as an error. - */ - return FALSE; -} - -/* - * Constructor for the source manager that we provide to libjpeg - * We provide skia implementations of all of the stream processing functions required by libjpeg - */ -skjpeg_source_mgr::skjpeg_source_mgr(SkStream* stream) - : fStream(stream) -{ - if (stream->hasLength() && stream->getMemoryBase()) { - init_source = sk_init_mem_source; - fill_input_buffer = sk_fill_mem_input_buffer; - skip_input_data = sk_skip_mem_input_data; - resync_to_restart = jpeg_resync_to_restart; - term_source = sk_term_source; - bytes_in_buffer = static_cast(stream->getLength()); - next_input_byte = static_cast(stream->getMemoryBase()); - } else { - init_source = sk_init_buffered_source; - fill_input_buffer = sk_fill_buffered_input_buffer; - skip_input_data = sk_skip_buffered_input_data; - resync_to_restart = jpeg_resync_to_restart; - term_source = sk_term_source; - } -} diff --git a/gfx/skia/skia/src/codec/SkJpegUtility.h b/gfx/skia/skia/src/codec/SkJpegUtility.h deleted file mode 100644 index 5c279bfc0c7a..000000000000 --- a/gfx/skia/skia/src/codec/SkJpegUtility.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#ifndef SkJpegUtility_codec_DEFINED -#define SkJpegUtility_codec_DEFINED - -#include - -extern "C" { - // We need to include stdio.h before jpeg because jpeg does not include it, but uses FILE - // See https://github.com/libjpeg-turbo/libjpeg-turbo/issues/17 - #include // IWYU pragma: keep - #include "jpeglib.h" // NO_G3_REWRITE -} - -class SkStream; - -/* - * Error handling function - */ -void skjpeg_err_exit(j_common_ptr cinfo); - -/* - * Source handling struct for that allows libjpeg to use our stream object - */ -struct skjpeg_source_mgr : jpeg_source_mgr { - skjpeg_source_mgr(SkStream* stream); - - SkStream* fStream; // unowned - enum { - // TODO (msarett): Experiment with different buffer sizes. - // This size was chosen because it matches SkImageDecoder. - kBufferSize = 1024 - }; - uint8_t fBuffer[kBufferSize]; -}; - -#endif diff --git a/gfx/skia/skia/src/codec/SkJpegXmp.cpp b/gfx/skia/skia/src/codec/SkJpegXmp.cpp deleted file mode 100644 index c2df4a1a607e..000000000000 --- a/gfx/skia/skia/src/codec/SkJpegXmp.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright 2023 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "src/codec/SkJpegXmp.h" - -#include "include/private/SkGainmapInfo.h" -#include "include/utils/SkParse.h" -#include "src/codec/SkCodecPriv.h" -#include "src/codec/SkJpegConstants.h" -#include "src/core/SkMD5.h" -#include "src/xml/SkDOM.h" - -#include -#include - -constexpr size_t kGuidAsciiSize = 32; - -/* - * Extract standard XMP metadata. The decoderApp1Params must outlive the returned SkData. - * - * See XMP Specification Part 3: Storage in files, Section 1.1.3: JPEG. - */ -static sk_sp read_xmp_standard(const std::vector>& decoderApp1Params) { - constexpr size_t kSigSize = sizeof(kXMPStandardSig); - // Iterate through the image's segments. - for (const auto& params : decoderApp1Params) { - // Skip segments that don't have the right marker, signature, or are too small. - if (params->size() <= kSigSize) { - continue; - } - if (memcmp(params->bytes(), kXMPStandardSig, kSigSize) != 0) { - continue; - } - return SkData::MakeWithoutCopy(params->bytes() + kSigSize, params->size() - kSigSize); - } - return nullptr; -} - -/* - * Extract and validate extended XMP metadata. - * - * See XMP Specification Part 3: Storage in files, Section 1.1.3.1: Extended XMP in JPEG: - * Each chunk is written into the JPEG file within a separate APP1 marker segment. Each ExtendedXMP - * marker segment contains: - * - A null-terminated signature string - * - A 128-bit GUID stored as a 32-byte ASCII hex string, capital A-F, no null termination. The - * GUID is a 128-bit MD5 digest of the full ExtendedXMP serialization. - * - The full length of the ExtendedXMP serialization as a 32-bit unsigned integer. - * - The offset of this portion as a 32-bit unsigned integer. - * - The portion of the ExtendedXMP - */ -static sk_sp read_xmp_extended(const std::vector>& decoderApp1Params, - const char* guidAscii) { - constexpr size_t kSigSize = sizeof(kXMPExtendedSig); - constexpr size_t kFullLengthSize = 4; - constexpr size_t kOffsetSize = 4; - constexpr size_t kHeaderSize = kSigSize + kGuidAsciiSize + kFullLengthSize + kOffsetSize; - - // Validate the provided ASCII guid. - if (strlen(guidAscii) != kGuidAsciiSize) { - SkCodecPrintf("Invalid ASCII GUID size.\n"); - return nullptr; - } - SkMD5::Digest guidAsDigest; - for (size_t i = 0; i < kGuidAsciiSize; ++i) { - uint8_t digit = 0; - if (guidAscii[i] >= '0' && guidAscii[i] <= '9') { - digit = guidAscii[i] - '0'; - } else if (guidAscii[i] >= 'A' && guidAscii[i] <= 'F') { - digit = guidAscii[i] - 'A' + 10; - } else { - SkCodecPrintf("GUID is not upper-case hex.\n"); - return nullptr; - } - if (i % 2 == 0) { - guidAsDigest.data[i / 2] = 16 * digit; - } else { - guidAsDigest.data[i / 2] += digit; - } - } - - // Iterate through the image's segments. - uint32_t fullLength = 0; - using Part = std::tuple>; - std::vector parts; - for (const auto& params : decoderApp1Params) { - // Skip segments that don't have the right marker, signature, or are too small. - if (params->size() <= kHeaderSize) { - continue; - } - if (memcmp(params->bytes(), kXMPExtendedSig, kSigSize) != 0) { - continue; - } - - // Ignore parts that do not match the expected GUID. - const uint8_t* partGuidAscii = params->bytes() + kSigSize; - if (memcmp(guidAscii, partGuidAscii, kGuidAsciiSize) != 0) { - SkCodecPrintf("Ignoring unexpected GUID.\n"); - continue; - } - - // Read the full length and the offset for this part. - uint32_t partFullLength = 0; - uint32_t partOffset = 0; - const uint8_t* partFullLengthBytes = params->bytes() + kSigSize + kGuidAsciiSize; - const uint8_t* partOffsetBytes = - params->bytes() + kSigSize + kGuidAsciiSize + kFullLengthSize; - for (size_t i = 0; i < 4; ++i) { - partFullLength *= 256; - partOffset *= 256; - partFullLength += partFullLengthBytes[i]; - partOffset += partOffsetBytes[i]; - } - - // If this is the first part, set our global full length size. - if (parts.empty()) { - fullLength = partFullLength; - } - - // Ensure all parts agree on the full length. - if (partFullLength != fullLength) { - SkCodecPrintf("Multiple parts had different total lengths.\n"); - return nullptr; - } - - // Add it to the list. - auto partData = SkData::MakeWithoutCopy(params->bytes() + kHeaderSize, - params->size() - kHeaderSize); - parts.push_back({partOffset, partData}); - } - if (parts.empty() || fullLength == 0) { - return nullptr; - } - - // Sort the list of parts by offset. - std::sort(parts.begin(), parts.end(), [](const Part& a, const Part& b) { - return std::get<0>(a) < std::get<0>(b); - }); - - // Stitch the parts together. Fail if we find that they are not contiguous. - auto xmpExtendedData = SkData::MakeUninitialized(fullLength); - uint8_t* xmpExtendedBase = reinterpret_cast(xmpExtendedData->writable_data()); - uint8_t* xmpExtendedCurrent = xmpExtendedBase; - SkMD5 md5; - for (const auto& part : parts) { - uint32_t currentOffset = static_cast(xmpExtendedCurrent - xmpExtendedBase); - uint32_t partOffset = std::get<0>(part); - const sk_sp& partData = std::get<1>(part); - // Make sure the data is contiguous and doesn't overflow the buffer. - if (partOffset != currentOffset) { - SkCodecPrintf("XMP extension parts not contiguous\n"); - return nullptr; - } - if (partData->size() > fullLength - currentOffset) { - SkCodecPrintf("XMP extension parts overflow\n"); - return nullptr; - } - memcpy(xmpExtendedCurrent, partData->data(), partData->size()); - xmpExtendedCurrent += partData->size(); - } - // Make sure we wrote the full buffer. - if (static_cast(xmpExtendedCurrent - xmpExtendedBase) != fullLength) { - SkCodecPrintf("XMP extension did not match full length.\n"); - return nullptr; - } - - // Make sure the MD5 hash of the extended data matched the GUID. - md5.write(xmpExtendedData->data(), xmpExtendedData->size()); - if (md5.finish() != guidAsDigest) { - SkCodecPrintf("XMP extension did not hash to GUID.\n"); - return nullptr; - } - - return xmpExtendedData; -} - -std::unique_ptr SkJpegMakeXmp(const std::vector>& decoderApp1Params) { - auto xmpStandard = read_xmp_standard(decoderApp1Params); - if (!xmpStandard) { - return nullptr; - } - - std::unique_ptr xmp = SkXmp::Make(xmpStandard); - if (!xmp) { - return nullptr; - } - - // Extract the GUID (the MD5 hash) of the extended metadata. - const char* extendedGuid = xmp->getExtendedXmpGuid(); - if (!extendedGuid) { - return xmp; - } - - // Extract and validate the extended metadata from the JPEG structure. - auto xmpExtended = read_xmp_extended(decoderApp1Params, extendedGuid); - if (!xmpExtended) { - SkCodecPrintf("Extended XMP was indicated but failed to read or validate.\n"); - return xmp; - } - - return SkXmp::Make(xmpStandard, xmpExtended); -} diff --git a/gfx/skia/skia/src/codec/SkJpegXmp.h b/gfx/skia/skia/src/codec/SkJpegXmp.h deleted file mode 100644 index c25441a64489..000000000000 --- a/gfx/skia/skia/src/codec/SkJpegXmp.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * 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 SkJpegXmp_codec_DEFINED -#define SkJpegXmp_codec_DEFINED - -#include "include/core/SkRefCnt.h" -#include "include/private/SkXmp.h" - -class SkData; - -#include -#include - -// Find and parse all XMP metadata, given a list of all APP1 segment parameters. -std::unique_ptr SkJpegMakeXmp(const std::vector>& decoderApp1Params); - -#endif diff --git a/gfx/skia/skia/src/codec/SkJpegxlCodec.cpp b/gfx/skia/skia/src/codec/SkJpegxlCodec.cpp deleted file mode 100644 index 87a54f7090dc..000000000000 --- a/gfx/skia/skia/src/codec/SkJpegxlCodec.cpp +++ /dev/null @@ -1,495 +0,0 @@ -/* - * Copyright 2021 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "src/codec/SkJpegxlCodec.h" - -#include "include/codec/SkCodec.h" -#include "include/codec/SkJpegxlDecoder.h" -#include "include/core/SkColorType.h" -#include "include/core/SkData.h" -#include "include/core/SkImageInfo.h" -#include "include/core/SkRefCnt.h" -#include "include/core/SkStream.h" -#include "include/core/SkTypes.h" -#include "include/private/SkEncodedInfo.h" -#include "include/private/base/SkTFitsIn.h" -#include "include/private/base/SkTemplates.h" -#include "include/private/base/SkTo.h" -#include "modules/skcms/skcms.h" -#include "src/codec/SkFrameHolder.h" -#include "src/core/SkStreamPriv.h" -#include "src/core/SkSwizzlePriv.h" - -#include "jxl/codestream_header.h" // NO_G3_REWRITE -#include "jxl/decode.h" // NO_G3_REWRITE -#include "jxl/decode_cxx.h" // NO_G3_REWRITE -#include "jxl/types.h" // NO_G3_REWRITE - -#include -#include -#include -#include -#include - -namespace { - -class Frame : public SkFrame { -public: - explicit Frame(int i, SkEncodedInfo::Alpha alpha) : INHERITED(i), fReportedAlpha(alpha) {} - SkEncodedInfo::Alpha onReportedAlpha() const override { return fReportedAlpha; } - -private: - const SkEncodedInfo::Alpha fReportedAlpha; - - using INHERITED = SkFrame; -}; - -} // namespace - -bool SkJpegxlCodec::IsJpegxl(const void* buffer, size_t bytesRead) { - JxlSignature result = JxlSignatureCheck(reinterpret_cast(buffer), bytesRead); - return (result == JXL_SIG_CODESTREAM) || (result == JXL_SIG_CONTAINER); -} - -class SkJpegxlCodecPriv : public SkFrameHolder { -public: - SkJpegxlCodecPriv() : fDecoder(JxlDecoderMake(/* memory_manager= */ nullptr)) {} - JxlDecoderPtr fDecoder; // unique_ptr with custom destructor - JxlBasicInfo fInfo; - bool fSeenAllFrames = false; - std::vector fFrames; - int fLastProcessedFrame = SkCodec::kNoFrame; - void* fDst; - size_t fPixelShift; - size_t fRowBytes; - SkColorType fDstColorType; - -protected: - const SkFrame* onGetFrame(int i) const override { - SkASSERT(i >= 0 && static_cast(i) < fFrames.size()); - return static_cast(&fFrames[i]); - } -}; - -SkJpegxlCodec::SkJpegxlCodec(std::unique_ptr codec, - SkEncodedInfo&& info, - std::unique_ptr stream, - sk_sp data) - : INHERITED(std::move(info), skcms_PixelFormat_RGBA_16161616LE, std::move(stream)) - , fCodec(std::move(codec)) - , fData(std::move(data)) {} - -std::unique_ptr SkJpegxlCodec::MakeFromStream(std::unique_ptr stream, - Result* result) { - SkASSERT(result); - if (!stream) { - *result = SkCodec::kInvalidInput; - return nullptr; - } - *result = kInternalError; - // Either wrap or copy stream data. - sk_sp data = nullptr; - if (stream->getMemoryBase()) { - data = SkData::MakeWithoutCopy(stream->getMemoryBase(), stream->getLength()); - } else { - data = SkCopyStreamToData(stream.get()); - // Data is copied; stream can be released now. - stream.reset(nullptr); - } - - auto priv = std::make_unique(); - JxlDecoder* dec = priv->fDecoder.get(); - - // Only query metadata this time. - auto status = JxlDecoderSubscribeEvents(dec, JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING); - if (status != JXL_DEC_SUCCESS) { - // Fresh instance must accept request for subscription. - SkDEBUGFAIL("libjxl returned unexpected status"); - return nullptr; - } - - status = JxlDecoderSetInput(dec, data->bytes(), data->size()); - if (status != JXL_DEC_SUCCESS) { - // Fresh instance must accept first chunk of input. - SkDEBUGFAIL("libjxl returned unexpected status"); - return nullptr; - } - - status = JxlDecoderProcessInput(dec); - if (status == JXL_DEC_NEED_MORE_INPUT) { - *result = kIncompleteInput; - return nullptr; - } - if (status != JXL_DEC_BASIC_INFO) { - *result = kInvalidInput; - return nullptr; - } - JxlBasicInfo& info = priv->fInfo; - status = JxlDecoderGetBasicInfo(dec, &info); - if (status != JXL_DEC_SUCCESS) { - // Current event is "JXL_DEC_BASIC_INFO" -> can't fail. - SkDEBUGFAIL("libjxl returned unexpected status"); - return nullptr; - } - - // Check that image dimensions are not too large. - if (!SkTFitsIn(info.xsize) || !SkTFitsIn(info.ysize)) { - *result = kInvalidInput; - return nullptr; - } - int32_t width = SkTo(info.xsize); - int32_t height = SkTo(info.ysize); - - bool hasAlpha = (info.alpha_bits != 0); - bool isGray = (info.num_color_channels == 1); - SkEncodedInfo::Alpha alpha = - hasAlpha ? SkEncodedInfo::kUnpremul_Alpha : SkEncodedInfo::kOpaque_Alpha; - SkEncodedInfo::Color color; - if (hasAlpha) { - color = isGray ? SkEncodedInfo::kGrayAlpha_Color : SkEncodedInfo::kRGBA_Color; - } else { - color = isGray ? SkEncodedInfo::kGray_Color : SkEncodedInfo::kRGB_Color; - } - - status = JxlDecoderProcessInput(dec); - if (status != JXL_DEC_COLOR_ENCODING) { - *result = kInvalidInput; - return nullptr; - } - - size_t iccSize = 0; - // TODO(eustas): format field is currently ignored by decoder. - status = JxlDecoderGetICCProfileSize( - dec, /* format = */ nullptr, JXL_COLOR_PROFILE_TARGET_DATA, &iccSize); - if (status != JXL_DEC_SUCCESS) { - // Likely incompatible colorspace. - iccSize = 0; - } - std::unique_ptr profile = nullptr; - if (iccSize) { - auto icc = SkData::MakeUninitialized(iccSize); - // TODO(eustas): format field is currently ignored by decoder. - status = JxlDecoderGetColorAsICCProfile(dec, - /* format = */ nullptr, - JXL_COLOR_PROFILE_TARGET_DATA, - reinterpret_cast(icc->writable_data()), - iccSize); - if (status != JXL_DEC_SUCCESS) { - // Current event is JXL_DEC_COLOR_ENCODING -> can't fail. - SkDEBUGFAIL("libjxl returned unexpected status"); - return nullptr; - } - profile = SkEncodedInfo::ICCProfile::Make(std::move(icc)); - } - - int bitsPerChannel = 16; - - *result = kSuccess; - SkEncodedInfo encodedInfo = - SkEncodedInfo::Make(width, height, color, alpha, bitsPerChannel, std::move(profile)); - - return std::unique_ptr(new SkJpegxlCodec( - std::move(priv), std::move(encodedInfo), std::move(stream), std::move(data))); -} - -SkCodec::Result SkJpegxlCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t rowBytes, - const Options& options, int* rowsDecodedPtr) { - // TODO(eustas): implement - if (options.fSubset) { - return kUnimplemented; - } - auto& codec = *fCodec.get(); - const int index = options.fFrameIndex; - SkASSERT(0 == index || static_cast(index) < codec.fFrames.size()); - auto* dec = codec.fDecoder.get(); - JxlDecoderStatus status; - - if ((codec.fLastProcessedFrame >= index) || (codec.fLastProcessedFrame = SkCodec::kNoFrame)) { - codec.fLastProcessedFrame = SkCodec::kNoFrame; - JxlDecoderRewind(dec); - status = JxlDecoderSubscribeEvents(dec, JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE); - if (status != JXL_DEC_SUCCESS) { - // Fresh decoder instance (after rewind) must accept subscription request. - SkDEBUGFAIL("libjxl returned unexpected status"); - return kInternalError; - } - status = JxlDecoderSetInput(dec, fData->bytes(), fData->size()); - if (status != JXL_DEC_SUCCESS) { - // Fresh decoder instance (after rewind) must accept first data chunk. - SkDEBUGFAIL("libjxl returned unexpected status"); - return kInternalError; - } - SkASSERT(codec.fLastProcessedFrame + 1 == 0); - } - - int nextFrame = codec.fLastProcessedFrame + 1; - if (nextFrame < index) { - JxlDecoderSkipFrames(dec, index - nextFrame); - } - - // Decode till the frame start. - status = JxlDecoderProcessInput(dec); - // TODO(eustas): actually, frame is not completely processed; for streaming / partial decoding - // we should also add a flag that "last processed frame" is still incomplete, and - // flip that flag when frame decoding is over. - codec.fLastProcessedFrame = index; - if (status != JXL_DEC_FRAME) { - // TODO(eustas): check status: it might be either corrupted or incomplete input. - return kInternalError; - } - - codec.fDst = dst; - codec.fRowBytes = rowBytes; - - // TODO(eustas): consider grayscale. - uint32_t numColorChannels = 3; - // TODO(eustas): consider no-alpha. - uint32_t numAlphaChannels = 1; - // NB: SKIA works with little-endian F16s. - auto endianness = JXL_LITTLE_ENDIAN; - - // Internally JXL does most processing in floats. By "default" we request - // output data type to be U8; it takes less memory, but results in some precision loss. - // We request F16 in two cases: - // - destination type is F16 - // - color transformation is required; in this case values are remapped, - // and with 8-bit precision it is likely that visual artefact will appear - // (like banding, etc.) - bool halfFloatOutput = false; - if (fCodec->fDstColorType == kRGBA_F16_SkColorType) halfFloatOutput = true; - if (colorXform()) halfFloatOutput = true; - auto dataType = halfFloatOutput ? JXL_TYPE_FLOAT16 : JXL_TYPE_UINT8; - - JxlPixelFormat format = - {numColorChannels + numAlphaChannels, dataType, endianness, /* align = */ 0}; - status = JxlDecoderSetImageOutCallback(dec, &format, SkJpegxlCodec::imageOutCallback, this); - if (status != JXL_DEC_SUCCESS) { - // Current event is JXL_DEC_FRAME -> decoder must accept callback. - SkDEBUGFAIL("libjxl returned unexpected status"); - return kInternalError; - } - - // Decode till the frame start. - status = JxlDecoderProcessInput(dec); - if (status != JXL_DEC_FULL_IMAGE) { - // TODO(eustas): check status: it might be either corrupted or incomplete input. - return kInternalError; - } - // TODO(eustas): currently it is supposed that complete input is accessible; - // when streaming support is added JXL_DEC_NEED_MORE_INPUT would also - // become a legal outcome; amount of decoded scanlines should be calculated - // based on callback invocations / render-pipeline API. - *rowsDecodedPtr = dstInfo.height(); - - return kSuccess; -} - -bool SkJpegxlCodec::onRewind() { - JxlDecoderRewind(fCodec->fDecoder.get()); - return true; -} - -bool SkJpegxlCodec::conversionSupported(const SkImageInfo& dstInfo, bool srcIsOpaque, - bool needsColorXform) { - fCodec->fDstColorType = dstInfo.colorType(); - switch (dstInfo.colorType()) { - case kRGBA_8888_SkColorType: - return true; // memcpy - case kBGRA_8888_SkColorType: - return true; // rgba->bgra - - case kRGBA_F16_SkColorType: - SkASSERT(needsColorXform); // TODO(eustas): not necessary for JXL. - return true; // memcpy - - // TODO(eustas): implement - case kRGB_565_SkColorType: - return false; - case kGray_8_SkColorType: - return false; - case kAlpha_8_SkColorType: - return false; - - default: - return false; - } - return true; -} - -void SkJpegxlCodec::imageOutCallback(void* opaque, size_t x, size_t y, - size_t num_pixels, const void* pixels) { - SkJpegxlCodec* instance = reinterpret_cast(opaque); - auto& codec = *instance->fCodec.get(); - size_t offset = y * codec.fRowBytes + (x << codec.fPixelShift); - void* dst = SkTAddOffset(codec.fDst, offset); - if (instance->colorXform()) { - instance->applyColorXform(dst, pixels, num_pixels); - return; - } - switch (codec.fDstColorType) { - case kRGBA_8888_SkColorType: - memcpy(dst, pixels, 4 * num_pixels); - return; - case kBGRA_8888_SkColorType: - SkOpts::RGBA_to_bgrA((uint32_t*) dst, (const uint32_t*)(pixels), num_pixels); - return; - case kRGBA_F16_SkColorType: - memcpy(dst, pixels, 8 * num_pixels); - return; - default: - SK_ABORT("Selected output format is not supported yet"); - return; - } -} - -bool SkJpegxlCodec::scanFrames() { - auto decoder = JxlDecoderMake(/* memory_manager = */ nullptr); - JxlDecoder* dec = decoder.get(); - auto* frameHolder = fCodec.get(); - auto& frames = frameHolder->fFrames; - const auto& info = fCodec->fInfo; - frames.clear(); - - auto alpha = (info.alpha_bits != 0) ? SkEncodedInfo::Alpha::kUnpremul_Alpha - : SkEncodedInfo::Alpha::kOpaque_Alpha; - - auto status = JxlDecoderSubscribeEvents(dec, JXL_DEC_FRAME); - if (status != JXL_DEC_SUCCESS) { - // Fresh instance must accept request for subscription. - SkDEBUGFAIL("libjxl returned unexpected status"); - return true; - } - - status = JxlDecoderSetInput(dec, fData->bytes(), fData->size()); - if (status != JXL_DEC_SUCCESS) { - // Fresh instance must accept first input chunk. - SkDEBUGFAIL("libjxl returned unexpected status"); - return true; - } - - while (true) { - status = JxlDecoderProcessInput(dec); - switch (status) { - case JXL_DEC_FRAME: { - size_t frameId = frames.size(); - JxlFrameHeader frameHeader; - if (JxlDecoderGetFrameHeader(dec, &frameHeader) != JXL_DEC_SUCCESS) { - return true; - } - frames.emplace_back(static_cast(frameId), alpha); - auto& frame = frames.back(); - // TODO(eustas): for better consistency we need to track total duration and report - // frame duration as delta to previous frame. - int duration = (1000 * frameHeader.duration * info.animation.tps_denominator) / - info.animation.tps_numerator; - frame.setDuration(duration); - frameHolder->setAlphaAndRequiredFrame(&frame); - break; - } - case JXL_DEC_SUCCESS: { - return true; - } - default: { - return false; - } - } - } -} - -int SkJpegxlCodec::onGetFrameCount() { - if (!fCodec->fInfo.have_animation) { - return 1; - } - - if (!fCodec->fSeenAllFrames) { - fCodec->fSeenAllFrames = scanFrames(); - } - - return fCodec->fFrames.size(); -} - -bool SkJpegxlCodec::onGetFrameInfo(int index, FrameInfo* frameInfo) const { - if (index < 0) { - return false; - } - if (static_cast(index) >= fCodec->fFrames.size()) { - return false; - } - fCodec->fFrames[index].fillIn(frameInfo, true); - return true; -} - -int SkJpegxlCodec::onGetRepetitionCount() { - JxlBasicInfo& info = fCodec->fInfo; - if (!info.have_animation) { - return 0; - } - - if (info.animation.num_loops == 0) { - return kRepetitionCountInfinite; - } - - if (SkTFitsIn(info.animation.num_loops)) { - return info.animation.num_loops - 1; - } - - // Largest "non-infinite" value. - return std::numeric_limits::max(); -} - -const SkFrameHolder* SkJpegxlCodec::getFrameHolder() const { - return fCodec.get(); -} - -// TODO(eustas): implement -// SkCodec::Result SkJpegxlCodec::onStartScanlineDecode( -// const SkImageInfo& /*dstInfo*/, const Options& /*options*/) { return kUnimplemented; } - -// TODO(eustas): implement -// SkCodec::Result SkJpegxlCodec::onStartIncrementalDecode( -// const SkImageInfo& /*dstInfo*/, void*, size_t, const Options&) { return kUnimplemented; } - -// TODO(eustas): implement -// SkCodec::Result SkJpegxlCodec::onIncrementalDecode(int*) { return kUnimplemented; } - -// TODO(eustas): implement -// bool SkJpegxlCodec::onSkipScanlines(int /*countLines*/) { return false; } - -// TODO(eustas): implement -// int SkJpegxlCodec::onGetScanlines( -// void* /*dst*/, int /*countLines*/, size_t /*rowBytes*/) { return 0; } - -// TODO(eustas): implement -// SkSampler* SkJpegxlCodec::getSampler(bool /*createIfNecessary*/) { return nullptr; } - -namespace SkJpegxlDecoder { -bool IsJpegxl(const void* data, size_t len) { - return SkJpegxlCodec::IsJpegxl(data, len); -} - -std::unique_ptr Decode(std::unique_ptr stream, - SkCodec::Result* outResult, - SkCodecs::DecodeContext) { - SkCodec::Result resultStorage; - if (!outResult) { - outResult = &resultStorage; - } - return SkJpegxlCodec::MakeFromStream(std::move(stream), outResult); -} - -std::unique_ptr Decode(sk_sp data, - SkCodec::Result* outResult, - SkCodecs::DecodeContext) { - if (!data) { - if (outResult) { - *outResult = SkCodec::kInvalidInput; - } - return nullptr; - } - return Decode(SkMemoryStream::Make(std::move(data)), outResult, nullptr); -} -} // namespace SkJpegDecoder diff --git a/gfx/skia/skia/src/codec/SkJpegxlCodec.h b/gfx/skia/skia/src/codec/SkJpegxlCodec.h deleted file mode 100644 index c8c5a68ead19..000000000000 --- a/gfx/skia/skia/src/codec/SkJpegxlCodec.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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 SkJpegxlCodec_DEFINED -#define SkJpegxlCodec_DEFINED - -#include "include/codec/SkEncodedImageFormat.h" -#include "include/core/SkData.h" -#include "include/core/SkRefCnt.h" -#include "src/codec/SkScalingCodec.h" - -#include -#include - -class SkCodec; -class SkFrameHolder; -class SkJpegxlCodecPriv; -class SkStream; -struct SkEncodedInfo; -struct SkImageInfo; - -/* - * - * This class implements the decoding for jpegxl images - * - */ -class SkJpegxlCodec : public SkScalingCodec { -public: - static bool IsJpegxl(const void*, size_t); - - /* - * Assumes IsJpegxl was called and returned true - * Takes ownership of the stream - */ - static std::unique_ptr MakeFromStream(std::unique_ptr, Result*); - -protected: - /* TODO(eustas): implement when downscaling is supported. */ - /* SkISize onGetScaledDimensions(float desiredScale) const override; */ - - /* TODO(eustas): implement when up-/down-scaling is supported. */ - /* bool onDimensionsSupported(const SkISize&) override; */ - - SkEncodedImageFormat onGetEncodedFormat() const override { - return SkEncodedImageFormat::kJPEGXL; - } - - Result onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t rowBytes, - const Options& options, int* rowsDecodedPtr) override; - - /* TODO(eustas): add support for transcoded JPEG images? */ - /* bool onQueryYUVAInfo(const SkYUVAPixmapInfo::SupportedDataTypes&, - SkYUVAPixmapInfo*) const override; */ - - /* TODO(eustas): add support for transcoded JPEG images? */ - /* Result onGetYUVAPlanes(const SkYUVAPixmaps& yuvaPixmaps) override; */ - - /* TODO(eustas): implement when cropped output is supported. */ - /* bool onGetValidSubset(SkIRect* desiredSubset) const override; */ - - bool onRewind() override; - - /* TODO(eustas): top-down by default; do we need something else? */ - /* SkScanlineOrder onGetScanlineOrder() const override; */ - /* int onOutputScanline(int inputScanline) const override; */ - - bool conversionSupported(const SkImageInfo&, bool, bool) override; - - int onGetFrameCount() override; - - bool onGetFrameInfo(int, FrameInfo*) const override; - - int onGetRepetitionCount() override; - -private: - const SkFrameHolder* getFrameHolder() const override; - - // Result onStartScanlineDecode( - // const SkImageInfo& /*dstInfo*/, const Options& /*options*/) override; - // Result onStartIncrementalDecode( - // const SkImageInfo& /*dstInfo*/, void*, size_t, const Options&) override; - // Result onIncrementalDecode(int*) override; - // bool onSkipScanlines(int /*countLines*/) override; - // int onGetScanlines(void* /*dst*/, int /*countLines*/, size_t /*rowBytes*/) override; - // SkSampler* getSampler(bool /*createIfNecessary*/) override; - - // Opaque codec implementation for lightweight header file. - std::unique_ptr fCodec; - sk_sp fData; - - bool scanFrames(); - static void imageOutCallback( - void* opaque, size_t x, size_t y, size_t num_pixels, const void* pixels); - - SkJpegxlCodec(std::unique_ptr codec, - SkEncodedInfo&& info, - std::unique_ptr stream, - sk_sp data); - - using INHERITED = SkScalingCodec; -}; - -#endif diff --git a/gfx/skia/skia/src/codec/SkMaskSwizzler.cpp b/gfx/skia/skia/src/codec/SkMaskSwizzler.cpp deleted file mode 100644 index deb9c16a7f6c..000000000000 --- a/gfx/skia/skia/src/codec/SkMaskSwizzler.cpp +++ /dev/null @@ -1,575 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "src/codec/SkMaskSwizzler.h" - -#include "include/core/SkAlphaType.h" -#include "include/core/SkColor.h" -#include "include/core/SkColorType.h" -#include "include/core/SkImageInfo.h" -#include "include/core/SkRect.h" -#include "include/private/SkColorData.h" -#include "src/codec/SkCodecPriv.h" -#include "src/core/SkMasks.h" - -static void swizzle_mask16_to_rgba_opaque( - void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, - uint32_t startX, uint32_t sampleX) { - - // Use the masks to decode to the destination - const uint16_t* srcPtr = ((const uint16_t*) srcRow) + startX; - SkPMColor* dstPtr = (SkPMColor*) dstRow; - for (int i = 0; i < width; i++) { - uint16_t p = srcPtr[0]; - uint8_t red = masks->getRed(p); - uint8_t green = masks->getGreen(p); - uint8_t blue = masks->getBlue(p); - dstPtr[i] = SkPackARGB_as_RGBA(0xFF, red, green, blue); - srcPtr += sampleX; - } -} - -static void swizzle_mask16_to_bgra_opaque( - void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, - uint32_t startX, uint32_t sampleX) { - - // Use the masks to decode to the destination - const uint16_t* srcPtr = ((const uint16_t*) srcRow) + startX; - SkPMColor* dstPtr = (SkPMColor*) dstRow; - for (int i = 0; i < width; i++) { - uint16_t p = srcPtr[0]; - uint8_t red = masks->getRed(p); - uint8_t green = masks->getGreen(p); - uint8_t blue = masks->getBlue(p); - dstPtr[i] = SkPackARGB_as_BGRA(0xFF, red, green, blue); - srcPtr += sampleX; - } -} - -static void swizzle_mask16_to_rgba_unpremul( - void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, - uint32_t startX, uint32_t sampleX) { - - // Use the masks to decode to the destination - const uint16_t* srcPtr = ((const uint16_t*) srcRow) + startX; - SkPMColor* dstPtr = (SkPMColor*) dstRow; - for (int i = 0; i < width; i++) { - uint16_t p = srcPtr[0]; - uint8_t red = masks->getRed(p); - uint8_t green = masks->getGreen(p); - uint8_t blue = masks->getBlue(p); - uint8_t alpha = masks->getAlpha(p); - dstPtr[i] = SkPackARGB_as_RGBA(alpha, red, green, blue); - srcPtr += sampleX; - } -} - -static void swizzle_mask16_to_bgra_unpremul( - void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, - uint32_t startX, uint32_t sampleX) { - - // Use the masks to decode to the destination - const uint16_t* srcPtr = ((const uint16_t*) srcRow) + startX; - SkPMColor* dstPtr = (SkPMColor*) dstRow; - for (int i = 0; i < width; i++) { - uint16_t p = srcPtr[0]; - uint8_t red = masks->getRed(p); - uint8_t green = masks->getGreen(p); - uint8_t blue = masks->getBlue(p); - uint8_t alpha = masks->getAlpha(p); - dstPtr[i] = SkPackARGB_as_BGRA(alpha, red, green, blue); - srcPtr += sampleX; - } -} - -static void swizzle_mask16_to_rgba_premul( - void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, - uint32_t startX, uint32_t sampleX) { - - // Use the masks to decode to the destination - const uint16_t* srcPtr = ((const uint16_t*) srcRow) + startX; - SkPMColor* dstPtr = (SkPMColor*) dstRow; - for (int i = 0; i < width; i++) { - uint16_t p = srcPtr[0]; - uint8_t red = masks->getRed(p); - uint8_t green = masks->getGreen(p); - uint8_t blue = masks->getBlue(p); - uint8_t alpha = masks->getAlpha(p); - dstPtr[i] = premultiply_argb_as_rgba(alpha, red, green, blue); - srcPtr += sampleX; - } -} - -static void swizzle_mask16_to_bgra_premul( - void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, - uint32_t startX, uint32_t sampleX) { - - // Use the masks to decode to the destination - const uint16_t* srcPtr = ((const uint16_t*) srcRow) + startX; - SkPMColor* dstPtr = (SkPMColor*) dstRow; - for (int i = 0; i < width; i++) { - uint16_t p = srcPtr[0]; - uint8_t red = masks->getRed(p); - uint8_t green = masks->getGreen(p); - uint8_t blue = masks->getBlue(p); - uint8_t alpha = masks->getAlpha(p); - dstPtr[i] = premultiply_argb_as_bgra(alpha, red, green, blue); - srcPtr += sampleX; - } -} - -// TODO (msarett): We have promoted a two byte per pixel image to 8888, only to -// convert it back to 565. Instead, we should swizzle to 565 directly. -static void swizzle_mask16_to_565( - void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, - uint32_t startX, uint32_t sampleX) { - - // Use the masks to decode to the destination - const uint16_t* srcPtr = ((const uint16_t*) srcRow) + startX; - uint16_t* dstPtr = (uint16_t*) dstRow; - for (int i = 0; i < width; i++) { - uint16_t p = srcPtr[0]; - uint8_t red = masks->getRed(p); - uint8_t green = masks->getGreen(p); - uint8_t blue = masks->getBlue(p); - dstPtr[i] = SkPack888ToRGB16(red, green, blue); - srcPtr += sampleX; - } -} - -static void swizzle_mask24_to_rgba_opaque( - void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, - uint32_t startX, uint32_t sampleX) { - - // Use the masks to decode to the destination - srcRow += 3 * startX; - SkPMColor* dstPtr = (SkPMColor*) dstRow; - for (int i = 0; i < width; i++) { - uint32_t p = srcRow[0] | (srcRow[1] << 8) | srcRow[2] << 16; - uint8_t red = masks->getRed(p); - uint8_t green = masks->getGreen(p); - uint8_t blue = masks->getBlue(p); - dstPtr[i] = SkPackARGB_as_RGBA(0xFF, red, green, blue); - srcRow += 3 * sampleX; - } -} - -static void swizzle_mask24_to_bgra_opaque( - void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, - uint32_t startX, uint32_t sampleX) { - - // Use the masks to decode to the destination - srcRow += 3 * startX; - SkPMColor* dstPtr = (SkPMColor*) dstRow; - for (int i = 0; i < width; i++) { - uint32_t p = srcRow[0] | (srcRow[1] << 8) | srcRow[2] << 16; - uint8_t red = masks->getRed(p); - uint8_t green = masks->getGreen(p); - uint8_t blue = masks->getBlue(p); - dstPtr[i] = SkPackARGB_as_BGRA(0xFF, red, green, blue); - srcRow += 3 * sampleX; - } -} - -static void swizzle_mask24_to_rgba_unpremul( - void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, - uint32_t startX, uint32_t sampleX) { - - // Use the masks to decode to the destination - srcRow += 3 * startX; - SkPMColor* dstPtr = (SkPMColor*) dstRow; - for (int i = 0; i < width; i++) { - uint32_t p = srcRow[0] | (srcRow[1] << 8) | srcRow[2] << 16; - uint8_t red = masks->getRed(p); - uint8_t green = masks->getGreen(p); - uint8_t blue = masks->getBlue(p); - uint8_t alpha = masks->getAlpha(p); - dstPtr[i] = SkPackARGB_as_RGBA(alpha, red, green, blue); - srcRow += 3 * sampleX; - } -} - -static void swizzle_mask24_to_bgra_unpremul( - void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, - uint32_t startX, uint32_t sampleX) { - - // Use the masks to decode to the destination - srcRow += 3 * startX; - SkPMColor* dstPtr = (SkPMColor*) dstRow; - for (int i = 0; i < width; i++) { - uint32_t p = srcRow[0] | (srcRow[1] << 8) | srcRow[2] << 16; - uint8_t red = masks->getRed(p); - uint8_t green = masks->getGreen(p); - uint8_t blue = masks->getBlue(p); - uint8_t alpha = masks->getAlpha(p); - dstPtr[i] = SkPackARGB_as_BGRA(alpha, red, green, blue); - srcRow += 3 * sampleX; - } -} - -static void swizzle_mask24_to_rgba_premul( - void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, - uint32_t startX, uint32_t sampleX) { - - // Use the masks to decode to the destination - srcRow += 3 * startX; - SkPMColor* dstPtr = (SkPMColor*) dstRow; - for (int i = 0; i < width; i++) { - uint32_t p = srcRow[0] | (srcRow[1] << 8) | srcRow[2] << 16; - uint8_t red = masks->getRed(p); - uint8_t green = masks->getGreen(p); - uint8_t blue = masks->getBlue(p); - uint8_t alpha = masks->getAlpha(p); - dstPtr[i] = premultiply_argb_as_rgba(alpha, red, green, blue); - srcRow += 3 * sampleX; - } -} - -static void swizzle_mask24_to_bgra_premul( - void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, - uint32_t startX, uint32_t sampleX) { - - // Use the masks to decode to the destination - srcRow += 3 * startX; - SkPMColor* dstPtr = (SkPMColor*) dstRow; - for (int i = 0; i < width; i++) { - uint32_t p = srcRow[0] | (srcRow[1] << 8) | srcRow[2] << 16; - uint8_t red = masks->getRed(p); - uint8_t green = masks->getGreen(p); - uint8_t blue = masks->getBlue(p); - uint8_t alpha = masks->getAlpha(p); - dstPtr[i] = premultiply_argb_as_bgra(alpha, red, green, blue); - srcRow += 3 * sampleX; - } -} - -static void swizzle_mask24_to_565( - void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, - uint32_t startX, uint32_t sampleX) { - - // Use the masks to decode to the destination - srcRow += 3 * startX; - uint16_t* dstPtr = (uint16_t*) dstRow; - for (int i = 0; i < width; i++) { - uint32_t p = srcRow[0] | (srcRow[1] << 8) | srcRow[2] << 16; - uint8_t red = masks->getRed(p); - uint8_t green = masks->getGreen(p); - uint8_t blue = masks->getBlue(p); - dstPtr[i] = SkPack888ToRGB16(red, green, blue); - srcRow += 3 * sampleX; - } -} - -static void swizzle_mask32_to_rgba_opaque( - void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, - uint32_t startX, uint32_t sampleX) { - - // Use the masks to decode to the destination - const uint32_t* srcPtr = ((const uint32_t*) srcRow) + startX; - SkPMColor* dstPtr = (SkPMColor*) dstRow; - for (int i = 0; i < width; i++) { - uint32_t p = srcPtr[0]; - uint8_t red = masks->getRed(p); - uint8_t green = masks->getGreen(p); - uint8_t blue = masks->getBlue(p); - dstPtr[i] = SkPackARGB_as_RGBA(0xFF, red, green, blue); - srcPtr += sampleX; - } -} - -static void swizzle_mask32_to_bgra_opaque( - void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, - uint32_t startX, uint32_t sampleX) { - - // Use the masks to decode to the destination - const uint32_t* srcPtr = ((const uint32_t*) srcRow) + startX; - SkPMColor* dstPtr = (SkPMColor*) dstRow; - for (int i = 0; i < width; i++) { - uint32_t p = srcPtr[0]; - uint8_t red = masks->getRed(p); - uint8_t green = masks->getGreen(p); - uint8_t blue = masks->getBlue(p); - dstPtr[i] = SkPackARGB_as_BGRA(0xFF, red, green, blue); - srcPtr += sampleX; - } -} - -static void swizzle_mask32_to_rgba_unpremul( - void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, - uint32_t startX, uint32_t sampleX) { - - // Use the masks to decode to the destination - const uint32_t* srcPtr = ((const uint32_t*) srcRow) + startX; - SkPMColor* dstPtr = (SkPMColor*) dstRow; - for (int i = 0; i < width; i++) { - uint32_t p = srcPtr[0]; - uint8_t red = masks->getRed(p); - uint8_t green = masks->getGreen(p); - uint8_t blue = masks->getBlue(p); - uint8_t alpha = masks->getAlpha(p); - dstPtr[i] = SkPackARGB_as_RGBA(alpha, red, green, blue); - srcPtr += sampleX; - } -} - -static void swizzle_mask32_to_bgra_unpremul( - void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, - uint32_t startX, uint32_t sampleX) { - - // Use the masks to decode to the destination - const uint32_t* srcPtr = ((const uint32_t*) srcRow) + startX; - SkPMColor* dstPtr = (SkPMColor*) dstRow; - for (int i = 0; i < width; i++) { - uint32_t p = srcPtr[0]; - uint8_t red = masks->getRed(p); - uint8_t green = masks->getGreen(p); - uint8_t blue = masks->getBlue(p); - uint8_t alpha = masks->getAlpha(p); - dstPtr[i] = SkPackARGB_as_BGRA(alpha, red, green, blue); - srcPtr += sampleX; - } -} - -static void swizzle_mask32_to_rgba_premul( - void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, - uint32_t startX, uint32_t sampleX) { - - // Use the masks to decode to the destination - const uint32_t* srcPtr = ((const uint32_t*) srcRow) + startX; - SkPMColor* dstPtr = (SkPMColor*) dstRow; - for (int i = 0; i < width; i++) { - uint32_t p = srcPtr[0]; - uint8_t red = masks->getRed(p); - uint8_t green = masks->getGreen(p); - uint8_t blue = masks->getBlue(p); - uint8_t alpha = masks->getAlpha(p); - dstPtr[i] = premultiply_argb_as_rgba(alpha, red, green, blue); - srcPtr += sampleX; - } -} - -static void swizzle_mask32_to_bgra_premul( - void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, - uint32_t startX, uint32_t sampleX) { - - // Use the masks to decode to the destination - const uint32_t* srcPtr = ((const uint32_t*) srcRow) + startX; - SkPMColor* dstPtr = (SkPMColor*) dstRow; - for (int i = 0; i < width; i++) { - uint32_t p = srcPtr[0]; - uint8_t red = masks->getRed(p); - uint8_t green = masks->getGreen(p); - uint8_t blue = masks->getBlue(p); - uint8_t alpha = masks->getAlpha(p); - dstPtr[i] = premultiply_argb_as_bgra(alpha, red, green, blue); - srcPtr += sampleX; - } -} - -static void swizzle_mask32_to_565( - void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks, - uint32_t startX, uint32_t sampleX) { - // Use the masks to decode to the destination - const uint32_t* srcPtr = ((const uint32_t*) srcRow) + startX; - uint16_t* dstPtr = (uint16_t*) dstRow; - for (int i = 0; i < width; i++) { - uint32_t p = srcPtr[0]; - uint8_t red = masks->getRed(p); - uint8_t green = masks->getGreen(p); - uint8_t blue = masks->getBlue(p); - dstPtr[i] = SkPack888ToRGB16(red, green, blue); - srcPtr += sampleX; - } -} - -/* - * - * Create a new mask swizzler - * - */ -SkMaskSwizzler* SkMaskSwizzler::CreateMaskSwizzler(const SkImageInfo& dstInfo, - bool srcIsOpaque, SkMasks* masks, uint32_t bitsPerPixel, - const SkCodec::Options& options) { - - // Choose the appropriate row procedure - RowProc proc = nullptr; - switch (bitsPerPixel) { - case 16: - switch (dstInfo.colorType()) { - case kRGBA_8888_SkColorType: - if (srcIsOpaque) { - proc = &swizzle_mask16_to_rgba_opaque; - } else { - switch (dstInfo.alphaType()) { - case kUnpremul_SkAlphaType: - proc = &swizzle_mask16_to_rgba_unpremul; - break; - case kPremul_SkAlphaType: - proc = &swizzle_mask16_to_rgba_premul; - break; - default: - break; - } - } - break; - case kBGRA_8888_SkColorType: - if (srcIsOpaque) { - proc = &swizzle_mask16_to_bgra_opaque; - } else { - switch (dstInfo.alphaType()) { - case kUnpremul_SkAlphaType: - proc = &swizzle_mask16_to_bgra_unpremul; - break; - case kPremul_SkAlphaType: - proc = &swizzle_mask16_to_bgra_premul; - break; - default: - break; - } - } - break; - case kRGB_565_SkColorType: - proc = &swizzle_mask16_to_565; - break; - default: - break; - } - break; - case 24: - switch (dstInfo.colorType()) { - case kRGBA_8888_SkColorType: - if (srcIsOpaque) { - proc = &swizzle_mask24_to_rgba_opaque; - } else { - switch (dstInfo.alphaType()) { - case kUnpremul_SkAlphaType: - proc = &swizzle_mask24_to_rgba_unpremul; - break; - case kPremul_SkAlphaType: - proc = &swizzle_mask24_to_rgba_premul; - break; - default: - break; - } - } - break; - case kBGRA_8888_SkColorType: - if (srcIsOpaque) { - proc = &swizzle_mask24_to_bgra_opaque; - } else { - switch (dstInfo.alphaType()) { - case kUnpremul_SkAlphaType: - proc = &swizzle_mask24_to_bgra_unpremul; - break; - case kPremul_SkAlphaType: - proc = &swizzle_mask24_to_bgra_premul; - break; - default: - break; - } - } - break; - case kRGB_565_SkColorType: - proc = &swizzle_mask24_to_565; - break; - default: - break; - } - break; - case 32: - switch (dstInfo.colorType()) { - case kRGBA_8888_SkColorType: - if (srcIsOpaque) { - proc = &swizzle_mask32_to_rgba_opaque; - } else { - switch (dstInfo.alphaType()) { - case kUnpremul_SkAlphaType: - proc = &swizzle_mask32_to_rgba_unpremul; - break; - case kPremul_SkAlphaType: - proc = &swizzle_mask32_to_rgba_premul; - break; - default: - break; - } - } - break; - case kBGRA_8888_SkColorType: - if (srcIsOpaque) { - proc = &swizzle_mask32_to_bgra_opaque; - } else { - switch (dstInfo.alphaType()) { - case kUnpremul_SkAlphaType: - proc = &swizzle_mask32_to_bgra_unpremul; - break; - case kPremul_SkAlphaType: - proc = &swizzle_mask32_to_bgra_premul; - break; - default: - break; - } - } - break; - case kRGB_565_SkColorType: - proc = &swizzle_mask32_to_565; - break; - default: - break; - } - break; - default: - SkASSERT(false); - return nullptr; - } - - int srcOffset = 0; - int srcWidth = dstInfo.width(); - if (options.fSubset) { - srcOffset = options.fSubset->left(); - srcWidth = options.fSubset->width(); - } - - return new SkMaskSwizzler(masks, proc, srcOffset, srcWidth); -} - -/* - * - * Constructor for mask swizzler - * - */ -SkMaskSwizzler::SkMaskSwizzler(SkMasks* masks, RowProc proc, int srcOffset, int subsetWidth) - : fMasks(masks) - , fRowProc(proc) - , fSubsetWidth(subsetWidth) - , fDstWidth(subsetWidth) - , fSampleX(1) - , fSrcOffset(srcOffset) - , fX0(srcOffset) -{} - -int SkMaskSwizzler::onSetSampleX(int sampleX) { - // FIXME: Share this function with SkSwizzler? - SkASSERT(sampleX > 0); // Surely there is an upper limit? Should there be - // way to report failure? - fSampleX = sampleX; - fX0 = get_start_coord(sampleX) + fSrcOffset; - fDstWidth = get_scaled_dimension(fSubsetWidth, sampleX); - - // check that fX0 is valid - SkASSERT(fX0 >= 0); - return fDstWidth; -} - -/* - * - * Swizzle the specified row - * - */ -void SkMaskSwizzler::swizzle(void* dst, const uint8_t* SK_RESTRICT src) { - SkASSERT(nullptr != dst && nullptr != src); - fRowProc(dst, src, fDstWidth, fMasks, fX0, fSampleX); -} diff --git a/gfx/skia/skia/src/codec/SkMaskSwizzler.h b/gfx/skia/skia/src/codec/SkMaskSwizzler.h deleted file mode 100644 index 4cac41905cae..000000000000 --- a/gfx/skia/skia/src/codec/SkMaskSwizzler.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#ifndef SkMaskSwizzler_DEFINED -#define SkMaskSwizzler_DEFINED - -#include "include/codec/SkCodec.h" -#include "include/core/SkTypes.h" -#include "src/codec/SkSampler.h" - -#include - -class SkMasks; -struct SkImageInfo; - -/* - * - * Used to swizzle images whose pixel components are extracted by bit masks - * Currently only used by bmp - * - */ -class SkMaskSwizzler : public SkSampler { -public: - - /* - * @param masks Unowned pointer to helper class - */ - static SkMaskSwizzler* CreateMaskSwizzler(const SkImageInfo& dstInfo, - bool srcIsOpaque, - SkMasks* masks, - uint32_t bitsPerPixel, - const SkCodec::Options& options); - - /* - * Swizzle a row - */ - void swizzle(void* dst, const uint8_t* SK_RESTRICT src); - - int fillWidth() const override { - return fDstWidth; - } - - /** - * Returns the byte offset at which we write to destination memory, taking - * scaling, subsetting, and partial frames into account. - * A similar function exists on SkSwizzler. - */ - int swizzleWidth() const { return fDstWidth; } - -private: - - /* - * Row procedure used for swizzle - */ - typedef void (*RowProc)(void* dstRow, const uint8_t* srcRow, int width, - SkMasks* masks, uint32_t startX, uint32_t sampleX); - - SkMaskSwizzler(SkMasks* masks, RowProc proc, int subsetWidth, int srcOffset); - - int onSetSampleX(int) override; - - SkMasks* fMasks; // unowned - const RowProc fRowProc; - - // FIXME: Can this class share more with SkSwizzler? These variables are all the same. - const int fSubsetWidth; // Width of the subset of source before any sampling. - int fDstWidth; // Width of dst, which may differ with sampling. - int fSampleX; - int fSrcOffset; - int fX0; -}; - -#endif diff --git a/gfx/skia/skia/src/codec/SkParseEncodedOrigin.cpp b/gfx/skia/skia/src/codec/SkParseEncodedOrigin.cpp deleted file mode 100644 index 366a3266bb24..000000000000 --- a/gfx/skia/skia/src/codec/SkParseEncodedOrigin.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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/codec/SkParseEncodedOrigin.h" - -#include "include/core/SkData.h" -#include "include/core/SkRefCnt.h" -#include "include/private/SkExif.h" -#include "include/private/base/SkAssert.h" - -#include - -bool SkParseEncodedOrigin(const void* data, size_t data_length, SkEncodedOrigin* orientation) { - SkASSERT(orientation); - SkExif::Metadata exif; - SkExif::Parse(exif, SkData::MakeWithoutCopy(data, data_length).get()); - if (exif.fOrigin.has_value()) { - *orientation = exif.fOrigin.value(); - return true; - } - return false; -} diff --git a/gfx/skia/skia/src/codec/SkParseEncodedOrigin.h b/gfx/skia/skia/src/codec/SkParseEncodedOrigin.h deleted file mode 100644 index 2786b668e89c..000000000000 --- a/gfx/skia/skia/src/codec/SkParseEncodedOrigin.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkParseEncodedOrigin_DEFINED -#define SkParseEncodedOrigin_DEFINED - -#include -#include "include/codec/SkEncodedOrigin.h" - -/** - * If |data| is an EXIF tag representing an SkEncodedOrigin, return true and set |out| - * appropriately. Otherwise return false. - */ -bool SkParseEncodedOrigin(const void* data, size_t data_length, SkEncodedOrigin* out); - -#endif // SkParseEncodedOrigin_DEFINED diff --git a/gfx/skia/skia/src/codec/SkPngCodec.cpp b/gfx/skia/skia/src/codec/SkPngCodec.cpp deleted file mode 100644 index 98b69f04283a..000000000000 --- a/gfx/skia/skia/src/codec/SkPngCodec.cpp +++ /dev/null @@ -1,1039 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "src/codec/SkPngCodec.h" - -#include "include/codec/SkPngChunkReader.h" -#include "include/codec/SkPngDecoder.h" -#include "include/core/SkData.h" -#include "include/core/SkImageInfo.h" -#include "include/core/SkRect.h" -#include "include/core/SkSize.h" -#include "include/core/SkSpan.h" -#include "include/core/SkStream.h" -#include "include/core/SkTypes.h" -#include "include/private/SkEncodedInfo.h" -#include "include/private/base/SkNoncopyable.h" -#include "include/private/base/SkTemplates.h" -#include "modules/skcms/skcms.h" -#include "src/codec/SkCodecPriv.h" -#include "src/codec/SkPngPriv.h" -#include "src/codec/SkSwizzler.h" - -#include -#include -#include -#include - -#include -#include - -using namespace skia_private; - -#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK - #include "include/android/SkAndroidFrameworkUtils.h" -#endif - -// This warning triggers false positives way too often in here. -#if defined(__GNUC__) && !defined(__clang__) - #pragma GCC diagnostic ignored "-Wclobbered" -#endif - -// FIXME (scroggo): We can use png_jumpbuf directly once Google3 is on 1.6 -#define PNG_JMPBUF(x) png_jmpbuf((png_structp) x) - -/////////////////////////////////////////////////////////////////////////////// -// Callback functions -/////////////////////////////////////////////////////////////////////////////// - -// When setjmp is first called, it returns 0, meaning longjmp was not called. -constexpr int kSetJmpOkay = 0; -// An error internal to libpng. -constexpr int kPngError = 1; -// Passed to longjmp when we have decoded as many lines as we need. -constexpr int kStopDecoding = 2; - -static void sk_error_fn(png_structp png_ptr, png_const_charp msg) { - SkCodecPrintf("------ png error %s\n", msg); - longjmp(PNG_JMPBUF(png_ptr), kPngError); -} - -void sk_warning_fn(png_structp, png_const_charp msg) { - SkCodecPrintf("----- png warning %s\n", msg); -} - -#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED -static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) { - SkPngChunkReader* chunkReader = (SkPngChunkReader*)png_get_user_chunk_ptr(png_ptr); - // readChunk() returning true means continue decoding - return chunkReader->readChunk((const char*)chunk->name, chunk->data, chunk->size) ? 1 : -1; -} -#endif - -/////////////////////////////////////////////////////////////////////////////// -// Helpers -/////////////////////////////////////////////////////////////////////////////// - -class AutoCleanPng : public SkNoncopyable { -public: - /* - * This class does not take ownership of stream or reader, but if codecPtr - * is non-NULL, and decodeBounds succeeds, it will have created a new - * SkCodec (pointed to by *codecPtr) which will own/ref them, as well as - * the png_ptr and info_ptr. - */ - AutoCleanPng(png_structp png_ptr, SkStream* stream, SkPngChunkReader* reader, - SkCodec** codecPtr) - : fPng_ptr(png_ptr) - , fInfo_ptr(nullptr) - , fStream(stream) - , fChunkReader(reader) - , fOutCodec(codecPtr) - {} - - ~AutoCleanPng() { - // fInfo_ptr will never be non-nullptr unless fPng_ptr is. - if (fPng_ptr) { - png_infopp info_pp = fInfo_ptr ? &fInfo_ptr : nullptr; - png_destroy_read_struct(&fPng_ptr, info_pp, nullptr); - } - } - - void setInfoPtr(png_infop info_ptr) { - SkASSERT(nullptr == fInfo_ptr); - fInfo_ptr = info_ptr; - } - - /** - * Reads enough of the input stream to decode the bounds. - * @return false if the stream is not a valid PNG (or too short). - * true if it read enough of the stream to determine the bounds. - * In the latter case, the stream may have been read beyond the - * point to determine the bounds, and the png_ptr will have saved - * any extra data. Further, if the codecPtr supplied to the - * constructor was not NULL, it will now point to a new SkCodec, - * which owns (or refs, in the case of the SkPngChunkReader) the - * inputs. If codecPtr was NULL, the png_ptr and info_ptr are - * unowned, and it is up to the caller to destroy them. - */ - bool decodeBounds(); - -private: - png_structp fPng_ptr; - png_infop fInfo_ptr; - SkStream* fStream; - SkPngChunkReader* fChunkReader; - SkCodec** fOutCodec; - - void infoCallback(size_t idatLength); - - void releasePngPtrs() { - fPng_ptr = nullptr; - fInfo_ptr = nullptr; - } -}; - -static inline bool is_chunk(const png_byte* chunk, const char* tag) { - return memcmp(chunk + 4, tag, 4) == 0; -} - -static inline bool process_data(png_structp png_ptr, png_infop info_ptr, - SkStream* stream, void* buffer, size_t bufferSize, size_t length) { - while (length > 0) { - const size_t bytesToProcess = std::min(bufferSize, length); - const size_t bytesRead = stream->read(buffer, bytesToProcess); - png_process_data(png_ptr, info_ptr, (png_bytep) buffer, bytesRead); - if (bytesRead < bytesToProcess) { - return false; - } - length -= bytesToProcess; - } - return true; -} - -bool AutoCleanPng::decodeBounds() { - SkASSERT(fStream); - if (setjmp(PNG_JMPBUF(fPng_ptr))) { - return false; - } - - png_set_progressive_read_fn(fPng_ptr, nullptr, nullptr, nullptr, nullptr); - - // Arbitrary buffer size, though note that it matches (below) - // SkPngCodec::processData(). FIXME: Can we better suit this to the size of - // the PNG header? - constexpr size_t kBufferSize = 4096; - char buffer[kBufferSize]; - - { - // Parse the signature. - if (fStream->read(buffer, 8) < 8) { - return false; - } - - png_process_data(fPng_ptr, fInfo_ptr, (png_bytep) buffer, 8); - } - - while (true) { - // Parse chunk length and type. - if (fStream->read(buffer, 8) < 8) { - // We have read to the end of the input without decoding bounds. - break; - } - - png_byte* chunk = reinterpret_cast(buffer); - const size_t length = png_get_uint_32(chunk); - - if (is_chunk(chunk, "IDAT")) { - this->infoCallback(length); - return true; - } - - png_process_data(fPng_ptr, fInfo_ptr, chunk, 8); - // Process the full chunk + CRC. - if (!process_data(fPng_ptr, fInfo_ptr, fStream, buffer, kBufferSize, length + 4)) { - return false; - } - } - - return false; -} - -bool SkPngCodec::processData() { - switch (setjmp(PNG_JMPBUF(fPng_ptr))) { - case kPngError: - // There was an error. Stop processing data. - // FIXME: Do we need to discard png_ptr? - return false; - case kStopDecoding: - // We decoded all the lines we want. - return true; - case kSetJmpOkay: - // Everything is okay. - break; - default: - // No other values should be passed to longjmp. - SkASSERT(false); - } - - // Arbitrary buffer size - constexpr size_t kBufferSize = 4096; - char buffer[kBufferSize]; - - bool iend = false; - while (true) { - size_t length; - if (fDecodedIdat) { - // Parse chunk length and type. - if (this->stream()->read(buffer, 8) < 8) { - break; - } - - png_byte* chunk = reinterpret_cast(buffer); - png_process_data(fPng_ptr, fInfo_ptr, chunk, 8); - if (is_chunk(chunk, "IEND")) { - iend = true; - } - - length = png_get_uint_32(chunk); - } else { - length = fIdatLength; - png_byte idat[] = {0, 0, 0, 0, 'I', 'D', 'A', 'T'}; - png_save_uint_32(idat, length); - png_process_data(fPng_ptr, fInfo_ptr, idat, 8); - fDecodedIdat = true; - } - - // Process the full chunk + CRC. - if (!process_data(fPng_ptr, fInfo_ptr, this->stream(), buffer, kBufferSize, length + 4) - || iend) { - break; - } - } - - return true; -} - -std::optional> SkPngCodec::onTryGetPlteChunk() { - int numColors; - png_color* palette; - if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numColors)) { - return std::nullopt; - } - - static_assert(sizeof(png_color) == sizeof(PaletteColorEntry)); - return SkSpan(reinterpret_cast(palette), numColors); -} - -std::optional> SkPngCodec::onTryGetTrnsChunk() { - png_bytep alphas; - int numColorsWithAlpha = 0; - if (!png_get_tRNS(fPng_ptr, fInfo_ptr, &alphas, &numColorsWithAlpha, nullptr)) { - return std::nullopt; - } - return SkSpan(alphas, numColorsWithAlpha); -} - -/////////////////////////////////////////////////////////////////////////////// -// Creation -/////////////////////////////////////////////////////////////////////////////// - -bool SkPngCodec::IsPng(const void* buf, size_t bytesRead) { - return !png_sig_cmp((png_const_bytep) buf, (png_size_t)0, bytesRead); -} - -#if (PNG_LIBPNG_VER_MAJOR > 1) || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 6) - -static float png_fixed_point_to_float(png_fixed_point x) { - // We multiply by the same factor that libpng used to convert - // fixed point -> double. Since we want floats, we choose to - // do the conversion ourselves rather than convert - // fixed point -> double -> float. - return ((float) x) * 0.00001f; -} - -static float png_inverted_fixed_point_to_float(png_fixed_point x) { - // This is necessary because the gAMA chunk actually stores 1/gamma. - return 1.0f / png_fixed_point_to_float(x); -} - -#endif // LIBPNG >= 1.6 - -// If there is no color profile information, it will use sRGB. -std::unique_ptr read_color_profile(png_structp png_ptr, - png_infop info_ptr) { - -#if (PNG_LIBPNG_VER_MAJOR > 1) || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 6) - // First check for an ICC profile - png_bytep profile; - png_uint_32 length; - // The below variables are unused, however, we need to pass them in anyway or - // png_get_iCCP() will return nothing. - // Could knowing the |name| of the profile ever be interesting? Maybe for debugging? - png_charp name; - // The |compression| is uninteresting since: - // (1) libpng has already decompressed the profile for us. - // (2) "deflate" is the only mode of decompression that libpng supports. - int compression; - if (PNG_INFO_iCCP == png_get_iCCP(png_ptr, info_ptr, &name, &compression, &profile, - &length)) { - auto data = SkData::MakeWithCopy(profile, length); - return SkEncodedInfo::ICCProfile::Make(std::move(data)); - } - - // Second, check for sRGB. - // Note that Blink does this first. This code checks ICC first, with the thinking that - // an image has both truly wants the potentially more specific ICC chunk, with sRGB as a - // backup in case the decoder does not support full color management. - if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) { - // TODO(https://crbug.com/362304558): Consider the intent field from the - // `sRGB` chunk. - return nullptr; - } - - // Default to SRGB gamut. - skcms_Matrix3x3 toXYZD50 = skcms_sRGB_profile()->toXYZD50; - // Next, check for chromaticities. - png_fixed_point chrm[8]; - png_fixed_point gamma; - if (png_get_cHRM_fixed(png_ptr, info_ptr, &chrm[0], &chrm[1], &chrm[2], &chrm[3], &chrm[4], - &chrm[5], &chrm[6], &chrm[7])) - { - float rx = png_fixed_point_to_float(chrm[2]); - float ry = png_fixed_point_to_float(chrm[3]); - float gx = png_fixed_point_to_float(chrm[4]); - float gy = png_fixed_point_to_float(chrm[5]); - float bx = png_fixed_point_to_float(chrm[6]); - float by = png_fixed_point_to_float(chrm[7]); - float wx = png_fixed_point_to_float(chrm[0]); - float wy = png_fixed_point_to_float(chrm[1]); - - skcms_Matrix3x3 tmp; - if (skcms_PrimariesToXYZD50(rx, ry, gx, gy, bx, by, wx, wy, &tmp)) { - toXYZD50 = tmp; - } else { - // Note that Blink simply returns nullptr in this case. We'll fall - // back to srgb. - } - } - - skcms_TransferFunction fn; - if (PNG_INFO_gAMA == png_get_gAMA_fixed(png_ptr, info_ptr, &gamma)) { - fn.a = 1.0f; - fn.b = fn.c = fn.d = fn.e = fn.f = 0.0f; - fn.g = png_inverted_fixed_point_to_float(gamma); - } else { - // Default to sRGB gamma if the image has color space information, - // but does not specify gamma. - // Note that Blink would again return nullptr in this case. - fn = *skcms_sRGB_TransferFunction(); - } - - skcms_ICCProfile skcmsProfile; - skcms_Init(&skcmsProfile); - skcms_SetTransferFunction(&skcmsProfile, &fn); - skcms_SetXYZD50(&skcmsProfile, &toXYZD50); - - return SkEncodedInfo::ICCProfile::Make(skcmsProfile); -#else // LIBPNG >= 1.6 - return nullptr; -#endif // LIBPNG >= 1.6 -} - -static SkCodec::Result log_and_return_error(bool success) { - if (success) return SkCodec::kIncompleteInput; -#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK - SkAndroidFrameworkUtils::SafetyNetLog("117838472"); -#endif - return SkCodec::kErrorInInput; -} - -class SkPngNormalDecoder : public SkPngCodec { -public: - SkPngNormalDecoder(SkEncodedInfo&& info, - std::unique_ptr stream, - SkPngChunkReader* reader, - png_structp png_ptr, - png_infop info_ptr) - : SkPngCodec(std::move(info), std::move(stream), reader, png_ptr, info_ptr) - , fRowsWrittenToOutput(0) - , fDst(nullptr) - , fRowBytes(0) - , fFirstRow(0) - , fLastRow(0) {} - - static void AllRowsCallback(png_structp png_ptr, png_bytep row, png_uint_32 rowNum, int /*pass*/) { - GetDecoder(png_ptr)->allRowsCallback(row, rowNum); - } - - static void RowCallback(png_structp png_ptr, png_bytep row, png_uint_32 rowNum, int /*pass*/) { - GetDecoder(png_ptr)->rowCallback(row, rowNum); - } - -private: - int fRowsWrittenToOutput; - void* fDst; - size_t fRowBytes; - - // Variables for partial decode - int fFirstRow; // FIXME: Move to baseclass? - int fLastRow; - int fRowsNeeded; - - static SkPngNormalDecoder* GetDecoder(png_structp png_ptr) { - return static_cast(png_get_progressive_ptr(png_ptr)); - } - - Result decodeAllRows(void* dst, size_t rowBytes, int* rowsDecoded) override { - const int height = this->dimensions().height(); - png_set_progressive_read_fn(this->png_ptr(), this, nullptr, AllRowsCallback, nullptr); - fDst = dst; - fRowBytes = rowBytes; - - fRowsWrittenToOutput = 0; - fFirstRow = 0; - fLastRow = height - 1; - - const bool success = this->processData(); - if (success && fRowsWrittenToOutput == height) { - return kSuccess; - } - - if (rowsDecoded) { - *rowsDecoded = fRowsWrittenToOutput; - } - - return log_and_return_error(success); - } - - void allRowsCallback(png_bytep row, int rowNum) { - SkASSERT(rowNum == fRowsWrittenToOutput); - fRowsWrittenToOutput++; - this->applyXformRow(fDst, row); - fDst = SkTAddOffset(fDst, fRowBytes); - } - - void setRange(int firstRow, int lastRow, void* dst, size_t rowBytes) override { - png_set_progressive_read_fn(this->png_ptr(), this, nullptr, RowCallback, nullptr); - fFirstRow = firstRow; - fLastRow = lastRow; - fDst = dst; - fRowBytes = rowBytes; - fRowsWrittenToOutput = 0; - fRowsNeeded = fLastRow - fFirstRow + 1; - } - - Result decode(int* rowsDecoded) override { - if (this->swizzler()) { - const int sampleY = this->swizzler()->sampleY(); - fRowsNeeded = get_scaled_dimension(fLastRow - fFirstRow + 1, sampleY); - } - - const bool success = this->processData(); - if (success && fRowsWrittenToOutput == fRowsNeeded) { - return kSuccess; - } - - if (rowsDecoded) { - *rowsDecoded = fRowsWrittenToOutput; - } - - return log_and_return_error(success); - } - - void rowCallback(png_bytep row, int rowNum) { - if (rowNum < fFirstRow) { - // Ignore this row. - return; - } - - SkASSERT(rowNum <= fLastRow); - SkASSERT(fRowsWrittenToOutput < fRowsNeeded); - - // If there is no swizzler, all rows are needed. - if (!this->swizzler() || this->swizzler()->rowNeeded(rowNum - fFirstRow)) { - this->applyXformRow(fDst, row); - fDst = SkTAddOffset(fDst, fRowBytes); - fRowsWrittenToOutput++; - } - - if (fRowsWrittenToOutput == fRowsNeeded) { - // Fake error to stop decoding scanlines. - longjmp(PNG_JMPBUF(this->png_ptr()), kStopDecoding); - } - } -}; - -class SkPngInterlacedDecoder : public SkPngCodec { -public: - SkPngInterlacedDecoder(SkEncodedInfo&& info, - std::unique_ptr stream, - SkPngChunkReader* reader, - png_structp png_ptr, - png_infop info_ptr, - int numberPasses) - : SkPngCodec(std::move(info), std::move(stream), reader, png_ptr, info_ptr) - , fNumberPasses(numberPasses) - , fFirstRow(0) - , fLastRow(0) - , fLinesDecoded(0) - , fInterlacedComplete(false) - , fPng_rowbytes(0) {} - - static void InterlacedRowCallback(png_structp png_ptr, png_bytep row, png_uint_32 rowNum, int pass) { - auto decoder = static_cast(png_get_progressive_ptr(png_ptr)); - decoder->interlacedRowCallback(row, rowNum, pass); - } - -private: - const int fNumberPasses; - int fFirstRow; - int fLastRow; - void* fDst; - size_t fRowBytes; - int fLinesDecoded; - bool fInterlacedComplete; - size_t fPng_rowbytes; - AutoTMalloc fInterlaceBuffer; - - // FIXME: Currently sharing interlaced callback for all rows and subset. It's not - // as expensive as the subset version of non-interlaced, but it still does extra - // work. - void interlacedRowCallback(png_bytep row, int rowNum, int pass) { - if (rowNum < fFirstRow || rowNum > fLastRow || fInterlacedComplete) { - // Ignore this row - return; - } - - png_bytep oldRow = fInterlaceBuffer.get() + (rowNum - fFirstRow) * fPng_rowbytes; - png_progressive_combine_row(this->png_ptr(), oldRow, row); - - if (0 == pass) { - // The first pass initializes all rows. - SkASSERT(row); - SkASSERT(fLinesDecoded == rowNum - fFirstRow); - fLinesDecoded++; - } else { - SkASSERT(fLinesDecoded == fLastRow - fFirstRow + 1); - if (fNumberPasses - 1 == pass && rowNum == fLastRow) { - // Last pass, and we have read all of the rows we care about. - fInterlacedComplete = true; - if (fLastRow != this->dimensions().height() - 1 || - (this->swizzler() && this->swizzler()->sampleY() != 1)) { - // Fake error to stop decoding scanlines. Only stop if we're not decoding the - // whole image, in which case processing the rest of the image might be - // expensive. When decoding the whole image, read through the IEND chunk to - // preserve Android behavior of leaving the input stream in the right place. - longjmp(PNG_JMPBUF(this->png_ptr()), kStopDecoding); - } - } - } - } - - Result decodeAllRows(void* dst, size_t rowBytes, int* rowsDecoded) override { - const int height = this->dimensions().height(); - this->setUpInterlaceBuffer(height); - png_set_progressive_read_fn(this->png_ptr(), this, nullptr, InterlacedRowCallback, - nullptr); - - fFirstRow = 0; - fLastRow = height - 1; - fLinesDecoded = 0; - - const bool success = this->processData(); - png_bytep srcRow = fInterlaceBuffer.get(); - // FIXME: When resuming, this may rewrite rows that did not change. - for (int rowNum = 0; rowNum < fLinesDecoded; rowNum++) { - this->applyXformRow(dst, srcRow); - dst = SkTAddOffset(dst, rowBytes); - srcRow = SkTAddOffset(srcRow, fPng_rowbytes); - } - if (success && fInterlacedComplete) { - return kSuccess; - } - - if (rowsDecoded) { - *rowsDecoded = fLinesDecoded; - } - - return log_and_return_error(success); - } - - void setRange(int firstRow, int lastRow, void* dst, size_t rowBytes) override { - // FIXME: We could skip rows in the interlace buffer that we won't put in the output. - this->setUpInterlaceBuffer(lastRow - firstRow + 1); - png_set_progressive_read_fn(this->png_ptr(), this, nullptr, InterlacedRowCallback, nullptr); - fFirstRow = firstRow; - fLastRow = lastRow; - fDst = dst; - fRowBytes = rowBytes; - fLinesDecoded = 0; - } - - Result decode(int* rowsDecoded) override { - const bool success = this->processData(); - - // Now apply Xforms on all the rows that were decoded. - if (!fLinesDecoded) { - if (rowsDecoded) { - *rowsDecoded = 0; - } - return log_and_return_error(success); - } - - const int sampleY = this->swizzler() ? this->swizzler()->sampleY() : 1; - const int rowsNeeded = get_scaled_dimension(fLastRow - fFirstRow + 1, sampleY); - - // FIXME: For resuming interlace, we may swizzle a row that hasn't changed. But it - // may be too tricky/expensive to handle that correctly. - - // Offset srcRow by get_start_coord rows. We do not need to account for fFirstRow, - // since the first row in fInterlaceBuffer corresponds to fFirstRow. - int srcRow = get_start_coord(sampleY); - void* dst = fDst; - int rowsWrittenToOutput = 0; - while (rowsWrittenToOutput < rowsNeeded && srcRow < fLinesDecoded) { - png_bytep src = SkTAddOffset(fInterlaceBuffer.get(), fPng_rowbytes * srcRow); - this->applyXformRow(dst, src); - dst = SkTAddOffset(dst, fRowBytes); - - rowsWrittenToOutput++; - srcRow += sampleY; - } - - if (success && fInterlacedComplete) { - return kSuccess; - } - - if (rowsDecoded) { - *rowsDecoded = rowsWrittenToOutput; - } - return log_and_return_error(success); - } - - void setUpInterlaceBuffer(int height) { - fPng_rowbytes = png_get_rowbytes(this->png_ptr(), this->info_ptr()); - fInterlaceBuffer.reset(fPng_rowbytes * height); - fInterlacedComplete = false; - } -}; - -// Reads the header and initializes the output fields, if not NULL. -// -// @param stream Input data. Will be read to get enough information to properly -// setup the codec. -// @param chunkReader SkPngChunkReader, for reading unknown chunks. May be NULL. -// If not NULL, png_ptr will hold an *unowned* pointer to it. The caller is -// expected to continue to own it for the lifetime of the png_ptr. -// @param outCodec Optional output variable. If non-NULL, will be set to a new -// SkPngCodec on success. -// @param png_ptrp Optional output variable. If non-NULL, will be set to a new -// png_structp on success. -// @param info_ptrp Optional output variable. If non-NULL, will be set to a new -// png_infop on success; -// @return if kSuccess, the caller is responsible for calling -// png_destroy_read_struct(png_ptrp, info_ptrp). -// Otherwise, the passed in fields (except stream) are unchanged. -static SkCodec::Result read_header(SkStream* stream, SkPngChunkReader* chunkReader, - SkCodec** outCodec, - png_structp* png_ptrp, png_infop* info_ptrp) { - // The image is known to be a PNG. Decode enough to know the SkImageInfo. - png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, - sk_error_fn, sk_warning_fn); - if (!png_ptr) { - return SkCodec::kInternalError; - } - -#ifdef PNG_SET_OPTION_SUPPORTED - // This setting ensures that we display images with incorrect CMF bytes. - // See crbug.com/807324. - png_set_option(png_ptr, PNG_MAXIMUM_INFLATE_WINDOW, PNG_OPTION_ON); -#endif - - AutoCleanPng autoClean(png_ptr, stream, chunkReader, outCodec); - - png_infop info_ptr = png_create_info_struct(png_ptr); - if (info_ptr == nullptr) { - return SkCodec::kInternalError; - } - - autoClean.setInfoPtr(info_ptr); - - if (setjmp(PNG_JMPBUF(png_ptr))) { - return SkCodec::kInvalidInput; - } - -#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED - // Hookup our chunkReader so we can see any user-chunks the caller may be interested in. - // This needs to be installed before we read the png header. Android may store ninepatch - // chunks in the header. - if (chunkReader) { - png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_const_bytep)"", 0); - png_set_read_user_chunk_fn(png_ptr, (png_voidp) chunkReader, sk_read_user_chunk); - } -#endif - - const bool decodedBounds = autoClean.decodeBounds(); - - if (!decodedBounds) { - return SkCodec::kIncompleteInput; - } - - // On success, decodeBounds releases ownership of png_ptr and info_ptr. - if (png_ptrp) { - *png_ptrp = png_ptr; - } - if (info_ptrp) { - *info_ptrp = info_ptr; - } - - // decodeBounds takes care of setting outCodec - if (outCodec) { - SkASSERT(*outCodec); - } - return SkCodec::kSuccess; -} - -void AutoCleanPng::infoCallback(size_t idatLength) { - png_uint_32 origWidth, origHeight; - int bitDepth, encodedColorType; - png_get_IHDR(fPng_ptr, fInfo_ptr, &origWidth, &origHeight, &bitDepth, - &encodedColorType, nullptr, nullptr, nullptr); - - // TODO(https://crbug.com/359245096): Should we support 16-bits of precision - // for gray images? - if (bitDepth == 16 && (PNG_COLOR_TYPE_GRAY == encodedColorType || - PNG_COLOR_TYPE_GRAY_ALPHA == encodedColorType)) { - bitDepth = 8; - png_set_strip_16(fPng_ptr); - } - - // Now determine the default colorType and alphaType and set the required transforms. - // Often, we depend on SkSwizzler to perform any transforms that we need. However, we - // still depend on libpng for many of the rare and PNG-specific cases. - SkEncodedInfo::Color color; - SkEncodedInfo::Alpha alpha; - switch (encodedColorType) { - case PNG_COLOR_TYPE_PALETTE: - // Extract multiple pixels with bit depths of 1, 2, and 4 from a single - // byte into separate bytes (useful for paletted and grayscale images). - if (bitDepth < 8) { - // TODO: Should we use SkSwizzler here? - bitDepth = 8; - png_set_packing(fPng_ptr); - } - - color = SkEncodedInfo::kPalette_Color; - // Set the alpha depending on if a transparency chunk exists. - alpha = png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS) ? - SkEncodedInfo::kUnpremul_Alpha : SkEncodedInfo::kOpaque_Alpha; - break; - case PNG_COLOR_TYPE_RGB: - if (png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS)) { - // Convert to RGBA if transparency chunk exists. - png_set_tRNS_to_alpha(fPng_ptr); - color = SkEncodedInfo::kRGBA_Color; - alpha = SkEncodedInfo::kBinary_Alpha; - } else { - color = SkEncodedInfo::kRGB_Color; - alpha = SkEncodedInfo::kOpaque_Alpha; - } - break; - case PNG_COLOR_TYPE_GRAY: - // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel. - if (bitDepth < 8) { - // TODO: Should we use SkSwizzler here? - bitDepth = 8; - png_set_expand_gray_1_2_4_to_8(fPng_ptr); - } - - if (png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS)) { - png_set_tRNS_to_alpha(fPng_ptr); - color = SkEncodedInfo::kGrayAlpha_Color; - alpha = SkEncodedInfo::kBinary_Alpha; - } else { - color = SkEncodedInfo::kGray_Color; - alpha = SkEncodedInfo::kOpaque_Alpha; - } - break; - case PNG_COLOR_TYPE_GRAY_ALPHA: - color = SkEncodedInfo::kGrayAlpha_Color; - alpha = SkEncodedInfo::kUnpremul_Alpha; - break; - case PNG_COLOR_TYPE_RGBA: - color = SkEncodedInfo::kRGBA_Color; - alpha = SkEncodedInfo::kUnpremul_Alpha; - break; - default: - // All the color types have been covered above. - SkASSERT(false); - color = SkEncodedInfo::kRGBA_Color; - alpha = SkEncodedInfo::kUnpremul_Alpha; - } - - const int numberPasses = png_set_interlace_handling(fPng_ptr); - - if (fOutCodec) { - SkASSERT(nullptr == *fOutCodec); - auto profile = read_color_profile(fPng_ptr, fInfo_ptr); - if (!SkPngCodecBase::isCompatibleColorProfileAndType(profile.get(), color)) { - profile = nullptr; - } - - switch (encodedColorType) { - case PNG_COLOR_TYPE_GRAY_ALPHA:{ - png_color_8p sigBits; - if (png_get_sBIT(fPng_ptr, fInfo_ptr, &sigBits)) { - if (8 == sigBits->alpha && kGraySigBit_GrayAlphaIsJustAlpha == sigBits->gray) { - color = SkEncodedInfo::kXAlpha_Color; - } - } - break; - } - case PNG_COLOR_TYPE_RGB:{ - png_color_8p sigBits; - if (png_get_sBIT(fPng_ptr, fInfo_ptr, &sigBits)) { - if (5 == sigBits->red && 6 == sigBits->green && 5 == sigBits->blue) { - // Recommend a decode to 565 if the sBIT indicates 565. - color = SkEncodedInfo::k565_Color; - } - } - break; - } - } - -#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK - if (encodedColorType != PNG_COLOR_TYPE_GRAY_ALPHA - && SkEncodedInfo::kOpaque_Alpha == alpha) { - png_color_8p sigBits; - if (png_get_sBIT(fPng_ptr, fInfo_ptr, &sigBits)) { - if (5 == sigBits->red && 6 == sigBits->green && 5 == sigBits->blue) { - SkAndroidFrameworkUtils::SafetyNetLog("190188264"); - } - } - } -#endif // SK_BUILD_FOR_ANDROID_FRAMEWORK - - SkEncodedInfo encodedInfo = SkEncodedInfo::Make(origWidth, origHeight, color, alpha, - bitDepth, std::move(profile)); - if (1 == numberPasses) { - *fOutCodec = new SkPngNormalDecoder(std::move(encodedInfo), - std::unique_ptr(fStream), - fChunkReader, - fPng_ptr, - fInfo_ptr); - } else { - *fOutCodec = new SkPngInterlacedDecoder(std::move(encodedInfo), - std::unique_ptr(fStream), - fChunkReader, - fPng_ptr, - fInfo_ptr, - numberPasses); - } - static_cast(*fOutCodec)->setIdatLength(idatLength); - } - - // Release the pointers, which are now owned by the codec or the caller is expected to - // take ownership. - this->releasePngPtrs(); -} - -SkPngCodec::SkPngCodec(SkEncodedInfo&& encodedInfo, - std::unique_ptr stream, - SkPngChunkReader* chunkReader, - void* png_ptr, - void* info_ptr) - : SkPngCodecBase(std::move(encodedInfo), std::move(stream)) - , fPngChunkReader(SkSafeRef(chunkReader)) - , fPng_ptr(png_ptr) - , fInfo_ptr(info_ptr) - , fIdatLength(0) - , fDecodedIdat(false) {} - -SkPngCodec::~SkPngCodec() { - this->destroyReadStruct(); -} - -void SkPngCodec::destroyReadStruct() { - if (fPng_ptr) { - // We will never have a nullptr fInfo_ptr with a non-nullptr fPng_ptr - SkASSERT(fInfo_ptr); - png_destroy_read_struct((png_struct**)&fPng_ptr, (png_info**)&fInfo_ptr, nullptr); - fPng_ptr = nullptr; - fInfo_ptr = nullptr; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// Getting the pixels -/////////////////////////////////////////////////////////////////////////////// - -SkCodec::Result SkPngCodec::initializeXforms(const SkImageInfo& dstInfo, const Options& options) { - if (setjmp(PNG_JMPBUF((png_struct*)fPng_ptr))) { - SkCodecPrintf("Failed on png_read_update_info.\n"); - return kInvalidInput; - } - png_read_update_info(fPng_ptr, fInfo_ptr); - - // `SkPngCodec` doesn't support APNG - the `frameWidth` is always the same - // as the full image width. - int frameWidth = dstInfo.width(); - - return SkPngCodecBase::initializeXforms(dstInfo, options, frameWidth); -} - -bool SkPngCodec::onRewind() { - // This sets fPng_ptr and fInfo_ptr to nullptr. If read_header - // succeeds, they will be repopulated, and if it fails, they will - // remain nullptr. Any future accesses to fPng_ptr and fInfo_ptr will - // come through this function which will rewind and again attempt - // to reinitialize them. - this->destroyReadStruct(); - - png_structp png_ptr; - png_infop info_ptr; - if (kSuccess != read_header(this->stream(), fPngChunkReader.get(), nullptr, - &png_ptr, &info_ptr)) { - return false; - } - - fPng_ptr = png_ptr; - fInfo_ptr = info_ptr; - fDecodedIdat = false; - return true; -} - -SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, - size_t rowBytes, const Options& options, - int* rowsDecoded) { - Result result = this->initializeXforms(dstInfo, options); - if (kSuccess != result) { - return result; - } - - if (options.fSubset) { - return kUnimplemented; - } - - this->initializeXformParams(); - return this->decodeAllRows(dst, rowBytes, rowsDecoded); -} - -SkCodec::Result SkPngCodec::onStartIncrementalDecode(const SkImageInfo& dstInfo, - void* dst, size_t rowBytes, const SkCodec::Options& options) { - Result result = this->initializeXforms(dstInfo, options); - if (kSuccess != result) { - return result; - } - - int firstRow, lastRow; - if (options.fSubset) { - firstRow = options.fSubset->top(); - lastRow = options.fSubset->bottom() - 1; - } else { - firstRow = 0; - lastRow = dstInfo.height() - 1; - } - this->setRange(firstRow, lastRow, dst, rowBytes); - return kSuccess; -} - -SkCodec::Result SkPngCodec::onIncrementalDecode(int* rowsDecoded) { - // FIXME: Only necessary on the first call. - this->initializeXformParams(); - - return this->decode(rowsDecoded); -} - -std::unique_ptr SkPngCodec::MakeFromStream(std::unique_ptr stream, - Result* result, SkPngChunkReader* chunkReader) { - SkASSERT(result); - if (!stream) { - *result = SkCodec::kInvalidInput; - return nullptr; - } - SkCodec* outCodec = nullptr; - *result = read_header(stream.get(), chunkReader, &outCodec, nullptr, nullptr); - if (kSuccess == *result) { - // Codec has taken ownership of the stream. - SkASSERT(outCodec); - stream.release(); - } - return std::unique_ptr(outCodec); -} - -namespace SkPngDecoder { -bool IsPng(const void* data, size_t len) { - return SkPngCodec::IsPng(data, len); -} - -std::unique_ptr Decode(std::unique_ptr stream, - SkCodec::Result* outResult, - SkCodecs::DecodeContext ctx) { - SkCodec::Result resultStorage; - if (!outResult) { - outResult = &resultStorage; - } - SkPngChunkReader* chunkReader = nullptr; - if (ctx) { - chunkReader = static_cast(ctx); - } - return SkPngCodec::MakeFromStream(std::move(stream), outResult, chunkReader); -} - -std::unique_ptr Decode(sk_sp data, - SkCodec::Result* outResult, - SkCodecs::DecodeContext ctx) { - if (!data) { - if (outResult) { - *outResult = SkCodec::kInvalidInput; - } - return nullptr; - } - return Decode(SkMemoryStream::Make(std::move(data)), outResult, ctx); -} -} // namespace SkPngDecoder diff --git a/gfx/skia/skia/src/codec/SkPngCodec.h b/gfx/skia/skia/src/codec/SkPngCodec.h deleted file mode 100644 index aef9a6597c30..000000000000 --- a/gfx/skia/skia/src/codec/SkPngCodec.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#ifndef SkPngCodec_DEFINED -#define SkPngCodec_DEFINED - -#include -#include -#include -#include - -#include "include/codec/SkCodec.h" -#include "include/core/SkRefCnt.h" -#include "src/codec/SkPngCodecBase.h" - -class SkPngChunkReader; -class SkStream; -struct SkEncodedInfo; -struct SkImageInfo; -template class SkSpan; - -class SkPngCodec : public SkPngCodecBase { -public: - static bool IsPng(const void*, size_t); - - // Assume IsPng was called and returned true. - static std::unique_ptr MakeFromStream(std::unique_ptr, Result*, - SkPngChunkReader* = nullptr); - - // FIXME (scroggo): Temporarily needed by AutoCleanPng. - void setIdatLength(size_t len) { fIdatLength = len; } - - ~SkPngCodec() override; - -protected: - // We hold the png_ptr and info_ptr as voidp to avoid having to include png.h - // or forward declare their types here. voidp auto-casts to the real pointer types. - struct voidp { - voidp(void* ptr) : fPtr(ptr) {} - - template - operator T*() const { return (T*)fPtr; } - - explicit operator bool() const { return fPtr != nullptr; } - - void* fPtr; - }; - - SkPngCodec(SkEncodedInfo&&, - std::unique_ptr, - SkPngChunkReader*, - void* png_ptr, - void* info_ptr); - - Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, int*) - override; - bool onRewind() override; - - voidp png_ptr() { return fPng_ptr; } - voidp info_ptr() { return fInfo_ptr; } - - /** - * Pass available input to libpng to process it. - * - * libpng will call any relevant callbacks installed. This will continue decoding - * until it reaches the end of the file, or until a callback tells libpng to stop. - */ - bool processData(); - - Result onStartIncrementalDecode(const SkImageInfo& dstInfo, void* pixels, size_t rowBytes, - const SkCodec::Options&) override; - Result onIncrementalDecode(int*) override; - - sk_sp fPngChunkReader; - voidp fPng_ptr; - voidp fInfo_ptr; - -private: - // SkPngCodecBase overrides: - std::optional> onTryGetPlteChunk() override; - std::optional> onTryGetTrnsChunk() override; - - // Thin wrapper around `SkPngCodecBase::initializeXforms` that also sets up - // some `libpng`-specific state. - Result initializeXforms(const SkImageInfo& dstInfo, const Options&); - - void destroyReadStruct(); - - virtual Result decodeAllRows(void* dst, size_t rowBytes, int* rowsDecoded) = 0; - virtual void setRange(int firstRow, int lastRow, void* dst, size_t rowBytes) = 0; - virtual Result decode(int* rowsDecoded) = 0; - - size_t fIdatLength; - bool fDecodedIdat; -}; -#endif // SkPngCodec_DEFINED diff --git a/gfx/skia/skia/src/codec/SkPngCodecBase.cpp b/gfx/skia/skia/src/codec/SkPngCodecBase.cpp deleted file mode 100644 index fa87bd012fe8..000000000000 --- a/gfx/skia/skia/src/codec/SkPngCodecBase.cpp +++ /dev/null @@ -1,355 +0,0 @@ -/* - * Copyright 2024 Google LLC. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "src/codec/SkPngCodecBase.h" - -#include -#include -#include - -#include "include/codec/SkCodec.h" -#include "include/codec/SkEncodedImageFormat.h" -#include "include/core/SkAlphaType.h" -#include "include/core/SkColor.h" -#include "include/core/SkColorType.h" -#include "include/core/SkImageInfo.h" -#include "include/core/SkRect.h" -#include "include/core/SkStream.h" -#include "include/private/SkEncodedInfo.h" -#include "include/private/base/SkAssert.h" -#include "include/private/base/SkSpan_impl.h" -#include "modules/skcms/skcms.h" -#include "src/codec/SkCodecPriv.h" -#include "src/codec/SkColorPalette.h" -#include "src/codec/SkSwizzler.h" -#include "src/core/SkMemset.h" -#include "src/core/SkSwizzlePriv.h" - -namespace { - -constexpr SkColorType kXformSrcColorType = kRGBA_8888_SkColorType; - -inline bool needs_premul(SkAlphaType dstAT, SkEncodedInfo::Alpha encodedAlpha) { - return kPremul_SkAlphaType == dstAT && SkEncodedInfo::kUnpremul_Alpha == encodedAlpha; -} - -skcms_PixelFormat ToPixelFormat(const SkEncodedInfo& info) { - // We use kRGB and kRGBA formats because color PNGs are always RGB or RGBA. - if (16 == info.bitsPerComponent()) { - if (SkEncodedInfo::kRGBA_Color == info.color()) { - return skcms_PixelFormat_RGBA_16161616BE; - } else if (SkEncodedInfo::kRGB_Color == info.color()) { - return skcms_PixelFormat_RGB_161616BE; - } - } else if (SkEncodedInfo::kGray_Color == info.color()) { - return skcms_PixelFormat_G_8; - } - - return skcms_PixelFormat_RGBA_8888; -} - -} // namespace - -SkPngCodecBase::~SkPngCodecBase() = default; - -// static -bool SkPngCodecBase::isCompatibleColorProfileAndType(const SkEncodedInfo::ICCProfile* profile, - SkEncodedInfo::Color color) { - if (profile) { - switch (profile->profile()->data_color_space) { - case skcms_Signature_CMYK: - return false; - case skcms_Signature_Gray: - if (SkEncodedInfo::kGray_Color != color && - SkEncodedInfo::kGrayAlpha_Color != color) { - return false; - } - break; - default: - break; - } - } - - return true; -} - -SkPngCodecBase::SkPngCodecBase(SkEncodedInfo&& encodedInfo, std::unique_ptr stream) - : SkCodec(std::move(encodedInfo), ToPixelFormat(encodedInfo), std::move(stream)) {} - -SkEncodedImageFormat SkPngCodecBase::onGetEncodedFormat() const { - return SkEncodedImageFormat::kPNG; -} - -SkCodec::Result SkPngCodecBase::initializeXforms(const SkImageInfo& dstInfo, - const Options& options, - int frameWidth) { - if (frameWidth != dstInfo.width() && options.fSubset) { - return kInvalidParameters; - } - fXformWidth = frameWidth; - - { - size_t encodedBitsPerPixel = static_cast(getEncodedInfo().bitsPerPixel()); - - // We assume that `frameWidth` and `bitsPerPixel` have been already sanitized - // earlier (and that the multiplication and addition below won't overflow). - SkASSERT(0 < frameWidth); - SkASSERT(frameWidth < 0xFFFFFF); - SkASSERT(encodedBitsPerPixel < 128); - - size_t encodedBitsPerRow = static_cast(frameWidth) * encodedBitsPerPixel; - fEncodedRowBytes = (encodedBitsPerRow + 7) / 8; // Round up to the next byte. - -#if defined(SK_DEBUG) - size_t dstBytesPerPixel = dstInfo.bytesPerPixel(); - fDstRowBytes = static_cast(frameWidth) * dstBytesPerPixel; -#endif - } - - // Reset fSwizzler and this->colorXform(). We can't do this in onRewind() because the - // interlaced scanline decoder may need to rewind. - fSwizzler.reset(nullptr); - - // If skcms directly supports the encoded PNG format, we should skip format - // conversion in the swizzler (or skip swizzling altogether). - bool skipFormatConversion = false; - switch (this->getEncodedInfo().color()) { - case SkEncodedInfo::kRGB_Color: - if (this->getEncodedInfo().bitsPerComponent() != 16) { - break; - } - [[fallthrough]]; - case SkEncodedInfo::kRGBA_Color: - case SkEncodedInfo::kGray_Color: - skipFormatConversion = this->colorXform(); - break; - default: - break; - } - - if (skipFormatConversion && !options.fSubset) { - fXformMode = kColorOnly_XformMode; - } else { - if (SkEncodedInfo::kPalette_Color == this->getEncodedInfo().color()) { - if (!this->createColorTable(dstInfo)) { - return kInvalidInput; - } - } - - Result result = - this->initializeSwizzler(dstInfo, options, skipFormatConversion, frameWidth); - if (result != kSuccess) { - return result; - } - } - - this->allocateStorage(dstInfo); - - // We can't call `initializeXformParams` here, because `swizzleWidth` may - // change *after* `onStartIncrementalDecode` - // (`SkSampledCodec::sampledDecode` first [transitively] calls - // `onStartIncrementalDecode` and *then* `SkSwizzler::onSetSampleX`). - - return kSuccess; -} - -void SkPngCodecBase::initializeXformParams() { - if (fXformMode == kSwizzleColor_XformMode) { - fXformWidth = this->swizzler()->swizzleWidth(); - } -} - -void SkPngCodecBase::allocateStorage(const SkImageInfo& dstInfo) { - switch (fXformMode) { - case kSwizzleOnly_XformMode: - break; - case kColorOnly_XformMode: - // Intentional fall through. A swizzler hasn't been created yet, but one will - // be created later if we are sampling. We'll go ahead and allocate - // enough memory to swizzle if necessary. - case kSwizzleColor_XformMode: { - const int bitsPerPixel = this->getEncodedInfo().bitsPerPixel(); - - // If we have more than 8-bits (per component) of precision, we will keep that - // extra precision. Otherwise, we will swizzle to RGBA_8888 before transforming. - const size_t bytesPerPixel = (bitsPerPixel > 32) ? bitsPerPixel / 8 : 4; - const size_t colorXformBytes = dstInfo.width() * bytesPerPixel; - fStorage.reset(colorXformBytes); - break; - } - } -} - -SkCodec::Result SkPngCodecBase::initializeSwizzler(const SkImageInfo& dstInfo, - const Options& options, - bool skipFormatConversion, - int frameWidth) { - SkImageInfo swizzlerInfo = dstInfo; - Options swizzlerOptions = options; - fXformMode = kSwizzleOnly_XformMode; - if (this->colorXform() && this->xformOnDecode()) { - if (SkEncodedInfo::kGray_Color == this->getEncodedInfo().color()) { - swizzlerInfo = swizzlerInfo.makeColorType(kGray_8_SkColorType); - } else { - swizzlerInfo = swizzlerInfo.makeColorType(kXformSrcColorType); - } - if (kPremul_SkAlphaType == dstInfo.alphaType()) { - swizzlerInfo = swizzlerInfo.makeAlphaType(kUnpremul_SkAlphaType); - } - - fXformMode = kSwizzleColor_XformMode; - - // Here, we swizzle into temporary memory, which is not zero initialized. - // FIXME (msarett): - // Is this a problem? - swizzlerOptions.fZeroInitialized = kNo_ZeroInitialized; - } - - SkIRect frameRect = SkIRect::MakeWH(frameWidth, 1); - const SkIRect* frameRectPtr = nullptr; - if (options.fSubset) { - SkASSERT(frameWidth == dstInfo.width()); - } else { - frameRectPtr = &frameRect; - } - - if (skipFormatConversion) { - // We cannot skip format conversion when there is a color table. - SkASSERT(!fColorTable); - int srcBPP = 0; - switch (this->getEncodedInfo().color()) { - case SkEncodedInfo::kRGB_Color: - SkASSERT(this->getEncodedInfo().bitsPerComponent() == 16); - srcBPP = 6; - break; - case SkEncodedInfo::kRGBA_Color: - srcBPP = this->getEncodedInfo().bitsPerComponent() / 2; - break; - case SkEncodedInfo::kGray_Color: - srcBPP = 1; - break; - default: - SkASSERT(false); - break; - } - fSwizzler = SkSwizzler::MakeSimple(srcBPP, swizzlerInfo, swizzlerOptions, frameRectPtr); - } else { - const SkPMColor* colors = get_color_ptr(fColorTable.get()); - fSwizzler = SkSwizzler::Make( - this->getEncodedInfo(), colors, swizzlerInfo, swizzlerOptions, frameRectPtr); - } - - return !!fSwizzler ? kSuccess : kUnimplemented; -} - -SkSampler* SkPngCodecBase::getSampler(bool createIfNecessary) { - if (fSwizzler || !createIfNecessary) { - return fSwizzler.get(); - } - - // Ok to ignore `initializeSwizzler`'s result, because if it fails, then - // `fSwizzler` will be `nullptr` and we want to return `nullptr` upon - // failure. - std::ignore = this->initializeSwizzler( - this->dstInfo(), this->options(), true, this->dstInfo().width()); - - return fSwizzler.get(); -} - -void SkPngCodecBase::applyXformRow(SkSpan dstRow, SkSpan srcRow) { - SkASSERT(dstRow.size() >= fDstRowBytes); - SkASSERT(srcRow.size() >= fEncodedRowBytes); - applyXformRow(dstRow.data(), srcRow.data()); -} - -void SkPngCodecBase::applyXformRow(void* dstRow, const uint8_t* srcRow) { - switch (fXformMode) { - case kSwizzleOnly_XformMode: - fSwizzler->swizzle(dstRow, srcRow); - break; - case kColorOnly_XformMode: - this->applyColorXform(dstRow, srcRow, fXformWidth); - break; - case kSwizzleColor_XformMode: - fSwizzler->swizzle(fStorage.get(), srcRow); - this->applyColorXform(dstRow, fStorage.get(), fXformWidth); - break; - } -} - -// Note: SkColorPalette claims to store SkPMColors, which is not necessarily the case here. -bool SkPngCodecBase::createColorTable(const SkImageInfo& dstInfo) { - std::optional> maybePlteChunk = this->onTryGetPlteChunk(); - if (!maybePlteChunk.has_value()) { - return false; - } - const PaletteColorEntry* palette = maybePlteChunk->data(); - size_t numColors = maybePlteChunk->size(); - - // Contents depend on tableColorType and our choice of if/when to premultiply: - // { kPremul, kUnpremul, kOpaque } x { RGBA, BGRA } - SkPMColor colorTable[256]; - SkColorType tableColorType = this->colorXform() ? kXformSrcColorType : dstInfo.colorType(); - - std::optional> maybeTrnsChunk = this->onTryGetTrnsChunk(); - const uint8_t* alphas = nullptr; - size_t numColorsWithAlpha = 0; - if (maybeTrnsChunk.has_value()) { - alphas = maybeTrnsChunk->data(); - numColorsWithAlpha = maybeTrnsChunk->size(); - } - - if (alphas) { - bool premultiply = needs_premul(dstInfo.alphaType(), this->getEncodedInfo().alpha()); - - // Choose which function to use to create the color table. If the final destination's - // colortype is unpremultiplied, the color table will store unpremultiplied colors. - PackColorProc proc = choose_pack_color_proc(premultiply, tableColorType); - - for (size_t i = 0; i < numColorsWithAlpha; i++) { - // We don't have a function in SkOpts that combines a set of alphas with a set - // of RGBs. We could write one, but it's hardly worth it, given that this - // is such a small fraction of the total decode time. - colorTable[i] = proc(alphas[i], palette->red, palette->green, palette->blue); - palette++; - } - } - - if (numColorsWithAlpha < numColors) { - // The optimized code depends on a 3-byte png_color struct with the colors - // in RGB order. These checks make sure it is safe to use. - static_assert(3 == sizeof(PaletteColorEntry)); - static_assert(offsetof(PaletteColorEntry, red) == 0); - static_assert(offsetof(PaletteColorEntry, green) == 1); - static_assert(offsetof(PaletteColorEntry, blue) == 2); - - if (is_rgba(tableColorType)) { - SkOpts::RGB_to_RGB1(colorTable + numColorsWithAlpha, - (const uint8_t*)palette, - numColors - numColorsWithAlpha); - } else { - SkOpts::RGB_to_BGR1(colorTable + numColorsWithAlpha, - (const uint8_t*)palette, - numColors - numColorsWithAlpha); - } - } - - if (this->colorXform() && !this->xformOnDecode()) { - this->applyColorXform(colorTable, colorTable, numColors); - } - - // Pad the color table with the last color in the table (or black) in the case that - // invalid pixel indices exceed the number of colors in the table. - const size_t maxColors = static_cast(1) << this->getEncodedInfo().bitsPerComponent(); - if (numColors < maxColors) { - SkPMColor lastColor = numColors > 0 ? colorTable[numColors - 1] : SK_ColorBLACK; - SkOpts::memset32(colorTable + numColors, lastColor, maxColors - numColors); - } - - fColorTable.reset(new SkColorPalette(colorTable, maxColors)); - return true; -} diff --git a/gfx/skia/skia/src/codec/SkPngCodecBase.h b/gfx/skia/skia/src/codec/SkPngCodecBase.h deleted file mode 100644 index 08be4221a6db..000000000000 --- a/gfx/skia/skia/src/codec/SkPngCodecBase.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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 SkPngCodecBase_DEFINED -#define SkPngCodecBase_DEFINED - -#include -#include -#include -#include - -#include "include/codec/SkCodec.h" -#include "include/core/SkRefCnt.h" -#include "include/private/SkEncodedInfo.h" -#include "include/private/base/SkDebug.h" -#include "include/private/base/SkTemplates.h" - -class SkColorPalette; -class SkSampler; -class SkStream; -class SkSwizzler; -enum class SkEncodedImageFormat; -struct SkImageInfo; -template class SkSpan; - -// This class implements functionality shared between `SkPngCodec` and -// `SkPngRustCodec` (the latter is from `experimental/rust_png`). -class SkPngCodecBase : public SkCodec { -public: - ~SkPngCodecBase() override; - - static bool isCompatibleColorProfileAndType(const SkEncodedInfo::ICCProfile* profile, - SkEncodedInfo::Color color); -protected: - SkPngCodecBase(SkEncodedInfo&&, std::unique_ptr); - - // Initialize most fields needed by `applyXformRow`. - // - // Each call to `applyXformRow` will transform `frameWidth` pixels - // (which may be less than `dstInfo.width()` when decoding frames that - // depend on earlier frames). - Result initializeXforms(const SkImageInfo& dstInfo, const Options& options, int frameWidth); - - // Initialize other fields needed by `applyXformRow`. - // - // Needs to be called *after* (i.e. outside of) `onStartIncrementalDecode`. - void initializeXformParams(); - - // Transforms a decoded row into the `dstInfo` format that was earlier - // passed to `initializeXforms`. - // - // The first bytes/pixels of `srcRow` will be transformed into the first - // bytes/pixels of `dstRow`. In other words, the transformation ignores - // `fcTL.x_offset` field - the caller should offset `dstRow` if desired - // (it may not be desirable when working with interlaced rows which are - // first transformed into an intermediate buffer). - void applyXformRow(SkSpan dstRow, SkSpan srcRow); - void applyXformRow(void* dstRow, const uint8_t* srcRow); - - size_t getEncodedRowBytes() const { return fEncodedRowBytes; } - const SkSwizzler* swizzler() const { return fSwizzler.get(); } - - struct PaletteColorEntry { - uint8_t red; - uint8_t green; - uint8_t blue; - }; - virtual std::optional> onTryGetPlteChunk() = 0; - virtual std::optional> onTryGetTrnsChunk() = 0; - -private: - // SkCodec overrides: - SkEncodedImageFormat onGetEncodedFormat() const final; - SkSampler* getSampler(bool createIfNecessary) final; - - void allocateStorage(const SkImageInfo& dstInfo); - Result initializeSwizzler(const SkImageInfo& dstInfo, - const Options& options, - bool skipFormatConversion, - int frameWidth); - bool createColorTable(const SkImageInfo& dstInfo); - - enum XformMode { - // Requires only a swizzle pass. - kSwizzleOnly_XformMode, - - // Requires only a color xform pass. - kColorOnly_XformMode, - - // Requires a swizzle and a color xform. - kSwizzleColor_XformMode, - }; - XformMode fXformMode; - - std::unique_ptr fSwizzler; - skia_private::AutoTMalloc fStorage; - int fXformWidth = -1; - sk_sp fColorTable; // May be unpremul. - - size_t fEncodedRowBytes = 0; // Size of encoded/source row in bytes. -#if defined(SK_DEBUG) - size_t fDstRowBytes = 0; // Size of destination row in bytes. -#endif -}; - -#endif // SkPngCodecBase_DEFINED diff --git a/gfx/skia/skia/src/codec/SkPngPriv.h b/gfx/skia/skia/src/codec/SkPngPriv.h deleted file mode 100644 index 5760179b6014..000000000000 --- a/gfx/skia/skia/src/codec/SkPngPriv.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkPngPriv_DEFINED -#define SkPngPriv_DEFINED - -#include "include/core/SkTypes.h" - -// We store kAlpha_8 images as GrayAlpha in png. Our private signal is significant bits for gray. -// If that is set to 1, we assume the gray channel can be ignored, and we output just alpha. -// We tried 0 at first, but png doesn't like a 0 sigbit for a channel it expects, hence we chose 1. - -static constexpr int kGraySigBit_GrayAlphaIsJustAlpha = 1; - -#endif diff --git a/gfx/skia/skia/src/codec/SkRawCodec.cpp b/gfx/skia/skia/src/codec/SkRawCodec.cpp deleted file mode 100644 index 81d5ec4c093d..000000000000 --- a/gfx/skia/skia/src/codec/SkRawCodec.cpp +++ /dev/null @@ -1,856 +0,0 @@ -/* - * 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 "src/codec/SkRawCodec.h" - -#include "include/codec/SkCodec.h" -#include "include/codec/SkRawDecoder.h" -#include "include/core/SkColorSpace.h" -#include "include/core/SkData.h" -#include "include/core/SkImageInfo.h" -#include "include/core/SkRefCnt.h" -#include "include/core/SkStream.h" -#include "include/core/SkTypes.h" -#include "include/private/SkEncodedInfo.h" -#include "include/private/base/SkDebug.h" -#include "include/private/base/SkMutex.h" -#include "include/private/base/SkTArray.h" -#include "include/private/base/SkTemplates.h" -#include "modules/skcms/skcms.h" -#include "src/codec/SkCodecPriv.h" -#include "src/codec/SkJpegCodec.h" -#include "src/core/SkStreamPriv.h" -#include "src/core/SkTaskGroup.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dng_area_task.h" // NO_G3_REWRITE -#include "dng_color_space.h" // NO_G3_REWRITE -#include "dng_errors.h" // NO_G3_REWRITE -#include "dng_exceptions.h" // NO_G3_REWRITE -#include "dng_host.h" // NO_G3_REWRITE -#include "dng_image.h" // NO_G3_REWRITE -#include "dng_info.h" // NO_G3_REWRITE -#include "dng_memory.h" // NO_G3_REWRITE -#include "dng_mosaic_info.h" // NO_G3_REWRITE -#include "dng_negative.h" // NO_G3_REWRITE -#include "dng_pixel_buffer.h" // NO_G3_REWRITE -#include "dng_point.h" // NO_G3_REWRITE -#include "dng_rational.h" // NO_G3_REWRITE -#include "dng_rect.h" // NO_G3_REWRITE -#include "dng_render.h" // NO_G3_REWRITE -#include "dng_sdk_limits.h" // NO_G3_REWRITE -#include "dng_stream.h" // NO_G3_REWRITE -#include "dng_tag_types.h" // NO_G3_REWRITE -#include "dng_types.h" // NO_G3_REWRITE -#include "dng_utils.h" // NO_G3_REWRITE - -#include "src/piex.h" // NO_G3_REWRITE -#include "src/piex_types.h" // NO_G3_REWRITE - -using namespace skia_private; - -template struct sk_is_trivially_relocatable; -template <> struct sk_is_trivially_relocatable : std::true_type {}; - -namespace { - -// Calculates the number of tiles of tile_size that fit into the area in vertical and horizontal -// directions. -dng_point num_tiles_in_area(const dng_point &areaSize, - const dng_point_real64 &tileSize) { - // FIXME: Add a ceil_div() helper in SkCodecPriv.h - return dng_point(static_cast((areaSize.v + tileSize.v - 1) / tileSize.v), - static_cast((areaSize.h + tileSize.h - 1) / tileSize.h)); -} - -int num_tasks_required(const dng_point& tilesInTask, - const dng_point& tilesInArea) { - return ((tilesInArea.v + tilesInTask.v - 1) / tilesInTask.v) * - ((tilesInArea.h + tilesInTask.h - 1) / tilesInTask.h); -} - -// Calculate the number of tiles to process per task, taking into account the maximum number of -// tasks. It prefers to increase horizontally for better locality of reference. -dng_point num_tiles_per_task(const int maxTasks, - const dng_point &tilesInArea) { - dng_point tilesInTask = {1, 1}; - while (num_tasks_required(tilesInTask, tilesInArea) > maxTasks) { - if (tilesInTask.h < tilesInArea.h) { - ++tilesInTask.h; - } else if (tilesInTask.v < tilesInArea.v) { - ++tilesInTask.v; - } else { - ThrowProgramError("num_tiles_per_task calculation is wrong."); - } - } - return tilesInTask; -} - -std::vector compute_task_areas(const int maxTasks, const dng_rect& area, - const dng_point& tileSize) { - std::vector taskAreas; - const dng_point tilesInArea = num_tiles_in_area(area.Size(), tileSize); - const dng_point tilesPerTask = num_tiles_per_task(maxTasks, tilesInArea); - const dng_point taskAreaSize = {tilesPerTask.v * tileSize.v, - tilesPerTask.h * tileSize.h}; - for (int v = 0; v < tilesInArea.v; v += tilesPerTask.v) { - for (int h = 0; h < tilesInArea.h; h += tilesPerTask.h) { - dng_rect taskArea; - taskArea.t = area.t + v * tileSize.v; - taskArea.l = area.l + h * tileSize.h; - taskArea.b = Min_int32(taskArea.t + taskAreaSize.v, area.b); - taskArea.r = Min_int32(taskArea.l + taskAreaSize.h, area.r); - - taskAreas.push_back(taskArea); - } - } - return taskAreas; -} - -class SkDngHost : public dng_host { -public: - explicit SkDngHost(dng_memory_allocator* allocater) : dng_host(allocater) {} - - void PerformAreaTask(dng_area_task& task, const dng_rect& area) override { - SkTaskGroup taskGroup; - - // tileSize is typically 256x256 - const dng_point tileSize(task.FindTileSize(area)); - const std::vector taskAreas = compute_task_areas(this->PerformAreaTaskThreads(), - area, tileSize); - const int numTasks = static_cast(taskAreas.size()); - - SkMutex mutex; - TArray exceptions; - task.Start(numTasks, tileSize, &Allocator(), Sniffer()); - for (int taskIndex = 0; taskIndex < numTasks; ++taskIndex) { - taskGroup.add([&mutex, &exceptions, &task, this, taskIndex, taskAreas, tileSize] { - try { - task.ProcessOnThread(taskIndex, taskAreas[taskIndex], tileSize, this->Sniffer()); - } catch (dng_exception& exception) { - SkAutoMutexExclusive lock(mutex); - exceptions.push_back(exception); - } catch (...) { - SkAutoMutexExclusive lock(mutex); - exceptions.push_back(dng_exception(dng_error_unknown)); - } - }); - } - - taskGroup.wait(); - task.Finish(numTasks); - - // We only re-throw the first exception. - if (!exceptions.empty()) { - Throw_dng_error(exceptions.front().ErrorCode(), nullptr, nullptr); - } - } - - uint32 PerformAreaTaskThreads() override { -#ifdef SK_BUILD_FOR_ANDROID - // Only use 1 thread. DNGs with the warp effect require a lot of memory, - // and the amount of memory required scales linearly with the number of - // threads. The sample used in CTS requires over 500 MB, so even two - // threads is significantly expensive. There is no good way to tell - // whether the image has the warp effect. - return 1; -#else - return kMaxMPThreads; -#endif - } - -private: - using INHERITED = dng_host; -}; - -// T must be unsigned type. -template -bool safe_add_to_size_t(T arg1, T arg2, size_t* result) { - SkASSERT(arg1 >= 0); - SkASSERT(arg2 >= 0); - if (arg1 >= 0 && arg2 <= std::numeric_limits::max() - arg1) { - T sum = arg1 + arg2; - if (sum <= std::numeric_limits::max()) { - *result = static_cast(sum); - return true; - } - } - return false; -} - -bool is_asset_stream(const SkStream& stream) { - return stream.hasLength() && stream.hasPosition(); -} - -} // namespace - -class SkRawStream { -public: - virtual ~SkRawStream() {} - - /* - * Gets the length of the stream. Depending on the type of stream, this may require reading to - * the end of the stream. - */ - virtual uint64 getLength() = 0; - - virtual bool read(void* data, size_t offset, size_t length) = 0; - - /* - * Creates an SkMemoryStream from the offset with size. - * Note: for performance reason, this function is destructive to the SkRawStream. One should - * abandon current object after the function call. - */ - virtual std::unique_ptr transferBuffer(size_t offset, size_t size) = 0; -}; - -class SkRawLimitedDynamicMemoryWStream : public SkDynamicMemoryWStream { -public: - ~SkRawLimitedDynamicMemoryWStream() override {} - - bool write(const void* buffer, size_t size) override { - size_t newSize; - if (!safe_add_to_size_t(this->bytesWritten(), size, &newSize) || - newSize > kMaxStreamSize) - { - SkCodecPrintf("Error: Stream size exceeds the limit.\n"); - return false; - } - return this->INHERITED::write(buffer, size); - } - -private: - // Most of valid RAW images will not be larger than 100MB. This limit is helpful to avoid - // streaming too large data chunk. We can always adjust the limit here if we need. - const size_t kMaxStreamSize = 100 * 1024 * 1024; // 100MB - - using INHERITED = SkDynamicMemoryWStream; -}; - -// Note: the maximum buffer size is 100MB (limited by SkRawLimitedDynamicMemoryWStream). -class SkRawBufferedStream : public SkRawStream { -public: - explicit SkRawBufferedStream(std::unique_ptr stream) - : fStream(std::move(stream)) - , fWholeStreamRead(false) - { - // Only use SkRawBufferedStream when the stream is not an asset stream. - SkASSERT(!is_asset_stream(*fStream)); - } - - ~SkRawBufferedStream() override {} - - uint64 getLength() override { - if (!this->bufferMoreData(kReadToEnd)) { // read whole stream - ThrowReadFile(); - } - return fStreamBuffer.bytesWritten(); - } - - bool read(void* data, size_t offset, size_t length) override { - if (length == 0) { - return true; - } - - size_t sum; - if (!safe_add_to_size_t(offset, length, &sum)) { - return false; - } - - return this->bufferMoreData(sum) && fStreamBuffer.read(data, offset, length); - } - - std::unique_ptr transferBuffer(size_t offset, size_t size) override { - sk_sp data(SkData::MakeUninitialized(size)); - if (offset > fStreamBuffer.bytesWritten()) { - // If the offset is not buffered, read from fStream directly and skip the buffering. - const size_t skipLength = offset - fStreamBuffer.bytesWritten(); - if (fStream->skip(skipLength) != skipLength) { - return nullptr; - } - const size_t bytesRead = fStream->read(data->writable_data(), size); - if (bytesRead < size) { - data = SkData::MakeSubset(data.get(), 0, bytesRead); - } - } else { - const size_t alreadyBuffered = std::min(fStreamBuffer.bytesWritten() - offset, size); - if (alreadyBuffered > 0 && - !fStreamBuffer.read(data->writable_data(), offset, alreadyBuffered)) { - return nullptr; - } - - const size_t remaining = size - alreadyBuffered; - if (remaining) { - auto* dst = static_cast(data->writable_data()) + alreadyBuffered; - const size_t bytesRead = fStream->read(dst, remaining); - size_t newSize; - if (bytesRead < remaining) { - if (!safe_add_to_size_t(alreadyBuffered, bytesRead, &newSize)) { - return nullptr; - } - data = SkData::MakeSubset(data.get(), 0, newSize); - } - } - } - return SkMemoryStream::Make(data); - } - -private: - // Note: if the newSize == kReadToEnd (0), this function will read to the end of stream. - bool bufferMoreData(size_t newSize) { - if (newSize == kReadToEnd) { - if (fWholeStreamRead) { // already read-to-end. - return true; - } - - // TODO: optimize for the special case when the input is SkMemoryStream. - return SkStreamCopy(&fStreamBuffer, fStream.get()); - } - - if (newSize <= fStreamBuffer.bytesWritten()) { // already buffered to newSize - return true; - } - if (fWholeStreamRead) { // newSize is larger than the whole stream. - return false; - } - - // Try to read at least 8192 bytes to avoid to many small reads. - const size_t kMinSizeToRead = 8192; - const size_t sizeRequested = newSize - fStreamBuffer.bytesWritten(); - const size_t sizeToRead = std::max(kMinSizeToRead, sizeRequested); - AutoSTMalloc tempBuffer(sizeToRead); - const size_t bytesRead = fStream->read(tempBuffer.get(), sizeToRead); - if (bytesRead < sizeRequested) { - return false; - } - return fStreamBuffer.write(tempBuffer.get(), bytesRead); - } - - std::unique_ptr fStream; - bool fWholeStreamRead; - - // Use a size-limited stream to avoid holding too huge buffer. - SkRawLimitedDynamicMemoryWStream fStreamBuffer; - - const size_t kReadToEnd = 0; -}; - -class SkRawAssetStream : public SkRawStream { -public: - explicit SkRawAssetStream(std::unique_ptr stream) - : fStream(std::move(stream)) - { - // Only use SkRawAssetStream when the stream is an asset stream. - SkASSERT(is_asset_stream(*fStream)); - } - - ~SkRawAssetStream() override {} - - uint64 getLength() override { - return fStream->getLength(); - } - - - bool read(void* data, size_t offset, size_t length) override { - if (length == 0) { - return true; - } - - size_t sum; - if (!safe_add_to_size_t(offset, length, &sum)) { - return false; - } - - return fStream->seek(offset) && (fStream->read(data, length) == length); - } - - std::unique_ptr transferBuffer(size_t offset, size_t size) override { - if (fStream->getLength() < offset) { - return nullptr; - } - - size_t sum; - if (!safe_add_to_size_t(offset, size, &sum)) { - return nullptr; - } - - // This will allow read less than the requested "size", because the JPEG codec wants to - // handle also a partial JPEG file. - const size_t bytesToRead = std::min(sum, fStream->getLength()) - offset; - if (bytesToRead == 0) { - return nullptr; - } - - if (fStream->getMemoryBase()) { // directly copy if getMemoryBase() is available. - sk_sp data(SkData::MakeWithCopy( - static_cast(fStream->getMemoryBase()) + offset, bytesToRead)); - fStream.reset(); - return SkMemoryStream::Make(data); - } else { - sk_sp data(SkData::MakeUninitialized(bytesToRead)); - if (!fStream->seek(offset)) { - return nullptr; - } - const size_t bytesRead = fStream->read(data->writable_data(), bytesToRead); - if (bytesRead < bytesToRead) { - data = SkData::MakeSubset(data.get(), 0, bytesRead); - } - return SkMemoryStream::Make(data); - } - } -private: - std::unique_ptr fStream; -}; - -class SkPiexStream : public ::piex::StreamInterface { -public: - // Will NOT take the ownership of the stream. - explicit SkPiexStream(SkRawStream* stream) : fStream(stream) {} - - ~SkPiexStream() override {} - - ::piex::Error GetData(const size_t offset, const size_t length, - uint8* data) override { - return fStream->read(static_cast(data), offset, length) ? - ::piex::Error::kOk : ::piex::Error::kFail; - } - -private: - SkRawStream* fStream; -}; - -class SkDngStream : public dng_stream { -public: - // Will NOT take the ownership of the stream. - SkDngStream(SkRawStream* stream) : fStream(stream) {} - - ~SkDngStream() override {} - - uint64 DoGetLength() override { return fStream->getLength(); } - - void DoRead(void* data, uint32 count, uint64 offset) override { - size_t sum; - if (!safe_add_to_size_t(static_cast(count), offset, &sum) || - !fStream->read(data, static_cast(offset), static_cast(count))) { - ThrowReadFile(); - } - } - -private: - SkRawStream* fStream; -}; - -class SkDngImage { -public: - /* - * Initializes the object with the information from Piex in a first attempt. This way it can - * save time and storage to obtain the DNG dimensions and color filter array (CFA) pattern - * which is essential for the demosaicing of the sensor image. - * Note: this will take the ownership of the stream. - */ - static SkDngImage* NewFromStream(SkRawStream* stream) { - std::unique_ptr dngImage(new SkDngImage(stream)); -#if defined(SK_BUILD_FOR_LIBFUZZER) - // Libfuzzer easily runs out of memory after here. To avoid that - // We just pretend all streams are invalid. Our AFL-fuzzer - // should still exercise this code; it's more resistant to OOM. - return nullptr; -#else - if (!dngImage->initFromPiex() && !dngImage->readDng()) { - return nullptr; - } - - return dngImage.release(); -#endif - } - - /* - * Renders the DNG image to the size. The DNG SDK only allows scaling close to integer factors - * down to 80 pixels on the short edge. The rendered image will be close to the specified size, - * but there is no guarantee that any of the edges will match the requested size. E.g. - * 100% size: 4000 x 3000 - * requested size: 1600 x 1200 - * returned size could be: 2000 x 1500 - */ - dng_image* render(int width, int height) { - if (!fHost || !fInfo || !fNegative || !fDngStream) { - if (!this->readDng()) { - return nullptr; - } - } - - // DNG SDK preserves the aspect ratio, so it only needs to know the longer dimension. - const int preferredSize = std::max(width, height); - try { - // render() takes ownership of fHost, fInfo, fNegative and fDngStream when available. - std::unique_ptr host(fHost.release()); - std::unique_ptr info(fInfo.release()); - std::unique_ptr negative(fNegative.release()); - std::unique_ptr dngStream(fDngStream.release()); - - host->SetPreferredSize(preferredSize); - host->ValidateSizes(); - - negative->ReadStage1Image(*host, *dngStream, *info); - - if (info->fMaskIndex != -1) { - negative->ReadTransparencyMask(*host, *dngStream, *info); - } - - negative->ValidateRawImageDigest(*host); - if (negative->IsDamaged()) { - return nullptr; - } - - const int32 kMosaicPlane = -1; - negative->BuildStage2Image(*host); - negative->BuildStage3Image(*host, kMosaicPlane); - - dng_render render(*host, *negative); - render.SetFinalSpace(dng_space_sRGB::Get()); - render.SetFinalPixelType(ttByte); - - dng_point stage3_size = negative->Stage3Image()->Size(); - render.SetMaximumSize(std::max(stage3_size.h, stage3_size.v)); - - return render.Render(); - } catch (...) { - return nullptr; - } - } - - int width() const { - return fWidth; - } - - int height() const { - return fHeight; - } - - bool isScalable() const { - return fIsScalable; - } - - bool isXtransImage() const { - return fIsXtransImage; - } - - // Quick check if the image contains a valid TIFF header as requested by DNG format. - // Does not affect ownership of stream. - static bool IsTiffHeaderValid(SkRawStream* stream) { - const size_t kHeaderSize = 4; - unsigned char header[kHeaderSize]; - if (!stream->read(header, 0 /* offset */, kHeaderSize)) { - return false; - } - - // Check if the header is valid (endian info and magic number "42"). - bool littleEndian; - if (!is_valid_endian_marker(header, &littleEndian)) { - return false; - } - - return 0x2A == get_endian_short(header + 2, littleEndian); - } - -private: - bool init(int width, int height, const dng_point& cfaPatternSize) { - fWidth = width; - fHeight = height; - - // The DNG SDK scales only during demosaicing, so scaling is only possible when - // a mosaic info is available. - fIsScalable = cfaPatternSize.v != 0 && cfaPatternSize.h != 0; - fIsXtransImage = fIsScalable ? (cfaPatternSize.v == 6 && cfaPatternSize.h == 6) : false; - - return width > 0 && height > 0; - } - - bool initFromPiex() { - // Does not take the ownership of rawStream. - SkPiexStream piexStream(fStream.get()); - ::piex::PreviewImageData imageData; - if (::piex::IsRaw(&piexStream) - && ::piex::GetPreviewImageData(&piexStream, &imageData) == ::piex::Error::kOk) - { - dng_point cfaPatternSize(imageData.cfa_pattern_dim[1], imageData.cfa_pattern_dim[0]); - return this->init(static_cast(imageData.full_width), - static_cast(imageData.full_height), cfaPatternSize); - } - return false; - } - - bool readDng() { - try { - // Due to the limit of DNG SDK, we need to reset host and info. - fHost = std::make_unique(&fAllocator); - fInfo = std::make_unique(); - fDngStream = std::make_unique(fStream.get()); - - fHost->ValidateSizes(); - fInfo->Parse(*fHost, *fDngStream); - fInfo->PostParse(*fHost); - if (!fInfo->IsValidDNG()) { - return false; - } - - fNegative.reset(fHost->Make_dng_negative()); - fNegative->Parse(*fHost, *fDngStream, *fInfo); - fNegative->PostParse(*fHost, *fDngStream, *fInfo); - fNegative->SynchronizeMetadata(); - - dng_point cfaPatternSize(0, 0); - if (fNegative->GetMosaicInfo() != nullptr) { - cfaPatternSize = fNegative->GetMosaicInfo()->fCFAPatternSize; - } - return this->init(static_cast(fNegative->DefaultCropSizeH().As_real64()), - static_cast(fNegative->DefaultCropSizeV().As_real64()), - cfaPatternSize); - } catch (...) { - return false; - } - } - - SkDngImage(SkRawStream* stream) - : fStream(stream) - {} - - dng_memory_allocator fAllocator; - std::unique_ptr fStream; - std::unique_ptr fHost; - std::unique_ptr fInfo; - std::unique_ptr fNegative; - std::unique_ptr fDngStream; - - int fWidth; - int fHeight; - bool fIsScalable; - bool fIsXtransImage; -}; - -/* - * Tries to handle the image with PIEX. If PIEX returns kOk and finds the preview image, create a - * SkJpegCodec. If PIEX returns kFail, then the file is invalid, return nullptr. In other cases, - * fallback to create SkRawCodec for DNG images. - */ -std::unique_ptr SkRawCodec::MakeFromStream(std::unique_ptr stream, - Result* result) { - SkASSERT(result); - if (!stream) { - *result = SkCodec::kInvalidInput; - return nullptr; - } - std::unique_ptr rawStream; - if (is_asset_stream(*stream)) { - rawStream = std::make_unique(std::move(stream)); - } else { - rawStream = std::make_unique(std::move(stream)); - } - - // Does not take the ownership of rawStream. - SkPiexStream piexStream(rawStream.get()); - ::piex::PreviewImageData imageData; - if (::piex::IsRaw(&piexStream)) { - ::piex::Error error = ::piex::GetPreviewImageData(&piexStream, &imageData); - if (error == ::piex::Error::kFail) { - *result = kInvalidInput; - return nullptr; - } - - std::unique_ptr profile; - if (imageData.color_space == ::piex::PreviewImageData::kAdobeRgb) { - skcms_ICCProfile skcmsProfile; - skcms_Init(&skcmsProfile); - skcms_SetTransferFunction(&skcmsProfile, &SkNamedTransferFn::k2Dot2); - skcms_SetXYZD50(&skcmsProfile, &SkNamedGamut::kAdobeRGB); - profile = SkEncodedInfo::ICCProfile::Make(skcmsProfile); - } - - // Theoretically PIEX can return JPEG compressed image or uncompressed RGB image. We only - // handle the JPEG compressed preview image here. - if (error == ::piex::Error::kOk && imageData.preview.length > 0 && - imageData.preview.format == ::piex::Image::kJpegCompressed) - { - // transferBuffer() is destructive to the rawStream. Abandon the rawStream after this - // function call. - // FIXME: one may avoid the copy of memoryStream and use the buffered rawStream. - auto memoryStream = rawStream->transferBuffer(imageData.preview.offset, - imageData.preview.length); - if (!memoryStream) { - *result = kInvalidInput; - return nullptr; - } - return SkJpegCodec::MakeFromStream(std::move(memoryStream), result, - std::move(profile)); - } - } - - if (!SkDngImage::IsTiffHeaderValid(rawStream.get())) { - *result = kUnimplemented; - return nullptr; - } - - // Takes the ownership of the rawStream. - std::unique_ptr dngImage(SkDngImage::NewFromStream(rawStream.release())); - if (!dngImage) { - *result = kInvalidInput; - return nullptr; - } - - *result = kSuccess; - return std::unique_ptr(new SkRawCodec(dngImage.release())); -} - -SkCodec::Result SkRawCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, - size_t dstRowBytes, const Options& options, - int* rowsDecoded) { - const int width = dstInfo.width(); - const int height = dstInfo.height(); - std::unique_ptr image(fDngImage->render(width, height)); - if (!image) { - return kInvalidInput; - } - - // Because the DNG SDK can not guarantee to render to requested size, we allow a small - // difference. Only the overlapping region will be converted. - const float maxDiffRatio = 1.03f; - const dng_point& imageSize = image->Size(); - if (imageSize.h / (float) width > maxDiffRatio || imageSize.h < width || - imageSize.v / (float) height > maxDiffRatio || imageSize.v < height) { - return SkCodec::kInvalidScale; - } - - void* dstRow = dst; - AutoTMalloc srcRow(width * 3); - - dng_pixel_buffer buffer; - buffer.fData = &srcRow[0]; - buffer.fPlane = 0; - buffer.fPlanes = 3; - buffer.fColStep = buffer.fPlanes; - buffer.fPlaneStep = 1; - buffer.fPixelType = ttByte; - buffer.fPixelSize = sizeof(uint8_t); - buffer.fRowStep = width * 3; - - constexpr auto srcFormat = skcms_PixelFormat_RGB_888; - skcms_PixelFormat dstFormat; - if (!sk_select_xform_format(dstInfo.colorType(), false, &dstFormat)) { - return kInvalidConversion; - } - - const skcms_ICCProfile* const srcProfile = this->getEncodedInfo().profile(); - skcms_ICCProfile dstProfileStorage; - const skcms_ICCProfile* dstProfile = nullptr; - if (auto cs = dstInfo.colorSpace()) { - cs->toProfile(&dstProfileStorage); - dstProfile = &dstProfileStorage; - } - - for (int i = 0; i < height; ++i) { - buffer.fArea = dng_rect(i, 0, i + 1, width); - - try { - image->Get(buffer, dng_image::edge_zero); - } catch (...) { - *rowsDecoded = i; - return kIncompleteInput; - } - - if (!skcms_Transform(&srcRow[0], srcFormat, skcms_AlphaFormat_Unpremul, srcProfile, - dstRow, dstFormat, skcms_AlphaFormat_Unpremul, dstProfile, - dstInfo.width())) { - SkDebugf("failed to transform\n"); - *rowsDecoded = i; - return kInternalError; - } - - dstRow = SkTAddOffset(dstRow, dstRowBytes); - } - return kSuccess; -} - -SkISize SkRawCodec::onGetScaledDimensions(float desiredScale) const { - SkASSERT(desiredScale <= 1.f); - - const SkISize dim = this->dimensions(); - SkASSERT(dim.fWidth != 0 && dim.fHeight != 0); - - if (!fDngImage->isScalable()) { - return dim; - } - - // Limits the minimum size to be 80 on the short edge. - const float shortEdge = static_cast(std::min(dim.fWidth, dim.fHeight)); - if (desiredScale < 80.f / shortEdge) { - desiredScale = 80.f / shortEdge; - } - - // For Xtrans images, the integer-factor scaling does not support the half-size scaling case - // (stronger downscalings are fine). In this case, returns the factor "3" scaling instead. - if (fDngImage->isXtransImage() && desiredScale > 1.f / 3.f && desiredScale < 1.f) { - desiredScale = 1.f / 3.f; - } - - // Round to integer-factors. - const float finalScale = std::floor(1.f/ desiredScale); - return SkISize::Make(static_cast(std::floor(dim.fWidth / finalScale)), - static_cast(std::floor(dim.fHeight / finalScale))); -} - -bool SkRawCodec::onDimensionsSupported(const SkISize& dim) { - const SkISize fullDim = this->dimensions(); - const float fullShortEdge = static_cast(std::min(fullDim.fWidth, fullDim.fHeight)); - const float shortEdge = static_cast(std::min(dim.fWidth, dim.fHeight)); - - SkISize sizeFloor = this->onGetScaledDimensions(1.f / std::floor(fullShortEdge / shortEdge)); - SkISize sizeCeil = this->onGetScaledDimensions(1.f / std::ceil(fullShortEdge / shortEdge)); - return sizeFloor == dim || sizeCeil == dim; -} - -SkRawCodec::~SkRawCodec() {} - -SkRawCodec::SkRawCodec(SkDngImage* dngImage) - : INHERITED(SkEncodedInfo::Make(dngImage->width(), dngImage->height(), - SkEncodedInfo::kRGB_Color, - SkEncodedInfo::kOpaque_Alpha, 8), - skcms_PixelFormat_RGBA_8888, nullptr) - , fDngImage(dngImage) {} - -namespace SkRawDecoder { - -std::unique_ptr Decode(std::unique_ptr stream, - SkCodec::Result* outResult, - SkCodecs::DecodeContext) { - SkCodec::Result resultStorage; - if (!outResult) { - outResult = &resultStorage; - } - return SkRawCodec::MakeFromStream(std::move(stream), outResult); -} - -std::unique_ptr Decode(sk_sp data, - SkCodec::Result* outResult, - SkCodecs::DecodeContext) { - if (!data) { - if (outResult) { - *outResult = SkCodec::kInvalidInput; - } - return nullptr; - } - return Decode(SkMemoryStream::Make(std::move(data)), outResult, nullptr); -} -} // namespace SkRawDecoder diff --git a/gfx/skia/skia/src/codec/SkRawCodec.h b/gfx/skia/skia/src/codec/SkRawCodec.h deleted file mode 100644 index 07985470f139..000000000000 --- a/gfx/skia/skia/src/codec/SkRawCodec.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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 SkRawCodec_DEFINED -#define SkRawCodec_DEFINED - -#include "include/codec/SkCodec.h" -#include "include/codec/SkEncodedImageFormat.h" -#include "include/core/SkSize.h" -#include "include/core/SkTypes.h" - -#include -#include - -class SkDngImage; -class SkStream; -struct SkImageInfo; - -/* - * - * This class implements the decoding for RAW images - * - */ -class SkRawCodec : public SkCodec { -public: - - /* - * Creates a RAW decoder - * Takes ownership of the stream - */ - static std::unique_ptr MakeFromStream(std::unique_ptr, Result*); - - ~SkRawCodec() override; - -protected: - - Result onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options&, - int*) override; - - SkEncodedImageFormat onGetEncodedFormat() const override { - return SkEncodedImageFormat::kDNG; - } - - SkISize onGetScaledDimensions(float desiredScale) const override; - - bool onDimensionsSupported(const SkISize&) override; - - // SkCodec only applies the colorXform if it's necessary for color space - // conversion. SkRawCodec will always convert, so tell SkCodec not to. - bool usesColorXform() const override { return false; } - -private: - - /* - * Creates an instance of the decoder - * Called only by NewFromStream, takes ownership of dngImage. - */ - SkRawCodec(SkDngImage* dngImage); - - std::unique_ptr fDngImage; - - using INHERITED = SkCodec; -}; - -#endif diff --git a/gfx/skia/skia/src/codec/SkSampledCodec.cpp b/gfx/skia/skia/src/codec/SkSampledCodec.cpp deleted file mode 100644 index 95e54abf020a..000000000000 --- a/gfx/skia/skia/src/codec/SkSampledCodec.cpp +++ /dev/null @@ -1,353 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "src/codec/SkSampledCodec.h" - -#include "include/codec/SkCodec.h" -#include "include/codec/SkEncodedImageFormat.h" -#include "include/core/SkImageInfo.h" -#include "include/core/SkRect.h" -#include "include/core/SkTypes.h" -#include "include/private/base/SkTemplates.h" -#include "src/base/SkMathPriv.h" -#include "src/codec/SkCodecPriv.h" -#include "src/codec/SkSampler.h" - -SkSampledCodec::SkSampledCodec(SkCodec* codec) - : INHERITED(codec) -{} - -SkISize SkSampledCodec::accountForNativeScaling(int* sampleSizePtr, int* nativeSampleSize) const { - SkISize preSampledSize = this->codec()->dimensions(); - int sampleSize = *sampleSizePtr; - SkASSERT(sampleSize > 1); - - if (nativeSampleSize) { - *nativeSampleSize = 1; - } - - // Only JPEG supports native downsampling. - if (this->codec()->getEncodedFormat() == SkEncodedImageFormat::kJPEG) { - // See if libjpeg supports this scale directly - switch (sampleSize) { - case 2: - case 4: - case 8: - // This class does not need to do any sampling. - *sampleSizePtr = 1; - return this->codec()->getScaledDimensions(get_scale_from_sample_size(sampleSize)); - default: - break; - } - - // Check if sampleSize is a multiple of something libjpeg can support. - int remainder; - const int sampleSizes[] = { 8, 4, 2 }; - for (int supportedSampleSize : sampleSizes) { - int actualSampleSize; - SkTDivMod(sampleSize, supportedSampleSize, &actualSampleSize, &remainder); - if (0 == remainder) { - float scale = get_scale_from_sample_size(supportedSampleSize); - - // this->codec() will scale to this size. - preSampledSize = this->codec()->getScaledDimensions(scale); - - // And then this class will sample it. - *sampleSizePtr = actualSampleSize; - if (nativeSampleSize) { - *nativeSampleSize = supportedSampleSize; - } - break; - } - } - } - - return preSampledSize; -} - -SkISize SkSampledCodec::onGetSampledDimensions(int sampleSize) const { - const SkISize size = this->accountForNativeScaling(&sampleSize); - return SkISize::Make(get_scaled_dimension(size.width(), sampleSize), - get_scaled_dimension(size.height(), sampleSize)); -} - -SkCodec::Result SkSampledCodec::onGetAndroidPixels(const SkImageInfo& info, void* pixels, - size_t rowBytes, const AndroidOptions& options) { - const SkIRect* subset = options.fSubset; - if (!subset || subset->size() == this->codec()->dimensions()) { - if (this->codec()->dimensionsSupported(info.dimensions())) { - return this->codec()->getPixels(info, pixels, rowBytes, &options); - } - - // If the native codec does not support the requested scale, scale by sampling. - return this->sampledDecode(info, pixels, rowBytes, options); - } - - // We are performing a subset decode. - int sampleSize = options.fSampleSize; - SkISize scaledSize = this->getSampledDimensions(sampleSize); - if (!this->codec()->dimensionsSupported(scaledSize)) { - // If the native codec does not support the requested scale, scale by sampling. - return this->sampledDecode(info, pixels, rowBytes, options); - } - - // Calculate the scaled subset bounds. - int scaledSubsetX = subset->x() / sampleSize; - int scaledSubsetY = subset->y() / sampleSize; - int scaledSubsetWidth = info.width(); - int scaledSubsetHeight = info.height(); - - const SkImageInfo scaledInfo = info.makeDimensions(scaledSize); - - // Copy so we can use a different fSubset. - AndroidOptions subsetOptions = options; - { - // Although startScanlineDecode expects the bottom and top to match the - // SkImageInfo, startIncrementalDecode uses them to determine which rows to - // decode. - SkIRect incrementalSubset = SkIRect::MakeXYWH(scaledSubsetX, scaledSubsetY, - scaledSubsetWidth, scaledSubsetHeight); - subsetOptions.fSubset = &incrementalSubset; - const SkCodec::Result startResult = this->codec()->startIncrementalDecode( - scaledInfo, pixels, rowBytes, &subsetOptions); - if (SkCodec::kSuccess == startResult) { - int rowsDecoded = 0; - const SkCodec::Result incResult = this->codec()->incrementalDecode(&rowsDecoded); - if (incResult == SkCodec::kSuccess) { - return SkCodec::kSuccess; - } - SkASSERT(incResult == SkCodec::kIncompleteInput || incResult == SkCodec::kErrorInInput); - - // FIXME: Can zero initialized be read from SkCodec::fOptions? - this->codec()->fillIncompleteImage(scaledInfo, pixels, rowBytes, - options.fZeroInitialized, scaledSubsetHeight, rowsDecoded); - return incResult; - } else if (startResult != SkCodec::kUnimplemented) { - return startResult; - } - // Otherwise fall down to use the old scanline decoder. - // subsetOptions.fSubset will be reset below, so it will not continue to - // point to the object that is no longer on the stack. - } - - // Start the scanline decode. - SkIRect scanlineSubset = SkIRect::MakeXYWH(scaledSubsetX, 0, scaledSubsetWidth, - scaledSize.height()); - subsetOptions.fSubset = &scanlineSubset; - - SkCodec::Result result = this->codec()->startScanlineDecode(scaledInfo, - &subsetOptions); - if (SkCodec::kSuccess != result) { - return result; - } - - // At this point, we are only concerned with subsetting. Either no scale was - // requested, or the this->codec() is handling the scale. - // Note that subsetting is only supported for kTopDown, so this code will not be - // reached for other orders. - SkASSERT(this->codec()->getScanlineOrder() == SkCodec::kTopDown_SkScanlineOrder); - if (!this->codec()->skipScanlines(scaledSubsetY)) { - this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, - scaledSubsetHeight, 0); - return SkCodec::kIncompleteInput; - } - - int decodedLines = this->codec()->getScanlines(pixels, scaledSubsetHeight, rowBytes); - if (decodedLines != scaledSubsetHeight) { - return SkCodec::kIncompleteInput; - } - return SkCodec::kSuccess; -} - - -SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pixels, - size_t rowBytes, const AndroidOptions& options) { - // We should only call this function when sampling. - SkASSERT(options.fSampleSize > 1); - - // FIXME: This was already called by onGetAndroidPixels. Can we reduce that? - int sampleSize = options.fSampleSize; - int nativeSampleSize; - SkISize nativeSize = this->accountForNativeScaling(&sampleSize, &nativeSampleSize); - - // Check if there is a subset. - SkIRect subset; - int subsetY = 0; - int subsetWidth = nativeSize.width(); - int subsetHeight = nativeSize.height(); - if (options.fSubset) { - // We will need to know about subsetting in the y-dimension in order to use the - // scanline decoder. - // Update the subset to account for scaling done by this->codec(). - const SkIRect* subsetPtr = options.fSubset; - - // Do the divide ourselves, instead of calling get_scaled_dimension. If - // X and Y are 0, they should remain 0, rather than being upgraded to 1 - // due to being smaller than the sampleSize. - const int subsetX = subsetPtr->x() / nativeSampleSize; - subsetY = subsetPtr->y() / nativeSampleSize; - - subsetWidth = get_scaled_dimension(subsetPtr->width(), nativeSampleSize); - subsetHeight = get_scaled_dimension(subsetPtr->height(), nativeSampleSize); - - // The scanline decoder only needs to be aware of subsetting in the x-dimension. - subset.setXYWH(subsetX, 0, subsetWidth, nativeSize.height()); - } - - // Since we guarantee that output dimensions are always at least one (even if the sampleSize - // is greater than a given dimension), the input sampleSize is not always the sampleSize that - // we use in practice. - const int sampleX = subsetWidth / info.width(); - const int sampleY = subsetHeight / info.height(); - - const int samplingOffsetY = get_start_coord(sampleY); - const int startY = samplingOffsetY + subsetY; - const int dstHeight = info.height(); - - const SkImageInfo nativeInfo = info.makeDimensions(nativeSize); - - { - // Although startScanlineDecode expects the bottom and top to match the - // SkImageInfo, startIncrementalDecode uses them to determine which rows to - // decode. - AndroidOptions incrementalOptions = options; - SkIRect incrementalSubset; - if (options.fSubset) { - incrementalSubset.fTop = subsetY; - incrementalSubset.fBottom = subsetY + subsetHeight; - incrementalSubset.fLeft = subset.fLeft; - incrementalSubset.fRight = subset.fRight; - incrementalOptions.fSubset = &incrementalSubset; - } - const SkCodec::Result startResult = this->codec()->startIncrementalDecode(nativeInfo, - pixels, rowBytes, &incrementalOptions); - if (SkCodec::kSuccess == startResult) { - SkSampler* sampler = this->codec()->getSampler(true); - if (!sampler) { - return SkCodec::kUnimplemented; - } - - if (sampler->setSampleX(sampleX) != info.width()) { - return SkCodec::kInvalidScale; - } - if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) { - return SkCodec::kInvalidScale; - } - - sampler->setSampleY(sampleY); - - int rowsDecoded = 0; - const SkCodec::Result incResult = this->codec()->incrementalDecode(&rowsDecoded); - if (incResult == SkCodec::kSuccess) { - return SkCodec::kSuccess; - } - SkASSERT(incResult == SkCodec::kIncompleteInput || incResult == SkCodec::kErrorInInput); - - SkASSERT(rowsDecoded <= info.height()); - this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, - info.height(), rowsDecoded); - return incResult; - } else if (startResult == SkCodec::kIncompleteInput - || startResult == SkCodec::kErrorInInput) { - return SkCodec::kInvalidInput; - } else if (startResult != SkCodec::kUnimplemented) { - return startResult; - } // kUnimplemented means use the old method. - } - - // Start the scanline decode. - AndroidOptions sampledOptions = options; - if (options.fSubset) { - sampledOptions.fSubset = ⊂ - } - SkCodec::Result result = this->codec()->startScanlineDecode(nativeInfo, - &sampledOptions); - if (SkCodec::kIncompleteInput == result || SkCodec::kErrorInInput == result) { - return SkCodec::kInvalidInput; - } else if (SkCodec::kSuccess != result) { - return result; - } - - SkSampler* sampler = this->codec()->getSampler(true); - if (!sampler) { - return SkCodec::kInternalError; - } - - if (sampler->setSampleX(sampleX) != info.width()) { - return SkCodec::kInvalidScale; - } - if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) { - return SkCodec::kInvalidScale; - } - - switch(this->codec()->getScanlineOrder()) { - case SkCodec::kTopDown_SkScanlineOrder: { - if (!this->codec()->skipScanlines(startY)) { - this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, - dstHeight, 0); - return SkCodec::kIncompleteInput; - } - void* pixelPtr = pixels; - for (int y = 0; y < dstHeight; y++) { - if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) { - this->codec()->fillIncompleteImage(info, pixels, rowBytes, - options.fZeroInitialized, dstHeight, y + 1); - return SkCodec::kIncompleteInput; - } - if (y < dstHeight - 1) { - if (!this->codec()->skipScanlines(sampleY - 1)) { - this->codec()->fillIncompleteImage(info, pixels, rowBytes, - options.fZeroInitialized, dstHeight, y + 1); - return SkCodec::kIncompleteInput; - } - } - pixelPtr = SkTAddOffset(pixelPtr, rowBytes); - } - return SkCodec::kSuccess; - } - case SkCodec::kBottomUp_SkScanlineOrder: { - // Note that these modes do not support subsetting. - SkASSERT(0 == subsetY && nativeSize.height() == subsetHeight); - int y; - for (y = 0; y < nativeSize.height(); y++) { - int srcY = this->codec()->nextScanline(); - if (is_coord_necessary(srcY, sampleY, dstHeight)) { - void* pixelPtr = SkTAddOffset(pixels, - rowBytes * get_dst_coord(srcY, sampleY)); - if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) { - break; - } - } else { - if (!this->codec()->skipScanlines(1)) { - break; - } - } - } - - if (nativeSize.height() == y) { - return SkCodec::kSuccess; - } - - // We handle filling uninitialized memory here instead of using this->codec(). - // this->codec() does not know that we are sampling. - const SkImageInfo fillInfo = info.makeWH(info.width(), 1); - for (; y < nativeSize.height(); y++) { - int srcY = this->codec()->outputScanline(y); - if (!is_coord_necessary(srcY, sampleY, dstHeight)) { - continue; - } - - void* rowPtr = SkTAddOffset(pixels, rowBytes * get_dst_coord(srcY, sampleY)); - SkSampler::Fill(fillInfo, rowPtr, rowBytes, options.fZeroInitialized); - } - return SkCodec::kIncompleteInput; - } - default: - SkASSERT(false); - return SkCodec::kUnimplemented; - } -} diff --git a/gfx/skia/skia/src/codec/SkSampledCodec.h b/gfx/skia/skia/src/codec/SkSampledCodec.h deleted file mode 100644 index f30edd055333..000000000000 --- a/gfx/skia/skia/src/codec/SkSampledCodec.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#ifndef SkSampledCodec_DEFINED -#define SkSampledCodec_DEFINED - -#include "include/codec/SkAndroidCodec.h" -#include "include/codec/SkCodec.h" -#include "include/core/SkSize.h" - -#include - -struct SkIRect; -struct SkImageInfo; - -/** - * This class implements the functionality of SkAndroidCodec. Scaling will - * be provided by sampling if it cannot be provided by fCodec. - */ -class SkSampledCodec : public SkAndroidCodec { -public: - explicit SkSampledCodec(SkCodec*); - - ~SkSampledCodec() override {} - -protected: - - SkISize onGetSampledDimensions(int sampleSize) const override; - - bool onGetSupportedSubset(SkIRect* desiredSubset) const override { return true; } - - SkCodec::Result onGetAndroidPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, - const AndroidOptions& options) override; - -private: - /** - * Find the best way to account for native scaling. - * - * Return a size that fCodec can scale to, and adjust sampleSize to finish scaling. - * - * @param sampleSize As an input, the requested sample size. - * As an output, sampling needed after letting fCodec - * scale to the returned dimensions. - * @param nativeSampleSize Optional output parameter. Will be set to the - * effective sample size done by fCodec. - * @return SkISize The size that fCodec should scale to. - */ - SkISize accountForNativeScaling(int* sampleSize, int* nativeSampleSize = nullptr) const; - - /** - * This fulfills the same contract as onGetAndroidPixels(). - * - * We call this function from onGetAndroidPixels() if we have determined - * that fCodec does not support the requested scale, and we need to - * provide the scale by sampling. - */ - SkCodec::Result sampledDecode(const SkImageInfo& info, void* pixels, size_t rowBytes, - const AndroidOptions& options); - - using INHERITED = SkAndroidCodec; -}; -#endif // SkSampledCodec_DEFINED diff --git a/gfx/skia/skia/src/codec/SkSampler.h b/gfx/skia/skia/src/codec/SkSampler.h index ac3b27c441b9..177a99114fda 100644 --- a/gfx/skia/skia/src/codec/SkSampler.h +++ b/gfx/skia/skia/src/codec/SkSampler.h @@ -46,7 +46,7 @@ public: * @param row Row of the image, starting with the first row in the subset. */ bool rowNeeded(int row) const { - return (row - get_start_coord(fSampleY)) % fSampleY == 0; + return (row - SkCodecPriv::GetStartCoord(fSampleY)) % fSampleY == 0; } /** diff --git a/gfx/skia/skia/src/codec/SkScalingCodec.h b/gfx/skia/skia/src/codec/SkScalingCodec.h deleted file mode 100644 index 31308ca47328..000000000000 --- a/gfx/skia/skia/src/codec/SkScalingCodec.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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 SkScalingCodec_DEFINED -#define SkScalingCodec_DEFINED - -#include "include/codec/SkCodec.h" -#include "include/codec/SkEncodedOrigin.h" -#include "include/core/SkScalar.h" -#include "include/core/SkSize.h" -#include "include/core/SkStream.h" -#include "include/private/SkEncodedInfo.h" - -#include -#include -#include - -// Helper class for an SkCodec that supports arbitrary downscaling. -class SkScalingCodec : public SkCodec { -protected: - SkScalingCodec(SkEncodedInfo&& info, XformFormat srcFormat, std::unique_ptr stream, - SkEncodedOrigin origin = kTopLeft_SkEncodedOrigin) - : SkCodec(std::move(info), srcFormat, std::move(stream), origin) {} - - SkISize onGetScaledDimensions(float desiredScale) const override { - SkISize dim = this->dimensions(); - // SkCodec treats zero dimensional images as errors, so the minimum size - // that we will recommend is 1x1. - dim.fWidth = std::max(1, SkScalarRoundToInt(desiredScale * dim.fWidth)); - dim.fHeight = std::max(1, SkScalarRoundToInt(desiredScale * dim.fHeight)); - return dim; - } - - bool onDimensionsSupported(const SkISize& requested) override { - SkISize dim = this->dimensions(); - int w = requested.width(); - int h = requested.height(); - return 1 <= w && w <= dim.width() && 1 <= h && h <= dim.height(); - } -}; - -#endif // SkScalingCodec_DEFINED diff --git a/gfx/skia/skia/src/codec/SkStubHeifDecoderAPI.h b/gfx/skia/skia/src/codec/SkStubHeifDecoderAPI.h deleted file mode 100644 index 3fcdc94a9c19..000000000000 --- a/gfx/skia/skia/src/codec/SkStubHeifDecoderAPI.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkStubHeifDecoderAPI_DEFINED -#define SkStubHeifDecoderAPI_DEFINED - -// This stub implementation of HeifDecoderAPI.h lets us compile SkHeifCodec.cpp -// even when libheif is not available. It, of course, does nothing and fails to decode. - -#include -#include -#include - -enum HeifColorFormat { - kHeifColorFormat_RGB565, - kHeifColorFormat_RGBA_8888, - kHeifColorFormat_BGRA_8888, - kHeifColorFormat_RGBA_1010102, -}; - -struct HeifStream { - virtual ~HeifStream() {} - - virtual size_t read(void*, size_t) = 0; - virtual bool rewind() = 0; - virtual bool seek(size_t) = 0; - virtual bool hasLength() const = 0; - virtual size_t getLength() const = 0; -}; - -struct HeifFrameInfo { - uint32_t mWidth; - uint32_t mHeight; - int32_t mRotationAngle; // Rotation angle, clockwise, should be multiple of 90 - uint32_t mBytesPerPixel; // Number of bytes for one pixel - int64_t mDurationUs; // Duration of the frame in us - std::vector mIccData; // ICC data array -}; - -struct HeifDecoder { - bool init(HeifStream* stream, HeifFrameInfo*) { - delete stream; - return false; - } - - bool getSequenceInfo(HeifFrameInfo* frameInfo, size_t *frameCount) { - return false; - } - - bool decode(HeifFrameInfo*) { - return false; - } - - bool decodeSequence(int frameIndex, HeifFrameInfo* frameInfo) { - return false; - } - - bool setOutputColor(HeifColorFormat) { - return false; - } - - bool getScanline(uint8_t*) { - return false; - } - - int skipScanlines(int) { - return 0; - } - - uint32_t getColorDepth() { - return 0; - } -}; - -static inline HeifDecoder* createHeifDecoder() { return new HeifDecoder; } - -#endif//SkStubHeifDecoderAPI_DEFINED diff --git a/gfx/skia/skia/src/codec/SkSwizzler.cpp b/gfx/skia/skia/src/codec/SkSwizzler.cpp deleted file mode 100644 index f0d8b2b48d00..000000000000 --- a/gfx/skia/skia/src/codec/SkSwizzler.cpp +++ /dev/null @@ -1,1259 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "src/codec/SkSwizzler.h" - -#include "include/core/SkAlphaType.h" -#include "include/core/SkColorPriv.h" -#include "include/core/SkColorType.h" -#include "include/core/SkImageInfo.h" -#include "include/core/SkRect.h" -#include "include/private/SkColorData.h" -#include "include/private/SkEncodedInfo.h" -#include "include/private/base/SkAlign.h" -#include "include/private/base/SkCPUTypes.h" -#include "include/private/base/SkMath.h" -#include "include/private/base/SkTemplates.h" -#include "src/base/SkHalf.h" -#include "src/codec/SkCodecPriv.h" -#include "src/core/SkSwizzlePriv.h" - -#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK - #include "include/android/SkAndroidFrameworkUtils.h" -#endif - -#include - -static void copy(void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, - const SkPMColor ctable[]) { - // This function must not be called if we are sampling. If we are not - // sampling, deltaSrc should equal bpp. - SkASSERT(deltaSrc == bpp); - - memcpy(dst, src + offset, width * bpp); -} - -static void sample1(void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, - const SkPMColor ctable[]) { - src += offset; - uint8_t* dst8 = (uint8_t*) dst; - for (int x = 0; x < width; x++) { - dst8[x] = *src; - src += deltaSrc; - } -} - -static void sample2(void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, - const SkPMColor ctable[]) { - src += offset; - uint16_t* dst16 = (uint16_t*) dst; - for (int x = 0; x < width; x++) { - dst16[x] = *((const uint16_t*) src); - src += deltaSrc; - } -} - -static void sample4(void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, - const SkPMColor ctable[]) { - src += offset; - uint32_t* dst32 = (uint32_t*) dst; - for (int x = 0; x < width; x++) { - dst32[x] = *((const uint32_t*) src); - src += deltaSrc; - } -} - -static void sample6(void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, - const SkPMColor ctable[]) { - src += offset; - uint8_t* dst8 = (uint8_t*) dst; - for (int x = 0; x < width; x++) { - memcpy(dst8, src, 6); - dst8 += 6; - src += deltaSrc; - } -} - -static void sample8(void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, - const SkPMColor ctable[]) { - src += offset; - uint64_t* dst64 = (uint64_t*) dst; - for (int x = 0; x < width; x++) { - dst64[x] = *((const uint64_t*) src); - src += deltaSrc; - } -} - -// kBit -// These routines exclusively choose between white and black - -#define GRAYSCALE_BLACK 0 -#define GRAYSCALE_WHITE 0xFF - - -// same as swizzle_bit_to_index and swizzle_bit_to_n32 except for value assigned to dst[x] -static void swizzle_bit_to_grayscale( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, - int bpp, int deltaSrc, int offset, const SkPMColor* /*ctable*/) { - - uint8_t* SK_RESTRICT dst = (uint8_t*) dstRow; - - // increment src by byte offset and bitIndex by bit offset - src += offset / 8; - int bitIndex = offset % 8; - uint8_t currByte = *src; - - dst[0] = ((currByte >> (7-bitIndex)) & 1) ? GRAYSCALE_WHITE : GRAYSCALE_BLACK; - - for (int x = 1; x < dstWidth; x++) { - int bitOffset = bitIndex + deltaSrc; - bitIndex = bitOffset % 8; - currByte = *(src += bitOffset / 8); - dst[x] = ((currByte >> (7-bitIndex)) & 1) ? GRAYSCALE_WHITE : GRAYSCALE_BLACK; - } -} - -#undef GRAYSCALE_BLACK -#undef GRAYSCALE_WHITE - -// same as swizzle_bit_to_grayscale and swizzle_bit_to_index except for value assigned to dst[x] -static void swizzle_bit_to_n32( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, - int bpp, int deltaSrc, int offset, const SkPMColor* /*ctable*/) { - SkPMColor* SK_RESTRICT dst = (SkPMColor*) dstRow; - - // increment src by byte offset and bitIndex by bit offset - src += offset / 8; - int bitIndex = offset % 8; - uint8_t currByte = *src; - - dst[0] = ((currByte >> (7 - bitIndex)) & 1) ? SK_ColorWHITE : SK_ColorBLACK; - - for (int x = 1; x < dstWidth; x++) { - int bitOffset = bitIndex + deltaSrc; - bitIndex = bitOffset % 8; - currByte = *(src += bitOffset / 8); - dst[x] = ((currByte >> (7 - bitIndex)) & 1) ? SK_ColorWHITE : SK_ColorBLACK; - } -} - -#define RGB565_BLACK 0 -#define RGB565_WHITE 0xFFFF - -static void swizzle_bit_to_565( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, - int bpp, int deltaSrc, int offset, const SkPMColor* /*ctable*/) { - uint16_t* SK_RESTRICT dst = (uint16_t*) dstRow; - - // increment src by byte offset and bitIndex by bit offset - src += offset / 8; - int bitIndex = offset % 8; - uint8_t currByte = *src; - - dst[0] = ((currByte >> (7 - bitIndex)) & 1) ? RGB565_WHITE : RGB565_BLACK; - - for (int x = 1; x < dstWidth; x++) { - int bitOffset = bitIndex + deltaSrc; - bitIndex = bitOffset % 8; - currByte = *(src += bitOffset / 8); - dst[x] = ((currByte >> (7 - bitIndex)) & 1) ? RGB565_WHITE : RGB565_BLACK; - } -} - -#undef RGB565_BLACK -#undef RGB565_WHITE - -static void swizzle_bit_to_f16( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, - int bpp, int deltaSrc, int offset, const SkPMColor* /*ctable*/) { - constexpr uint64_t kWhite = (((uint64_t) SK_Half1) << 0) | - (((uint64_t) SK_Half1) << 16) | - (((uint64_t) SK_Half1) << 32) | - (((uint64_t) SK_Half1) << 48); - constexpr uint64_t kBlack = (((uint64_t) 0) << 0) | - (((uint64_t) 0) << 16) | - (((uint64_t) 0) << 32) | - (((uint64_t) SK_Half1) << 48); - - uint64_t* SK_RESTRICT dst = (uint64_t*) dstRow; - - // increment src by byte offset and bitIndex by bit offset - src += offset / 8; - int bitIndex = offset % 8; - uint8_t currByte = *src; - - dst[0] = ((currByte >> (7 - bitIndex)) & 1) ? kWhite : kBlack; - - for (int x = 1; x < dstWidth; x++) { - int bitOffset = bitIndex + deltaSrc; - bitIndex = bitOffset % 8; - currByte = *(src += bitOffset / 8); - dst[x] = ((currByte >> (7 - bitIndex)) & 1) ? kWhite : kBlack; - } -} - -// kIndex1, kIndex2, kIndex4 - -static void swizzle_small_index_to_565( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, - int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { - - uint16_t* dst = (uint16_t*) dstRow; - src += offset / 8; - int bitIndex = offset % 8; - uint8_t currByte = *src; - const uint8_t mask = (1 << bpp) - 1; - uint8_t index = (currByte >> (8 - bpp - bitIndex)) & mask; - dst[0] = SkPixel32ToPixel16(ctable[index]); - - for (int x = 1; x < dstWidth; x++) { - int bitOffset = bitIndex + deltaSrc; - bitIndex = bitOffset % 8; - currByte = *(src += bitOffset / 8); - index = (currByte >> (8 - bpp - bitIndex)) & mask; - dst[x] = SkPixel32ToPixel16(ctable[index]); - } -} - -static void swizzle_small_index_to_n32( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, - int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { - - SkPMColor* dst = (SkPMColor*) dstRow; - src += offset / 8; - int bitIndex = offset % 8; - uint8_t currByte = *src; - const uint8_t mask = (1 << bpp) - 1; - uint8_t index = (currByte >> (8 - bpp - bitIndex)) & mask; - dst[0] = ctable[index]; - - for (int x = 1; x < dstWidth; x++) { - int bitOffset = bitIndex + deltaSrc; - bitIndex = bitOffset % 8; - currByte = *(src += bitOffset / 8); - index = (currByte >> (8 - bpp - bitIndex)) & mask; - dst[x] = ctable[index]; - } -} - -// kIndex - -static void swizzle_index_to_n32( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, - int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { - - src += offset; - SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; - for (int x = 0; x < dstWidth; x++) { - SkPMColor c = ctable[*src]; - dst[x] = c; - src += deltaSrc; - } -} - -static void swizzle_index_to_n32_skipZ( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, - int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { - - src += offset; - SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; - for (int x = 0; x < dstWidth; x++) { - SkPMColor c = ctable[*src]; - if (c != 0) { - dst[x] = c; - } - src += deltaSrc; - } -} - -static void swizzle_index_to_565( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, - int bytesPerPixel, int deltaSrc, int offset, const SkPMColor ctable[]) { - src += offset; - uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow; - for (int x = 0; x < dstWidth; x++) { - dst[x] = SkPixel32ToPixel16(ctable[*src]); - src += deltaSrc; - } -} - -// kGray - -static void swizzle_gray_to_n32( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, - int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { - - src += offset; - SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; - for (int x = 0; x < dstWidth; x++) { - dst[x] = SkPackARGB32(0xFF, *src, *src, *src); - src += deltaSrc; - } -} - -static void fast_swizzle_gray_to_n32( - void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, - const SkPMColor ctable[]) { - - // This function must not be called if we are sampling. If we are not - // sampling, deltaSrc should equal bpp. - SkASSERT(deltaSrc == bpp); - - // Note that there is no need to distinguish between RGB and BGR. - // Each color channel will get the same value. - SkOpts::gray_to_RGB1((uint32_t*) dst, src + offset, width); -} - -static void swizzle_gray_to_565( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, - int bytesPerPixel, int deltaSrc, int offset, const SkPMColor ctable[]) { - - src += offset; - uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow; - for (int x = 0; x < dstWidth; x++) { - dst[x] = SkPack888ToRGB16(src[0], src[0], src[0]); - src += deltaSrc; - } -} - -// kGrayAlpha - -static void swizzle_grayalpha_to_n32_unpremul( - void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, - const SkPMColor ctable[]) { - - src += offset; - SkPMColor* dst32 = (SkPMColor*) dst; - for (int x = 0; x < width; x++) { - dst32[x] = SkPackARGB32(src[1], src[0], src[0], src[0]); - src += deltaSrc; - } -} - -static void fast_swizzle_grayalpha_to_n32_unpremul( - void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, - const SkPMColor ctable[]) { - - // This function must not be called if we are sampling. If we are not - // sampling, deltaSrc should equal bpp. - SkASSERT(deltaSrc == bpp); - - // Note that there is no need to distinguish between RGB and BGR. - // Each color channel will get the same value. - SkOpts::grayA_to_RGBA((uint32_t*) dst, src + offset, width); -} - -static void swizzle_grayalpha_to_n32_premul( - void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, - const SkPMColor ctable[]) { - - src += offset; - SkPMColor* dst32 = (SkPMColor*) dst; - for (int x = 0; x < width; x++) { - uint8_t pmgray = SkMulDiv255Round(src[1], src[0]); - dst32[x] = SkPackARGB32(src[1], pmgray, pmgray, pmgray); - src += deltaSrc; - } -} - -static void fast_swizzle_grayalpha_to_n32_premul( - void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, - const SkPMColor ctable[]) { - - // This function must not be called if we are sampling. If we are not - // sampling, deltaSrc should equal bpp. - SkASSERT(deltaSrc == bpp); - - // Note that there is no need to distinguish between rgb and bgr. - // Each color channel will get the same value. - SkOpts::grayA_to_rgbA((uint32_t*) dst, src + offset, width); -} - -static void swizzle_grayalpha_to_a8(void* dst, const uint8_t* src, int width, int bpp, - int deltaSrc, int offset, const SkPMColor[]) { - src += offset; - uint8_t* dst8 = (uint8_t*)dst; - for (int x = 0; x < width; ++x) { - dst8[x] = src[1]; // src[0] is gray, ignored - src += deltaSrc; - } -} - -// kBGR - -static void swizzle_bgr_to_565( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, - int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { - - src += offset; - uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow; - for (int x = 0; x < dstWidth; x++) { - dst[x] = SkPack888ToRGB16(src[2], src[1], src[0]); - src += deltaSrc; - } -} - -// kRGB - -static void swizzle_rgb_to_rgba( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, - int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { - - src += offset; - SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; - for (int x = 0; x < dstWidth; x++) { - dst[x] = SkPackARGB_as_RGBA(0xFF, src[0], src[1], src[2]); - src += deltaSrc; - } -} - -static void swizzle_rgb_to_bgra( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, - int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { - - src += offset; - SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; - for (int x = 0; x < dstWidth; x++) { - dst[x] = SkPackARGB_as_BGRA(0xFF, src[0], src[1], src[2]); - src += deltaSrc; - } -} - -static void fast_swizzle_rgb_to_rgba( - void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, - int offset, const SkPMColor ctable[]) { - - // This function must not be called if we are sampling. If we are not - // sampling, deltaSrc should equal bpp. - SkASSERT(deltaSrc == bpp); - - SkOpts::RGB_to_RGB1((uint32_t*) dst, src + offset, width); -} - -static void fast_swizzle_rgb_to_bgra( - void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, - int offset, const SkPMColor ctable[]) { - - // This function must not be called if we are sampling. If we are not - // sampling, deltaSrc should equal bpp. - SkASSERT(deltaSrc == bpp); - - SkOpts::RGB_to_BGR1((uint32_t*) dst, src + offset, width); -} - -static void swizzle_rgb_to_565( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, - int bytesPerPixel, int deltaSrc, int offset, const SkPMColor ctable[]) { - - src += offset; - uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow; - for (int x = 0; x < dstWidth; x++) { - dst[x] = SkPack888ToRGB16(src[0], src[1], src[2]); - src += deltaSrc; - } -} - -// kRGBA - -static void swizzle_rgba_to_rgba_premul( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, - int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { - - src += offset; - SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; - for (int x = 0; x < dstWidth; x++) { - dst[x] = premultiply_argb_as_rgba(src[3], src[0], src[1], src[2]); - src += deltaSrc; - } -} - -static void swizzle_rgba_to_bgra_premul( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, - int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { - - src += offset; - SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; - for (int x = 0; x < dstWidth; x++) { - dst[x] = premultiply_argb_as_bgra(src[3], src[0], src[1], src[2]); - src += deltaSrc; - } -} - -static void fast_swizzle_rgba_to_rgba_premul( - void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, - int offset, const SkPMColor ctable[]) { - - // This function must not be called if we are sampling. If we are not - // sampling, deltaSrc should equal bpp. - SkASSERT(deltaSrc == bpp); - - SkOpts::RGBA_to_rgbA((uint32_t*) dst, (const uint32_t*)(src + offset), width); -} - -static void fast_swizzle_rgba_to_bgra_premul( - void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, - int offset, const SkPMColor ctable[]) { - - // This function must not be called if we are sampling. If we are not - // sampling, deltaSrc should equal bpp. - SkASSERT(deltaSrc == bpp); - - SkOpts::RGBA_to_bgrA((uint32_t*) dst, (const uint32_t*)(src + offset), width); -} - -static void swizzle_rgba_to_bgra_unpremul( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, - int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { - - src += offset; - uint32_t* SK_RESTRICT dst = reinterpret_cast(dstRow); - for (int x = 0; x < dstWidth; x++) { - unsigned alpha = src[3]; - dst[x] = SkPackARGB_as_BGRA(alpha, src[0], src[1], src[2]); - src += deltaSrc; - } -} - -static void fast_swizzle_rgba_to_bgra_unpremul( - void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, - const SkPMColor ctable[]) { - - // This function must not be called if we are sampling. If we are not - // sampling, deltaSrc should equal bpp. - SkASSERT(deltaSrc == bpp); - - SkOpts::RGBA_to_BGRA((uint32_t*) dst, (const uint32_t*)(src + offset), width); -} - -// 16-bits per component kRGB and kRGBA - -static void swizzle_rgb16_to_rgba( - void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, - const SkPMColor ctable[]) { - auto strip16to8 = [](const uint8_t* ptr) { - return 0xFF000000 | (ptr[4] << 16) | (ptr[2] << 8) | ptr[0]; - }; - - src += offset; - uint32_t* dst32 = (uint32_t*) dst; - for (int x = 0; x < width; x++) { - dst32[x] = strip16to8(src); - src += deltaSrc; - } -} - -static void swizzle_rgb16_to_bgra( - void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, - const SkPMColor ctable[]) { - auto strip16to8 = [](const uint8_t* ptr) { - return 0xFF000000 | (ptr[0] << 16) | (ptr[2] << 8) | ptr[4]; - }; - - src += offset; - uint32_t* dst32 = (uint32_t*) dst; - for (int x = 0; x < width; x++) { - dst32[x] = strip16to8(src); - src += deltaSrc; - } -} - -static void swizzle_rgb16_to_565( - void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, - const SkPMColor ctable[]) { - auto strip16to565 = [](const uint8_t* ptr) { - return SkPack888ToRGB16(ptr[0], ptr[2], ptr[4]); - }; - - src += offset; - uint16_t* dst16 = (uint16_t*) dst; - for (int x = 0; x < width; x++) { - dst16[x] = strip16to565(src); - src += deltaSrc; - } -} - -static void swizzle_rgba16_to_rgba_unpremul( - void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, - const SkPMColor ctable[]) { - auto strip16to8 = [](const uint8_t* ptr) { - return (ptr[6] << 24) | (ptr[4] << 16) | (ptr[2] << 8) | ptr[0]; - }; - - src += offset; - uint32_t* dst32 = (uint32_t*) dst; - for (int x = 0; x < width; x++) { - dst32[x] = strip16to8(src); - src += deltaSrc; - } -} - -static void swizzle_rgba16_to_rgba_premul( - void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, - const SkPMColor ctable[]) { - auto stripAndPremul16to8 = [](const uint8_t* ptr) { - return premultiply_argb_as_rgba(ptr[6], ptr[0], ptr[2], ptr[4]); - }; - - src += offset; - uint32_t* dst32 = (uint32_t*) dst; - for (int x = 0; x < width; x++) { - dst32[x] = stripAndPremul16to8(src); - src += deltaSrc; - } -} - -static void swizzle_rgba16_to_bgra_unpremul( - void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, - const SkPMColor ctable[]) { - auto strip16to8 = [](const uint8_t* ptr) { - return (ptr[6] << 24) | (ptr[0] << 16) | (ptr[2] << 8) | ptr[4]; - }; - - src += offset; - uint32_t* dst32 = (uint32_t*) dst; - for (int x = 0; x < width; x++) { - dst32[x] = strip16to8(src); - src += deltaSrc; - } -} - -static void swizzle_rgba16_to_bgra_premul( - void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, - const SkPMColor ctable[]) { - auto stripAndPremul16to8 = [](const uint8_t* ptr) { - return premultiply_argb_as_bgra(ptr[6], ptr[0], ptr[2], ptr[4]); - }; - - src += offset; - uint32_t* dst32 = (uint32_t*) dst; - for (int x = 0; x < width; x++) { - dst32[x] = stripAndPremul16to8(src); - src += deltaSrc; - } -} - -// kCMYK -// -// CMYK is stored as four bytes per pixel. -// -// We will implement a crude conversion from CMYK -> RGB using formulas -// from easyrgb.com. -// -// CMYK -> CMY -// C = C * (1 - K) + K -// M = M * (1 - K) + K -// Y = Y * (1 - K) + K -// -// libjpeg actually gives us inverted CMYK, so we must subtract the -// original terms from 1. -// CMYK -> CMY -// C = (1 - C) * (1 - (1 - K)) + (1 - K) -// M = (1 - M) * (1 - (1 - K)) + (1 - K) -// Y = (1 - Y) * (1 - (1 - K)) + (1 - K) -// -// Simplifying the above expression. -// CMYK -> CMY -// C = 1 - CK -// M = 1 - MK -// Y = 1 - YK -// -// CMY -> RGB -// R = (1 - C) * 255 -// G = (1 - M) * 255 -// B = (1 - Y) * 255 -// -// Therefore the full conversion is below. This can be verified at -// www.rapidtables.com (assuming inverted CMYK). -// CMYK -> RGB -// R = C * K * 255 -// G = M * K * 255 -// B = Y * K * 255 -// -// As a final note, we have treated the CMYK values as if they were on -// a scale from 0-1, when in fact they are 8-bit ints scaling from 0-255. -// We must divide each CMYK component by 255 to obtain the true conversion -// we should perform. -// CMYK -> RGB -// R = C * K / 255 -// G = M * K / 255 -// B = Y * K / 255 -static void swizzle_cmyk_to_rgba( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, - int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { - - src += offset; - SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; - for (int x = 0; x < dstWidth; x++) { - const uint8_t r = SkMulDiv255Round(src[0], src[3]); - const uint8_t g = SkMulDiv255Round(src[1], src[3]); - const uint8_t b = SkMulDiv255Round(src[2], src[3]); - - dst[x] = SkPackARGB_as_RGBA(0xFF, r, g, b); - src += deltaSrc; - } -} - -static void swizzle_cmyk_to_bgra( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, - int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { - - src += offset; - SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; - for (int x = 0; x < dstWidth; x++) { - const uint8_t r = SkMulDiv255Round(src[0], src[3]); - const uint8_t g = SkMulDiv255Round(src[1], src[3]); - const uint8_t b = SkMulDiv255Round(src[2], src[3]); - - dst[x] = SkPackARGB_as_BGRA(0xFF, r, g, b); - src += deltaSrc; - } -} - -static void fast_swizzle_cmyk_to_rgba( - void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, - const SkPMColor ctable[]) { - - // This function must not be called if we are sampling. If we are not - // sampling, deltaSrc should equal bpp. - SkASSERT(deltaSrc == bpp); - - SkOpts::inverted_CMYK_to_RGB1((uint32_t*) dst, (const uint32_t*)(src + offset), width); -} - -static void fast_swizzle_cmyk_to_bgra( - void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, - const SkPMColor ctable[]) { - - // This function must not be called if we are sampling. If we are not - // sampling, deltaSrc should equal bpp. - SkASSERT(deltaSrc == bpp); - - SkOpts::inverted_CMYK_to_BGR1((uint32_t*) dst, (const uint32_t*)(src + offset), width); -} - -static void swizzle_cmyk_to_565( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, - int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { - - src += offset; - uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow; - for (int x = 0; x < dstWidth; x++) { - const uint8_t r = SkMulDiv255Round(src[0], src[3]); - const uint8_t g = SkMulDiv255Round(src[1], src[3]); - const uint8_t b = SkMulDiv255Round(src[2], src[3]); - - dst[x] = SkPack888ToRGB16(r, g, b); - src += deltaSrc; - } -} - -template -void SkSwizzler::SkipLeadingGrayAlphaZerosThen( - void* dst, const uint8_t* src, int width, - int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { - SkASSERT(!ctable); - - const uint16_t* src16 = (const uint16_t*) (src + offset); - uint32_t* dst32 = (uint32_t*) dst; - - // This may miss opportunities to skip when the output is premultiplied, - // e.g. for a src pixel 0x00FF which is not zero but becomes zero after premultiplication. - while (width > 0 && *src16 == 0x0000) { - width--; - dst32++; - src16 += deltaSrc / 2; - } - proc(dst32, (const uint8_t*)src16, width, bpp, deltaSrc, 0, ctable); -} - -template -void SkSwizzler::SkipLeading8888ZerosThen( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, - int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { - SkASSERT(!ctable); - - auto src32 = (const uint32_t*)(src+offset); - auto dst32 = (uint32_t*)dstRow; - - // This may miss opportunities to skip when the output is premultiplied, - // e.g. for a src pixel 0x00FFFFFF which is not zero but becomes zero after premultiplication. - while (dstWidth > 0 && *src32 == 0x00000000) { - dstWidth--; - dst32++; - src32 += deltaSrc/4; - } - proc(dst32, (const uint8_t*)src32, dstWidth, bpp, deltaSrc, 0, ctable); -} - -std::unique_ptr SkSwizzler::MakeSimple(int srcBPP, - const SkImageInfo& dstInfo, - const SkCodec::Options& options, - const SkIRect* frame) { - RowProc proc = nullptr; - switch (srcBPP) { - case 1: // kGray_8_SkColorType - proc = &sample1; - break; - case 2: // kRGB_565_SkColorType - proc = &sample2; - break; - case 4: // kRGBA_8888_SkColorType - // kBGRA_8888_SkColorType - // kRGBA_1010102_SkColorType - proc = &sample4; - break; - case 6: // 16 bit PNG no alpha - proc = &sample6; - break; - case 8: // 16 bit PNG with alpha - proc = &sample8; - break; - default: - return nullptr; - } - - return Make(dstInfo, - ©, - proc, - nullptr /*ctable*/, - srcBPP, - dstInfo.bytesPerPixel(), - options, - frame); -} - -std::unique_ptr SkSwizzler::Make(const SkEncodedInfo& encodedInfo, - const SkPMColor* ctable, - const SkImageInfo& dstInfo, - const SkCodec::Options& options, - const SkIRect* frame) { - if (SkEncodedInfo::kPalette_Color == encodedInfo.color() && nullptr == ctable) { - return nullptr; - } - - RowProc fastProc = nullptr; - RowProc proc = nullptr; - SkCodec::ZeroInitialized zeroInit = options.fZeroInitialized; - const bool premultiply = (SkEncodedInfo::kOpaque_Alpha != encodedInfo.alpha()) && - (kPremul_SkAlphaType == dstInfo.alphaType()); - - switch (encodedInfo.color()) { - case SkEncodedInfo::kGray_Color: - switch (encodedInfo.bitsPerComponent()) { - case 1: - switch (dstInfo.colorType()) { - case kRGBA_8888_SkColorType: - case kBGRA_8888_SkColorType: - proc = &swizzle_bit_to_n32; - break; - case kRGB_565_SkColorType: - proc = &swizzle_bit_to_565; - break; - case kGray_8_SkColorType: - proc = &swizzle_bit_to_grayscale; - break; - case kRGBA_F16_SkColorType: - proc = &swizzle_bit_to_f16; - break; - default: - return nullptr; - } - break; - case 8: - switch (dstInfo.colorType()) { - case kRGBA_8888_SkColorType: - case kBGRA_8888_SkColorType: - proc = &swizzle_gray_to_n32; - fastProc = &fast_swizzle_gray_to_n32; - break; - case kGray_8_SkColorType: - proc = &sample1; - fastProc = © - break; - case kRGB_565_SkColorType: - proc = &swizzle_gray_to_565; - break; - default: - return nullptr; - } - break; - default: - return nullptr; - } - break; - case SkEncodedInfo::kXAlpha_Color: - case SkEncodedInfo::kGrayAlpha_Color: - switch (dstInfo.colorType()) { - case kRGBA_8888_SkColorType: - case kBGRA_8888_SkColorType: - if (premultiply) { - if (SkCodec::kYes_ZeroInitialized == zeroInit) { - proc = &SkipLeadingGrayAlphaZerosThen - ; - fastProc = &SkipLeadingGrayAlphaZerosThen - ; - } else { - proc = &swizzle_grayalpha_to_n32_premul; - fastProc = &fast_swizzle_grayalpha_to_n32_premul; - } - } else { - if (SkCodec::kYes_ZeroInitialized == zeroInit) { - proc = &SkipLeadingGrayAlphaZerosThen - ; - fastProc = &SkipLeadingGrayAlphaZerosThen - ; - } else { - proc = &swizzle_grayalpha_to_n32_unpremul; - fastProc = &fast_swizzle_grayalpha_to_n32_unpremul; - } - } - break; - case kAlpha_8_SkColorType: - proc = &swizzle_grayalpha_to_a8; - break; - default: - return nullptr; - } - break; - case SkEncodedInfo::kPalette_Color: - // We assume that the color table is premultiplied and swizzled - // as desired. - switch (encodedInfo.bitsPerComponent()) { - case 1: - case 2: - case 4: - switch (dstInfo.colorType()) { - case kRGBA_8888_SkColorType: - case kBGRA_8888_SkColorType: - proc = &swizzle_small_index_to_n32; - break; - case kRGB_565_SkColorType: - proc = &swizzle_small_index_to_565; - break; - default: - return nullptr; - } - break; - case 8: - switch (dstInfo.colorType()) { - case kRGBA_8888_SkColorType: - case kBGRA_8888_SkColorType: - case kBGR_101010x_XR_SkColorType: - if (SkCodec::kYes_ZeroInitialized == zeroInit) { - proc = &swizzle_index_to_n32_skipZ; - } else { - proc = &swizzle_index_to_n32; - } - break; - case kRGB_565_SkColorType: - proc = &swizzle_index_to_565; - break; - default: - return nullptr; - } - break; - default: - return nullptr; - } - break; - case SkEncodedInfo::k565_Color: - // Treat 565 exactly like RGB (since it's still encoded as 8 bits per component). - // We just mark as 565 when we have a hint that there are only 5/6/5 "significant" - // bits in each channel. - case SkEncodedInfo::kRGB_Color: - switch (dstInfo.colorType()) { - case kRGBA_8888_SkColorType: - if (16 == encodedInfo.bitsPerComponent()) { - proc = &swizzle_rgb16_to_rgba; - break; - } - - SkASSERT(8 == encodedInfo.bitsPerComponent()); - proc = &swizzle_rgb_to_rgba; - fastProc = &fast_swizzle_rgb_to_rgba; - break; - case kBGRA_8888_SkColorType: - if (16 == encodedInfo.bitsPerComponent()) { - proc = &swizzle_rgb16_to_bgra; - break; - } - - SkASSERT(8 == encodedInfo.bitsPerComponent()); - proc = &swizzle_rgb_to_bgra; - fastProc = &fast_swizzle_rgb_to_bgra; - break; - case kRGB_565_SkColorType: - if (16 == encodedInfo.bitsPerComponent()) { - proc = &swizzle_rgb16_to_565; - break; - } - - proc = &swizzle_rgb_to_565; - break; - default: - return nullptr; - } - break; - case SkEncodedInfo::kRGBA_Color: - switch (dstInfo.colorType()) { - case kRGBA_8888_SkColorType: - if (16 == encodedInfo.bitsPerComponent()) { - proc = premultiply ? &swizzle_rgba16_to_rgba_premul : - &swizzle_rgba16_to_rgba_unpremul; - break; - } - - SkASSERT(8 == encodedInfo.bitsPerComponent()); - if (premultiply) { - if (SkCodec::kYes_ZeroInitialized == zeroInit) { - proc = &SkipLeading8888ZerosThen; - fastProc = &SkipLeading8888ZerosThen - ; - } else { - proc = &swizzle_rgba_to_rgba_premul; - fastProc = &fast_swizzle_rgba_to_rgba_premul; - } - } else { - if (SkCodec::kYes_ZeroInitialized == zeroInit) { - proc = &SkipLeading8888ZerosThen; - fastProc = &SkipLeading8888ZerosThen; - } else { - proc = &sample4; - fastProc = © - } - } - break; - case kBGRA_8888_SkColorType: - if (16 == encodedInfo.bitsPerComponent()) { - proc = premultiply ? &swizzle_rgba16_to_bgra_premul : - &swizzle_rgba16_to_bgra_unpremul; - break; - } - - SkASSERT(8 == encodedInfo.bitsPerComponent()); - if (premultiply) { - if (SkCodec::kYes_ZeroInitialized == zeroInit) { - proc = &SkipLeading8888ZerosThen; - fastProc = &SkipLeading8888ZerosThen - ; - } else { - proc = &swizzle_rgba_to_bgra_premul; - fastProc = &fast_swizzle_rgba_to_bgra_premul; - } - } else { - if (SkCodec::kYes_ZeroInitialized == zeroInit) { - proc = &SkipLeading8888ZerosThen; - fastProc = &SkipLeading8888ZerosThen - ; - } else { - proc = &swizzle_rgba_to_bgra_unpremul; - fastProc = &fast_swizzle_rgba_to_bgra_unpremul; - } - } - break; - default: - return nullptr; - } - break; - case SkEncodedInfo::kBGR_Color: - switch (dstInfo.colorType()) { - case kBGRA_8888_SkColorType: - proc = &swizzle_rgb_to_rgba; - fastProc = &fast_swizzle_rgb_to_rgba; - break; - case kRGBA_8888_SkColorType: - proc = &swizzle_rgb_to_bgra; - fastProc = &fast_swizzle_rgb_to_bgra; - break; - case kRGB_565_SkColorType: - proc = &swizzle_bgr_to_565; - break; - default: - return nullptr; - } - break; - case SkEncodedInfo::kBGRX_Color: - switch (dstInfo.colorType()) { - case kBGRA_8888_SkColorType: - proc = &swizzle_rgb_to_rgba; - break; - case kRGBA_8888_SkColorType: - proc = &swizzle_rgb_to_bgra; - break; - case kRGB_565_SkColorType: - proc = &swizzle_bgr_to_565; - break; - default: - return nullptr; - } - break; - case SkEncodedInfo::kBGRA_Color: - switch (dstInfo.colorType()) { - case kBGRA_8888_SkColorType: - if (premultiply) { - if (SkCodec::kYes_ZeroInitialized == zeroInit) { - proc = &SkipLeading8888ZerosThen; - fastProc = &SkipLeading8888ZerosThen - ; - } else { - proc = &swizzle_rgba_to_rgba_premul; - fastProc = &fast_swizzle_rgba_to_rgba_premul; - } - } else { - if (SkCodec::kYes_ZeroInitialized == zeroInit) { - proc = &SkipLeading8888ZerosThen; - fastProc = &SkipLeading8888ZerosThen; - } else { - proc = &sample4; - fastProc = © - } - } - break; - case kRGBA_8888_SkColorType: - if (premultiply) { - if (SkCodec::kYes_ZeroInitialized == zeroInit) { - proc = &SkipLeading8888ZerosThen; - fastProc = &SkipLeading8888ZerosThen - ; - } else { - proc = &swizzle_rgba_to_bgra_premul; - fastProc = &fast_swizzle_rgba_to_bgra_premul; - } - } else { - if (SkCodec::kYes_ZeroInitialized == zeroInit) { - proc = &SkipLeading8888ZerosThen; - fastProc = &SkipLeading8888ZerosThen - ; - } else { - proc = &swizzle_rgba_to_bgra_unpremul; - fastProc = &fast_swizzle_rgba_to_bgra_unpremul; - } - } - break; - default: - return nullptr; - } - break; - case SkEncodedInfo::kInvertedCMYK_Color: - switch (dstInfo.colorType()) { - case kRGBA_8888_SkColorType: - proc = &swizzle_cmyk_to_rgba; - fastProc = &fast_swizzle_cmyk_to_rgba; - break; - case kBGRA_8888_SkColorType: - proc = &swizzle_cmyk_to_bgra; - fastProc = &fast_swizzle_cmyk_to_bgra; - break; - case kRGB_565_SkColorType: - proc = &swizzle_cmyk_to_565; - break; - default: - return nullptr; - } - break; - default: - return nullptr; - } - - // Store bpp in bytes if it is an even multiple, otherwise use bits - uint8_t bitsPerPixel = encodedInfo.bitsPerPixel(); - int srcBPP = SkIsAlign8(bitsPerPixel) ? bitsPerPixel / 8 : bitsPerPixel; - int dstBPP = dstInfo.bytesPerPixel(); - return Make(dstInfo, fastProc, proc, ctable, srcBPP, dstBPP, options, frame); -} - -std::unique_ptr SkSwizzler::Make(const SkImageInfo& dstInfo, - RowProc fastProc, RowProc proc, const SkPMColor* ctable, int srcBPP, - int dstBPP, const SkCodec::Options& options, const SkIRect* frame) { - int srcOffset = 0; - int srcWidth = dstInfo.width(); - int dstOffset = 0; - int dstWidth = srcWidth; - if (options.fSubset) { - // We do not currently support subset decodes for image types that may have - // frames (gif). - SkASSERT(!frame); - srcOffset = options.fSubset->left(); - srcWidth = options.fSubset->width(); - dstWidth = srcWidth; - } else if (frame) { - dstOffset = frame->left(); - srcWidth = frame->width(); - } - - return std::unique_ptr(new SkSwizzler(fastProc, proc, ctable, srcOffset, srcWidth, - dstOffset, dstWidth, srcBPP, dstBPP)); -} - -SkSwizzler::SkSwizzler(RowProc fastProc, RowProc proc, const SkPMColor* ctable, int srcOffset, - int srcWidth, int dstOffset, int dstWidth, int srcBPP, int dstBPP) - : fFastProc(fastProc) - , fSlowProc(proc) - , fActualProc(fFastProc ? fFastProc : fSlowProc) - , fColorTable(ctable) - , fSrcOffset(srcOffset) - , fDstOffset(dstOffset) - , fSrcOffsetUnits(srcOffset * srcBPP) - , fDstOffsetBytes(dstOffset * dstBPP) - , fSrcWidth(srcWidth) - , fDstWidth(dstWidth) - , fSwizzleWidth(srcWidth) - , fAllocatedWidth(dstWidth) - , fSampleX(1) - , fSrcBPP(srcBPP) - , fDstBPP(dstBPP) -{} - -int SkSwizzler::onSetSampleX(int sampleX) { - SkASSERT(sampleX > 0); - - fSampleX = sampleX; - fDstOffsetBytes = (fDstOffset / sampleX) * fDstBPP; - fSwizzleWidth = get_scaled_dimension(fSrcWidth, sampleX); - fAllocatedWidth = get_scaled_dimension(fDstWidth, sampleX); - - int frameSampleX = sampleX; - if (fSrcWidth < fDstWidth) { - // Although SkSampledCodec adjusted sampleX so that it will never be - // larger than the width of the image (or subset, if applicable), it - // doesn't account for the width of a subset frame (i.e. gif). As a - // result, get_start_coord(sampleX) could result in fSrcOffsetUnits - // being wider than fSrcWidth. Compute a sampling rate based on the - // frame width to ensure that fSrcOffsetUnits is sensible. - frameSampleX = fSrcWidth / fSwizzleWidth; - } - fSrcOffsetUnits = (get_start_coord(frameSampleX) + fSrcOffset) * fSrcBPP; - - if (fDstOffsetBytes > 0) { - const size_t dstSwizzleBytes = fSwizzleWidth * fDstBPP; - const size_t dstAllocatedBytes = fAllocatedWidth * fDstBPP; - if (fDstOffsetBytes + dstSwizzleBytes > dstAllocatedBytes) { -#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK - SkAndroidFrameworkUtils::SafetyNetLog("118143775"); -#endif - SkASSERT(dstSwizzleBytes <= dstAllocatedBytes); - fDstOffsetBytes = dstAllocatedBytes - dstSwizzleBytes; - } - } - - // The optimized swizzler functions do not support sampling. Sampled swizzles - // are already fast because they skip pixels. We haven't seen a situation - // where speeding up sampling has a significant impact on total decode time. - if (1 == fSampleX && fFastProc) { - fActualProc = fFastProc; - } else { - fActualProc = fSlowProc; - } - - return fAllocatedWidth; -} - -void SkSwizzler::swizzle(void* dst, const uint8_t* SK_RESTRICT src) { - SkASSERT(nullptr != dst && nullptr != src); - fActualProc(SkTAddOffset(dst, fDstOffsetBytes), src, fSwizzleWidth, fSrcBPP, - fSampleX * fSrcBPP, fSrcOffsetUnits, fColorTable); -} diff --git a/gfx/skia/skia/src/codec/SkSwizzler.h b/gfx/skia/skia/src/codec/SkSwizzler.h deleted file mode 100644 index eb4e61d2d280..000000000000 --- a/gfx/skia/skia/src/codec/SkSwizzler.h +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkSwizzler_DEFINED -#define SkSwizzler_DEFINED - -#include "include/codec/SkCodec.h" -#include "include/core/SkColor.h" -#include "include/core/SkTypes.h" -#include "src/codec/SkSampler.h" - -#include -#include -#include - -struct SkEncodedInfo; -struct SkIRect; -struct SkImageInfo; - -class SkSwizzler : public SkSampler { -public: - /** - * Create a new SkSwizzler. - * @param encodedInfo Description of the format of the encoded data. - * @param ctable Unowned pointer to an array of up to 256 colors for an - * index source. - * @param dstInfo Describes the destination. - * @param options Contains partial scanline information and whether the dst is zero- - * initialized. - * @param frame Is non-NULL if the source pixels are part of an image - * frame that is a subset of the full image. - * - * Note that a deeper discussion of partial scanline subsets and image frame - * subsets is below. Currently, we do not support both simultaneously. If - * options->fSubset is non-NULL, frame must be NULL. - * - * @return A new SkSwizzler or nullptr on failure. - */ - static std::unique_ptr Make(const SkEncodedInfo& encodedInfo, - const SkPMColor* ctable, const SkImageInfo& dstInfo, const SkCodec::Options&, - const SkIRect* frame = nullptr); - - /** - * Create a simplified swizzler that does not need to do format conversion. The swizzler - * only needs to sample and/or subset. - * - * @param srcBPP Bytes per pixel of the source. - * @param dstInfo Describes the destination. - * @param options Contains partial scanline information and whether the dst is zero- - * initialized. - * @return A new SkSwizzler or nullptr on failure. - */ - static std::unique_ptr MakeSimple(int srcBPP, - const SkImageInfo& dstInfo, - const SkCodec::Options&, - const SkIRect* frame = nullptr); - - /** - * Swizzle a line. Generally this will be called height times, once - * for each row of source. - * By allowing the caller to pass in the dst pointer, we give the caller - * flexibility to use the swizzler even when the encoded data does not - * store the rows in order. This also improves usability for scaled and - * subset decodes. - * @param dst Where we write the output. - * @param src The next row of the source data. - */ - void swizzle(void* dst, const uint8_t* SK_RESTRICT src); - - int fillWidth() const override { - return fAllocatedWidth; - } - - /** - * If fSampleX > 1, the swizzler is sampling every fSampleX'th pixel and - * discarding the rest. - * - * This getter is currently used by SkBmpStandardCodec for Bmp-in-Ico decodes. - * Ideally, the subclasses of SkCodec would have no knowledge of sampling, but - * this allows us to apply a transparency mask to pixels after swizzling. - */ - int sampleX() const { return fSampleX; } - - /** - * Returns the actual number of pixels written to destination memory, taking - * scaling, subsetting, and partial frames into account. - */ - int swizzleWidth() const { return fSwizzleWidth; } - - /** - * Returns the byte offset at which we write to destination memory, taking - * scaling, subsetting, and partial frames into account. - */ - size_t swizzleOffsetBytes() const { return fDstOffsetBytes; } - -private: - - /** - * Method for converting raw data to Skia pixels. - * @param dstRow Row in which to write the resulting pixels. - * @param src Row of src data, in format specified by SrcConfig - * @param dstWidth Width in pixels of the destination - * @param bpp if bitsPerPixel % 8 == 0, deltaSrc is bytesPerPixel - * else, deltaSrc is bitsPerPixel - * @param deltaSrc bpp * sampleX - * @param ctable Colors (used for kIndex source). - * @param offset The offset before the first pixel to sample. - Is in bytes or bits based on what deltaSrc is in. - */ - typedef void (*RowProc)(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int dstWidth, int bpp, int deltaSrc, int offset, - const SkPMColor ctable[]); - - template - static void SkipLeading8888ZerosThen(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int dstWidth, int bpp, int deltaSrc, int offset, - const SkPMColor ctable[]); - - template - static void SkipLeadingGrayAlphaZerosThen(void* dst, const uint8_t* src, int width, int bpp, - int deltaSrc, int offset, const SkPMColor ctable[]); - - // May be NULL. We have not implemented optimized functions for all supported transforms. - const RowProc fFastProc; - // Always non-NULL. Supports sampling. - const RowProc fSlowProc; - // The actual RowProc we are using. This depends on if fFastProc is non-NULL and - // whether or not we are sampling. - RowProc fActualProc; - - const SkPMColor* fColorTable; // Unowned pointer - - // Subset Swizzles - // There are two types of subset swizzles that we support. We do not - // support both at the same time. - // TODO: If we want to support partial scanlines for gifs (which may - // use frame subsets), we will need to support both subsetting - // modes at the same time. - // (1) Partial Scanlines - // The client only wants to write a subset of the source pixels - // to the destination. This subset is specified to CreateSwizzler - // using options->fSubset. We will store subset information in - // the following fields. - // - // fSrcOffset: The starting pixel of the source. - // fSrcOffsetUnits: Derived from fSrcOffset with two key - // differences: - // (1) This takes the size of source pixels into - // account by multiplying by fSrcBPP. This may - // be measured in bits or bytes depending on - // which is natural for the SrcConfig. - // (2) If we are sampling, this will be larger - // than fSrcOffset * fSrcBPP, since sampling - // implies that we will skip some pixels. - // fDstOffset: Will be zero. There is no destination offset - // for this type of subset. - // fDstOffsetBytes: Will be zero. - // fSrcWidth: The width of the desired subset of source - // pixels, before any sampling is performed. - // fDstWidth: Will be equal to fSrcWidth, since this is also - // calculated before any sampling is performed. - // For this type of subset, the destination width - // matches the desired subset of the source. - // fSwizzleWidth: The actual number of pixels that will be - // written by the RowProc. This is a scaled - // version of fSrcWidth/fDstWidth. - // fAllocatedWidth: Will be equal to fSwizzleWidth. For this type - // of subset, the number of pixels written is the - // same as the actual width of the destination. - // (2) Frame Subset - // The client will decode the entire width of the source into a - // subset of destination memory. This subset is specified to - // CreateSwizzler in the "frame" parameter. We store subset - // information in the following fields. - // - // fSrcOffset: Will be zero. The starting pixel of the source. - // fSrcOffsetUnits: Will only be non-zero if we are sampling, - // since sampling implies that we will skip some - // pixels. Note that this is measured in bits - // or bytes depending on which is natural for - // SrcConfig. - // fDstOffset: First pixel to write in destination. - // fDstOffsetBytes: fDstOffset * fDstBPP. - // fSrcWidth: The entire width of the source pixels, before - // any sampling is performed. - // fDstWidth: The entire width of the destination memory, - // before any sampling is performed. - // fSwizzleWidth: The actual number of pixels that will be - // written by the RowProc. This is a scaled - // version of fSrcWidth. - // fAllocatedWidth: The actual number of pixels in destination - // memory. This is a scaled version of - // fDstWidth. - // - // If we are not subsetting, these fields are more straightforward. - // fSrcOffset = fDstOffet = fDstOffsetBytes = 0 - // fSrcOffsetUnits may be non-zero (we will skip the first few pixels when sampling) - // fSrcWidth = fDstWidth = Full original width - // fSwizzleWidth = fAllcoatedWidth = Scaled width (if we are sampling) - const int fSrcOffset; - const int fDstOffset; - int fSrcOffsetUnits; - int fDstOffsetBytes; - const int fSrcWidth; - const int fDstWidth; - int fSwizzleWidth; - int fAllocatedWidth; - - int fSampleX; // Step between X samples - const int fSrcBPP; // Bits/bytes per pixel for the SrcConfig - // if bitsPerPixel % 8 == 0 - // fBPP is bytesPerPixel - // else - // fBPP is bitsPerPixel - const int fDstBPP; // Bytes per pixel for the destination color type - - SkSwizzler(RowProc fastProc, RowProc proc, const SkPMColor* ctable, int srcOffset, - int srcWidth, int dstOffset, int dstWidth, int srcBPP, int dstBPP); - static std::unique_ptr Make(const SkImageInfo& dstInfo, RowProc fastProc, - RowProc proc, const SkPMColor* ctable, int srcBPP, int dstBPP, - const SkCodec::Options& options, const SkIRect* frame); - - int onSetSampleX(int) override; - -}; - -#endif // SkSwizzler_DEFINED diff --git a/gfx/skia/skia/src/codec/SkTiffUtility.cpp b/gfx/skia/skia/src/codec/SkTiffUtility.cpp deleted file mode 100644 index e676c3d9ccdc..000000000000 --- a/gfx/skia/skia/src/codec/SkTiffUtility.cpp +++ /dev/null @@ -1,296 +0,0 @@ -/* - * Copyright 2023 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "src/codec/SkTiffUtility.h" - -#include "include/core/SkData.h" -#include "src/codec/SkCodecPriv.h" - -#include -#include - -namespace SkTiff { - -bool ImageFileDirectory::IsValidType(uint16_t type) { return type >= 1 && type <= 12; } - -size_t ImageFileDirectory::BytesForType(uint16_t type) { - switch (type) { - case kTypeUnsignedByte: - return 1; - case kTypeAsciiString: - return 1; - case kTypeUnsignedShort: - return kSizeShort; - case kTypeUnsignedLong: - return kSizeLong; - case kTypeUnsignedRational: - return 8; - case kTypeSignedByte: - return 1; - case kTypeUndefined: - return 1; - case kTypeSignedShort: - return kSizeShort; - case kTypeSignedLong: - return kSizeLong; - case kTypeSignedRational: - return 8; - case kTypeSingleFloat: - return 4; - case kTypeDoubleFloat: - return 8; - } - return 0; -} - -// Helper function for computing the address of an entry. -static const uint8_t* get_entry_address(const SkData* data, - uint32_t ifdOffset, - uint16_t entryIndex) { - return data->bytes() + // Base address - ifdOffset + // IFD offset - kSizeShort + // IFD number of entries - kSizeEntry * entryIndex; // Entries -} - -// Return true if the IFD starting at |ifdOffset| contains valid number of entries (that doesn't -// overrun |data|). -static bool validate_ifd(const SkData* data, - bool littleEndian, - uint32_t ifdOffset, - bool allowTruncated, - uint16_t* outNumEntries, - uint32_t* outNextIfdOffset) { - const uint8_t* dataCurrent = data->bytes(); - size_t dataSize = data->size(); - - // Seek to the IFD offset. - if (dataSize < ifdOffset) { - SkCodecPrintf("IFD offset is too large.\n"); - return false; - } - dataCurrent += ifdOffset; - dataSize -= ifdOffset; - - // Read the number of entries. - if (dataSize < kSizeShort) { - SkCodecPrintf("Insufficient space to store number of entries.\n"); - return false; - } - uint16_t numEntries = get_endian_short(dataCurrent, littleEndian); - dataCurrent += kSizeShort; - dataSize -= kSizeShort; - - // Check that there is enough space for all entries. - if (dataSize < kSizeEntry * numEntries) { - SkCodecPrintf("Insufficient space (%u) to store all %u entries.\n", - static_cast(data->size()), - numEntries); - if (allowTruncated) { - // Set the number of entries to the number of entries that can be fully read, and set - // the next IFD offset to 0 (indicating that there is no next IFD). - *outNumEntries = dataSize / kSizeEntry; - *outNextIfdOffset = 0; - return true; - } - return false; - } - - // Save the number of entries. - *outNumEntries = numEntries; - - // Seek past the entries. - dataCurrent += kSizeEntry * numEntries; - dataSize -= kSizeEntry * numEntries; - - // Read the next IFD offset. - if (dataSize < kSizeLong) { - SkCodecPrintf("Insufficient space to store next IFD offset.\n"); - if (allowTruncated) { - // Set the next IFD offset to 0 (indicating that there is no next IFD). - *outNextIfdOffset = 0; - return true; - } - return false; - } - - // Save the next IFD offset. - *outNextIfdOffset = get_endian_int(dataCurrent, littleEndian); - return true; -} - -bool ImageFileDirectory::ParseHeader(const SkData* data, - bool* outLittleEndian, - uint32_t* outIfdOffset) { - // Read the endianness (4 bytes) and IFD offset (4 bytes). - if (data->size() < 8) { - SkCodecPrintf("Tiff header must be at least 8 bytes.\n"); - return false; - } - if (!is_valid_endian_marker(data->bytes(), outLittleEndian)) { - SkCodecPrintf("Tiff header had invalid endian marker 0x%x,0x%x,0x%x,0x%x.\n", - data->bytes()[0], - data->bytes()[1], - data->bytes()[2], - data->bytes()[3]); - return false; - } - *outIfdOffset = get_endian_int(data->bytes() + 4, *outLittleEndian); - return true; -} - -std::unique_ptr ImageFileDirectory::MakeFromOffset(sk_sp data, - bool littleEndian, - uint32_t ifdOffset, - bool allowTruncated) { - uint16_t numEntries = 0; - uint32_t nextOffset = 0; - if (!validate_ifd( - data.get(), littleEndian, ifdOffset, allowTruncated, &numEntries, &nextOffset)) { - SkCodecPrintf("Failed to validate IFD.\n"); - return nullptr; - } - return std::unique_ptr(new ImageFileDirectory( - std::move(data), littleEndian, ifdOffset, numEntries, nextOffset)); -} - -ImageFileDirectory::ImageFileDirectory(sk_sp data, - bool littleEndian, - uint32_t offset, - uint16_t numEntries, - uint32_t nextIfdOffset) - : fData(std::move(data)) - , fLittleEndian(littleEndian) - , fOffset(offset) - , fNumEntries(numEntries) - , fNextIfdOffset(nextIfdOffset) {} - -uint16_t ImageFileDirectory::getEntryTag(uint16_t entryIndex) const { - const uint8_t* entry = get_entry_address(fData.get(), fOffset, entryIndex); - return get_endian_short(entry, fLittleEndian); -} - -bool ImageFileDirectory::getEntryRawData(uint16_t entryIndex, - uint16_t* outTag, - uint16_t* outType, - uint32_t* outCount, - const uint8_t** outData, - size_t* outDataSize) const { - const uint8_t* entry = get_entry_address(fData.get(), fOffset, entryIndex); - - // Read the tag - const uint16_t tag = get_endian_short(entry, fLittleEndian); - entry += kSizeShort; - - // Read the type. - const uint16_t type = get_endian_short(entry, fLittleEndian); - entry += kSizeShort; - if (!IsValidType(type)) { - return false; - } - - // Read the count. - const uint32_t count = get_endian_int(entry, fLittleEndian); - entry += kSizeLong; - - // If the entry fits in the remaining 4 bytes, use that. - const size_t entryDataBytes = BytesForType(type) * count; - const uint8_t* entryData = nullptr; - if (entryDataBytes <= kSizeLong) { - entryData = entry; - } else { - // Otherwise, the next 4 bytes specify an offset where the data can be found. - const uint32_t entryDataOffset = get_endian_int(entry, fLittleEndian); - if (fData->size() < entryDataOffset || fData->size() - entryDataOffset < entryDataBytes) { - return false; - } - entryData = fData->bytes() + entryDataOffset; - } - - if (outTag) *outTag = tag; - if (outType) *outType = type; - if (outCount) *outCount = count; - if (outData) *outData = entryData; - if (outDataSize) *outDataSize = entryDataBytes; - return true; -} - -sk_sp ImageFileDirectory::getEntryUndefinedData(uint16_t entryIndex) const { - uint16_t type = 0; - uint32_t count = 0; - const uint8_t* data = nullptr; - size_t size = 0; - if (!getEntryRawData(entryIndex, nullptr, &type, &count, &data, &size)) { - return nullptr; - } - if (type != kTypeUndefined) { - return nullptr; - } - return SkData::MakeSubset(fData.get(), data - fData->bytes(), size); -} - -bool ImageFileDirectory::getEntryValuesGeneric(uint16_t entryIndex, - uint16_t type, - uint32_t count, - void* values) const { - uint16_t entryType = 0; - uint32_t entryCount = 0; - const uint8_t* entryData = nullptr; - if (!getEntryRawData(entryIndex, nullptr, &entryType, &entryCount, &entryData, nullptr)) { - return false; - } - if (type != entryType) { - return false; - } - if (count != entryCount) { - return false; - } - for (uint32_t i = 0; i < count; ++i) { - const uint8_t* data = entryData + i * BytesForType(kTypeUnsignedLong); - switch (type) { - case kTypeUnsignedShort: - reinterpret_cast(values)[i] = get_endian_short(data, fLittleEndian); - break; - case kTypeUnsignedLong: - reinterpret_cast(values)[i] = get_endian_int(data, fLittleEndian); - break; - case kTypeSignedRational: { - uint32_t numerator = get_endian_int(data, fLittleEndian); - uint32_t denominator = get_endian_int(data + kSizeLong, fLittleEndian); - if (denominator == 0) { - // The TIFF specification does not indicate a behavior when the denominator is - // zero. The behavior of returning zero for a denominator of zero is a - // preservation of the behavior introduced in https://crrev.com/767874. - reinterpret_cast(values)[i] = 0; - } else { - reinterpret_cast(values)[i] = - numerator / static_cast(denominator); - } - break; - } - case kTypeUnsignedRational: { - uint32_t numerator = get_endian_int(data, fLittleEndian); - uint32_t denominator = get_endian_int(data + kSizeLong, fLittleEndian); - if (denominator == 0) { - // See comments in kTypeSignedRational. - reinterpret_cast(values)[i] = 0.f; - } else { - reinterpret_cast(values)[i] = - numerator / static_cast(denominator); - } - break; - } - default: - SkCodecPrintf("Unsupported type %u\n", type); - return false; - break; - } - } - return true; -} - -} // namespace SkTiff diff --git a/gfx/skia/skia/src/codec/SkTiffUtility.h b/gfx/skia/skia/src/codec/SkTiffUtility.h deleted file mode 100644 index 9da3c0417fa1..000000000000 --- a/gfx/skia/skia/src/codec/SkTiffUtility.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * 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 SkTiffUtility_codec_DEFINED -#define SkTiffUtility_codec_DEFINED - -#include "include/core/SkData.h" -#include "include/core/SkRefCnt.h" - -#include -#include -#include - -namespace SkTiff { - -// Constants for endian signature. -inline constexpr size_t kEndianSize = 4; -inline constexpr uint8_t kEndianBig[kEndianSize] = {'M', 'M', 0, 42}; -inline constexpr uint8_t kEndianLittle[kEndianSize] = {'I', 'I', 42, 0}; - -// Constants for types. -inline constexpr uint16_t kTypeUnsignedByte = 1; -inline constexpr uint16_t kTypeAsciiString = 2; -inline constexpr uint16_t kTypeUnsignedShort = 3; -inline constexpr uint16_t kTypeUnsignedLong = 4; -inline constexpr uint16_t kTypeUnsignedRational = 5; -inline constexpr uint16_t kTypeSignedByte = 6; -inline constexpr uint16_t kTypeUndefined = 7; -inline constexpr uint16_t kTypeSignedShort = 8; -inline constexpr uint16_t kTypeSignedLong = 9; -inline constexpr uint16_t kTypeSignedRational = 10; -inline constexpr uint16_t kTypeSingleFloat = 11; -inline constexpr uint16_t kTypeDoubleFloat = 12; - -inline constexpr size_t kSizeEntry = 12; -inline constexpr size_t kSizeShort = 2; -inline constexpr size_t kSizeLong = 4; - -/* - * Helper function for parsing a Tiff Image File Directory (IFD) structure. This structure is used - * by EXIF tags, multi-picture, and maker note metadata. - */ -class ImageFileDirectory { -public: - /* - * Parse |data| to read the endian-ness into |outLittleEndian| and the IFD offset into - * |outIfdOffset|. Return true if the endian-ness was successfully parsed and there was - * the IFD offset was read. - */ - static bool ParseHeader(const SkData* data, bool* outLittleEndian, uint32_t* outIfdOffset); - - /* - * Create an object for parsing an IFD at offset |ifdOffset| inside |data| which has endianness - * indicated by |littleEndian|. If |allowTruncated| is true, then parse as much of |data| as is - * possible, otherwise reject any incomplete IFDs. - */ - static std::unique_ptr MakeFromOffset(sk_sp data, - bool littleEndian, - uint32_t ifdOffset, - bool allowTruncated = false); - - /* - * Return the number of entries. - */ - uint16_t getNumEntries() const { return fNumEntries; } - - /* - * Return the offset (within the specified SkData) of the next IFD in the list of IFDs. - */ - uint32_t nextIfdOffset() const { return fNextIfdOffset; } - - /* - * Return the tag, of a specific entry. - */ - uint16_t getEntryTag(uint16_t entryIndex) const; - - /* - * If |entryIndex| has type unsigned short (3), unsigned long (4), or signed rational (10), and - * count |count|, then populate |values| with the data for the tag and return true. Otherwise - * return false. - */ - bool getEntryUnsignedShort(uint16_t entryIndex, uint32_t count, uint16_t* values) const { - return getEntryValuesGeneric(entryIndex, kTypeUnsignedShort, count, values); - } - bool getEntryUnsignedLong(uint16_t entryIndex, uint32_t count, uint32_t* values) const { - return getEntryValuesGeneric(entryIndex, kTypeUnsignedLong, count, values); - } - bool getEntrySignedRational(uint16_t entryIndex, uint32_t count, float* values) const { - return getEntryValuesGeneric(entryIndex, kTypeSignedRational, count, values); - } - bool getEntryUnsignedRational(uint16_t entryIndex, uint32_t count, float* values) const { - return getEntryValuesGeneric(entryIndex, kTypeUnsignedRational, count, values); - } - - /* - * If |entryIndex| has type undefined (7), then return the bytes specified by the count field - * and the offset (read from the value field as an unsigned long). - */ - sk_sp getEntryUndefinedData(uint16_t entryIndex) const; - -private: - static bool IsValidType(uint16_t type); - static size_t BytesForType(uint16_t type); - - ImageFileDirectory(sk_sp data, - bool littleEndian, - uint32_t offset, - uint16_t ifdNumEntries, - uint32_t ifdNextOffset); - - /* - * Return the tag, type, count, and data for the specified entry. Return false if the type - * is invalid, or if the data in the IFD is out of bounds. - */ - bool getEntryRawData(uint16_t entryIndex, - uint16_t* outTag, - uint16_t* outType, - uint32_t* outCount, - const uint8_t** outData, - size_t* outDataSize) const; - - /* - * Helper function for assorted getTag functions. - */ - bool getEntryValuesGeneric(uint16_t entryIndex, - uint16_t type, - uint32_t count, - void* values) const; - - // The data that the IFD indexes into. - const sk_sp fData; - - // True if the data is little endian. - const bool fLittleEndian; - - // The offset where the IFD starts. - const uint32_t fOffset; - - // The number of entries of the IFD (read from the first 2 bytes at the IFD offset). - const uint16_t fNumEntries; - - // The offset of the next IFD (read from the next 4 bytes after the IFD entries). - const uint32_t fNextIfdOffset; -}; - -} // namespace SkTiff - -#endif diff --git a/gfx/skia/skia/src/codec/SkWbmpCodec.cpp b/gfx/skia/skia/src/codec/SkWbmpCodec.cpp deleted file mode 100644 index 0b998b538c23..000000000000 --- a/gfx/skia/skia/src/codec/SkWbmpCodec.cpp +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "src/codec/SkWbmpCodec.h" - -#include "include/codec/SkCodec.h" -#include "include/codec/SkEncodedImageFormat.h" -#include "include/codec/SkWbmpDecoder.h" -#include "include/core/SkColorType.h" -#include "include/core/SkData.h" -#include "include/core/SkImageInfo.h" -#include "include/core/SkRefCnt.h" -#include "include/core/SkSize.h" -#include "include/core/SkStream.h" -#include "include/private/SkEncodedInfo.h" -#include "include/private/base/SkAlign.h" -#include "include/private/base/SkTo.h" -#include "modules/skcms/skcms.h" -#include "src/codec/SkCodecPriv.h" - -#include - -using namespace skia_private; - -// Each bit represents a pixel, so width is actually a number of bits. -// A row will always be stored in bytes, so we round width up to the -// nearest multiple of 8 to get the number of bits actually in the row. -// We then divide by 8 to convert to bytes. -static inline size_t get_src_row_bytes(int width) { - return SkAlign8(width) >> 3; -} - -static inline bool valid_color_type(const SkImageInfo& dstInfo) { - switch (dstInfo.colorType()) { - case kRGBA_8888_SkColorType: - case kBGRA_8888_SkColorType: - case kGray_8_SkColorType: - case kRGB_565_SkColorType: - return true; - case kRGBA_F16_SkColorType: - return dstInfo.colorSpace(); - default: - return false; - } -} - -static bool read_byte(SkStream* stream, uint8_t* data) -{ - return stream->read(data, 1) == 1; -} - -// http://en.wikipedia.org/wiki/Variable-length_quantity -static bool read_mbf(SkStream* stream, uint64_t* value) { - uint64_t n = 0; - uint8_t data; - const uint64_t kLimit = 0xFE00000000000000; - SkASSERT(kLimit == ~((~static_cast(0)) >> 7)); - do { - if (n & kLimit) { // Will overflow on shift by 7. - return false; - } - if (stream->read(&data, 1) != 1) { - return false; - } - n = (n << 7) | (data & 0x7F); - } while (data & 0x80); - *value = n; - return true; -} - -static bool read_header(SkStream* stream, SkISize* size) { - { - uint8_t data; - if (!read_byte(stream, &data) || data != 0) { // unknown type - return false; - } - if (!read_byte(stream, &data) || (data & 0x9F)) { // skip fixed header - return false; - } - } - - uint64_t width, height; - if (!read_mbf(stream, &width) || width > 0xFFFF || !width) { - return false; - } - if (!read_mbf(stream, &height) || height > 0xFFFF || !height) { - return false; - } - if (size) { - *size = SkISize::Make(SkToS32(width), SkToS32(height)); - } - return true; -} - -bool SkWbmpCodec::onRewind() { - return read_header(this->stream(), nullptr); -} - -bool SkWbmpCodec::readRow(uint8_t* row) { - return this->stream()->read(row, fSrcRowBytes) == fSrcRowBytes; -} - -SkWbmpCodec::SkWbmpCodec(SkEncodedInfo&& info, std::unique_ptr stream) - // Wbmp does not need a colorXform, so choose an arbitrary srcFormat. - : INHERITED(std::move(info), skcms_PixelFormat(), - std::move(stream)) - , fSrcRowBytes(get_src_row_bytes(this->dimensions().width())) - , fSwizzler(nullptr) -{} - -SkEncodedImageFormat SkWbmpCodec::onGetEncodedFormat() const { - return SkEncodedImageFormat::kWBMP; -} - -bool SkWbmpCodec::conversionSupported(const SkImageInfo& dst, bool srcIsOpaque, - bool /*needsColorXform*/) { - return valid_color_type(dst) && valid_alpha(dst.alphaType(), srcIsOpaque); -} - -SkCodec::Result SkWbmpCodec::onGetPixels(const SkImageInfo& info, - void* dst, - size_t rowBytes, - const Options& options, - int* rowsDecoded) { - if (options.fSubset) { - // Subsets are not supported. - return kUnimplemented; - } - - // Initialize the swizzler - std::unique_ptr swizzler = SkSwizzler::Make(this->getEncodedInfo(), nullptr, info, - options); - SkASSERT(swizzler); - - // Perform the decode - SkISize size = info.dimensions(); - AutoTMalloc src(fSrcRowBytes); - void* dstRow = dst; - for (int y = 0; y < size.height(); ++y) { - if (!this->readRow(src.get())) { - *rowsDecoded = y; - return kIncompleteInput; - } - swizzler->swizzle(dstRow, src.get()); - dstRow = SkTAddOffset(dstRow, rowBytes); - } - return kSuccess; -} - -bool SkWbmpCodec::IsWbmp(const void* buffer, size_t bytesRead) { - SkMemoryStream stream(buffer, bytesRead, false); - return read_header(&stream, nullptr); -} - -std::unique_ptr SkWbmpCodec::MakeFromStream(std::unique_ptr stream, - Result* result) { - SkASSERT(result); - if (!stream) { - *result = SkCodec::kInvalidInput; - return nullptr; - } - SkISize size; - if (!read_header(stream.get(), &size)) { - // This already succeeded in IsWbmp, so this stream was corrupted in/ - // after rewind. - *result = kCouldNotRewind; - return nullptr; - } - *result = kSuccess; - auto info = SkEncodedInfo::Make(size.width(), size.height(), SkEncodedInfo::kGray_Color, - SkEncodedInfo::kOpaque_Alpha, 1); - return std::unique_ptr(new SkWbmpCodec(std::move(info), std::move(stream))); -} - -int SkWbmpCodec::onGetScanlines(void* dst, int count, size_t dstRowBytes) { - void* dstRow = dst; - for (int y = 0; y < count; ++y) { - if (!this->readRow(fSrcBuffer.get())) { - return y; - } - fSwizzler->swizzle(dstRow, fSrcBuffer.get()); - dstRow = SkTAddOffset(dstRow, dstRowBytes); - } - return count; -} - -bool SkWbmpCodec::onSkipScanlines(int count) { - const size_t bytesToSkip = count * fSrcRowBytes; - return this->stream()->skip(bytesToSkip) == bytesToSkip; -} - -SkCodec::Result SkWbmpCodec::onStartScanlineDecode(const SkImageInfo& dstInfo, - const Options& options) { - if (options.fSubset) { - // Subsets are not supported. - return kUnimplemented; - } - - fSwizzler = SkSwizzler::Make(this->getEncodedInfo(), nullptr, dstInfo, options); - SkASSERT(fSwizzler); - - fSrcBuffer.reset(fSrcRowBytes); - - return kSuccess; -} - -namespace SkWbmpDecoder { -bool IsWbmp(const void* data, size_t len) { - return SkWbmpCodec::IsWbmp(data, len); -} - -std::unique_ptr Decode(std::unique_ptr stream, - SkCodec::Result* outResult, - SkCodecs::DecodeContext) { - SkCodec::Result resultStorage; - if (!outResult) { - outResult = &resultStorage; - } - return SkWbmpCodec::MakeFromStream(std::move(stream), outResult); -} - -std::unique_ptr Decode(sk_sp data, - SkCodec::Result* outResult, - SkCodecs::DecodeContext) { - if (!data) { - if (outResult) { - *outResult = SkCodec::kInvalidInput; - } - return nullptr; - } - return Decode(SkMemoryStream::Make(std::move(data)), outResult, nullptr); -} -} // namespace SkWbmpDecoder diff --git a/gfx/skia/skia/src/codec/SkWbmpCodec.h b/gfx/skia/skia/src/codec/SkWbmpCodec.h deleted file mode 100644 index 351abc4e5a4b..000000000000 --- a/gfx/skia/skia/src/codec/SkWbmpCodec.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkCodec_wbmp_DEFINED -#define SkCodec_wbmp_DEFINED - -#include "include/codec/SkCodec.h" -#include "include/core/SkTypes.h" -#include "include/private/base/SkTemplates.h" -#include "src/codec/SkSwizzler.h" - -#include -#include -#include - -class SkSampler; -class SkStream; -enum class SkEncodedImageFormat; -struct SkEncodedInfo; -struct SkImageInfo; - -class SkWbmpCodec final : public SkCodec { -public: - static bool IsWbmp(const void*, size_t); - - /* - * Assumes IsWbmp was called and returned true - * Creates a wbmp codec - * Takes ownership of the stream - */ - static std::unique_ptr MakeFromStream(std::unique_ptr, Result*); - -protected: - SkEncodedImageFormat onGetEncodedFormat() const override; - Result onGetPixels(const SkImageInfo&, void*, size_t, - const Options&, int*) override; - bool onRewind() override; - bool conversionSupported(const SkImageInfo& dst, bool srcIsOpaque, - bool needsXform) override; - // No need to Xform; all pixels are either black or white. - bool usesColorXform() const override { return false; } -private: - SkSampler* getSampler(bool createIfNecessary) override { - SkASSERT(fSwizzler || !createIfNecessary); - return fSwizzler.get(); - } - - /* - * Read a src row from the encoded stream - */ - bool readRow(uint8_t* row); - - SkWbmpCodec(SkEncodedInfo&&, std::unique_ptr); - - const size_t fSrcRowBytes; - - // Used for scanline decodes: - std::unique_ptr fSwizzler; - skia_private::AutoTMalloc fSrcBuffer; - - int onGetScanlines(void* dst, int count, size_t dstRowBytes) override; - bool onSkipScanlines(int count) override; - Result onStartScanlineDecode(const SkImageInfo& dstInfo, - const Options& options) override; - - using INHERITED = SkCodec; -}; - -#endif // SkCodec_wbmp_DEFINED diff --git a/gfx/skia/skia/src/codec/SkWebpCodec.cpp b/gfx/skia/skia/src/codec/SkWebpCodec.cpp deleted file mode 100644 index 202073d7b406..000000000000 --- a/gfx/skia/skia/src/codec/SkWebpCodec.cpp +++ /dev/null @@ -1,610 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "src/codec/SkWebpCodec.h" - -#include "include/codec/SkCodec.h" -#include "include/codec/SkCodecAnimation.h" -#include "include/codec/SkWebpDecoder.h" -#include "include/core/SkAlphaType.h" -#include "include/core/SkBitmap.h" -#include "include/core/SkColorType.h" -#include "include/core/SkImageInfo.h" -#include "include/core/SkRect.h" -#include "include/core/SkSize.h" -#include "include/core/SkStream.h" -#include "include/private/base/SkAlign.h" -#include "include/private/base/SkMath.h" -#include "include/private/base/SkTFitsIn.h" -#include "include/private/base/SkTemplates.h" -#include "include/private/base/SkTo.h" -#include "modules/skcms/skcms.h" -#include "src/codec/SkParseEncodedOrigin.h" -#include "src/codec/SkSampler.h" -#include "src/core/SkRasterPipeline.h" -#include "src/core/SkRasterPipelineOpContexts.h" -#include "src/core/SkRasterPipelineOpList.h" -#include "src/core/SkStreamPriv.h" - -#include -#include -#include -#include - -// A WebP decoder on top of (subset of) libwebp -// For more information on WebP image format, and libwebp library, see: -// https://code.google.com/speed/webp/ -// http://www.webmproject.org/code/#libwebp-webp-image-library -// https://chromium.googlesource.com/webm/libwebp - -// If moving libwebp out of skia source tree, path for webp headers must be -// updated accordingly. Here, we enforce using local copy in webp sub-directory. -#include "webp/decode.h" // NO_G3_REWRITE -#include "webp/demux.h" // NO_G3_REWRITE -#include "webp/mux_types.h" // NO_G3_REWRITE - -bool SkWebpCodec::IsWebp(const void* buf, size_t bytesRead) { - // WEBP starts with the following: - // RIFFXXXXWEBPVP - // Where XXXX is unspecified. - const char* bytes = static_cast(buf); - return bytesRead >= 14 && !memcmp(bytes, "RIFF", 4) && !memcmp(&bytes[8], "WEBPVP", 6); -} - -// Parse headers of RIFF container, and check for valid Webp (VP8) content. -// Returns an SkWebpCodec on success -std::unique_ptr SkWebpCodec::MakeFromStream(std::unique_ptr stream, - Result* result) { - SkASSERT(result); - if (!stream) { - *result = SkCodec::kInvalidInput; - return nullptr; - } - // Webp demux needs a contiguous data buffer. - sk_sp data = nullptr; - if (stream->getMemoryBase()) { - // It is safe to make without copy because we'll hold onto the stream. - data = SkData::MakeWithoutCopy(stream->getMemoryBase(), stream->getLength()); - } else { - data = SkCopyStreamToData(stream.get()); - - // If we are forced to copy the stream to a data, we can go ahead and delete the stream. - stream.reset(nullptr); - } - - // It's a little strange that the |demux| will outlive |webpData|, though it needs the - // pointer in |webpData| to remain valid. This works because the pointer remains valid - // until the SkData is freed. - WebPData webpData = { data->bytes(), data->size() }; - WebPDemuxState state; - SkAutoTCallVProc demux(WebPDemuxPartial(&webpData, &state)); - switch (state) { - case WEBP_DEMUX_PARSE_ERROR: - *result = kInvalidInput; - return nullptr; - case WEBP_DEMUX_PARSING_HEADER: - *result = kIncompleteInput; - return nullptr; - case WEBP_DEMUX_PARSED_HEADER: - case WEBP_DEMUX_DONE: - SkASSERT(demux); - break; - } - - const int width = WebPDemuxGetI(demux, WEBP_FF_CANVAS_WIDTH); - const int height = WebPDemuxGetI(demux, WEBP_FF_CANVAS_HEIGHT); - - // Validate the image size that's about to be decoded. - { - const int64_t size = sk_64_mul(width, height); - // now check that if we are 4-bytes per pixel, we also don't overflow - if (!SkTFitsIn(size) || SkTo(size) > (0x7FFFFFFF >> 2)) { - *result = kInvalidInput; - return nullptr; - } - } - - std::unique_ptr profile = nullptr; - { - WebPChunkIterator chunkIterator; - SkAutoTCallVProc autoCI(&chunkIterator); - if (WebPDemuxGetChunk(demux, "ICCP", 1, &chunkIterator)) { - // FIXME: I think this could be MakeWithoutCopy - auto chunk = SkData::MakeWithCopy(chunkIterator.chunk.bytes, chunkIterator.chunk.size); - profile = SkEncodedInfo::ICCProfile::Make(std::move(chunk)); - } - if (profile && profile->profile()->data_color_space != skcms_Signature_RGB) { - profile = nullptr; - } - } - - SkEncodedOrigin origin = kDefault_SkEncodedOrigin; - { - WebPChunkIterator chunkIterator; - SkAutoTCallVProc autoCI(&chunkIterator); - if (WebPDemuxGetChunk(demux, "EXIF", 1, &chunkIterator)) { - SkParseEncodedOrigin(chunkIterator.chunk.bytes, chunkIterator.chunk.size, &origin); - } - } - - // Get the first frame and its "features" to determine the color and alpha types. - WebPIterator frame; - SkAutoTCallVProc autoFrame(&frame); - if (!WebPDemuxGetFrame(demux, 1, &frame)) { - *result = kIncompleteInput; - return nullptr; - } - - WebPBitstreamFeatures features; - switch (WebPGetFeatures(frame.fragment.bytes, frame.fragment.size, &features)) { - case VP8_STATUS_OK: - break; - case VP8_STATUS_SUSPENDED: - case VP8_STATUS_NOT_ENOUGH_DATA: - *result = kIncompleteInput; - return nullptr; - default: - *result = kInvalidInput; - return nullptr; - } - - const bool hasAlpha = SkToBool(frame.has_alpha) - || frame.width != width || frame.height != height; - SkEncodedInfo::Color color; - SkEncodedInfo::Alpha alpha; - switch (features.format) { - case 0: - // This indicates a "mixed" format. We could see this for - // animated webps (multiple fragments). - // We could also guess kYUV here, but I think it makes more - // sense to guess kBGRA which is likely closer to the final - // output. Otherwise, we might end up converting - // BGRA->YUVA->BGRA. - [[fallthrough]]; - case 2: - // This is the lossless format (BGRA). - if (hasAlpha) { - color = SkEncodedInfo::kBGRA_Color; - alpha = SkEncodedInfo::kUnpremul_Alpha; - } else { - color = SkEncodedInfo::kBGRX_Color; - alpha = SkEncodedInfo::kOpaque_Alpha; - } - break; - case 1: - // This is the lossy format (YUV). - if (hasAlpha) { - color = SkEncodedInfo::kYUVA_Color; - alpha = SkEncodedInfo::kUnpremul_Alpha; - } else { - color = SkEncodedInfo::kYUV_Color; - alpha = SkEncodedInfo::kOpaque_Alpha; - } - break; - default: - *result = kInvalidInput; - return nullptr; - } - - - *result = kSuccess; - SkEncodedInfo info = SkEncodedInfo::Make(width, height, color, alpha, 8, std::move(profile)); - return std::unique_ptr(new SkWebpCodec(std::move(info), std::move(stream), - demux.release(), std::move(data), origin)); -} - -static WEBP_CSP_MODE webp_decode_mode(SkColorType dstCT, bool premultiply) { - switch (dstCT) { - case kBGRA_8888_SkColorType: - return premultiply ? MODE_bgrA : MODE_BGRA; - case kRGBA_8888_SkColorType: - return premultiply ? MODE_rgbA : MODE_RGBA; - case kRGB_565_SkColorType: - return MODE_RGB_565; - default: - return MODE_LAST; - } -} - -SkWebpCodec::Frame* SkWebpCodec::FrameHolder::appendNewFrame(bool hasAlpha) { - const int i = this->size(); - fFrames.emplace_back(i, hasAlpha ? SkEncodedInfo::kUnpremul_Alpha - : SkEncodedInfo::kOpaque_Alpha); - return &fFrames[i]; -} - -bool SkWebpCodec::onGetValidSubset(SkIRect* desiredSubset) const { - if (!desiredSubset) { - return false; - } - - if (!this->bounds().contains(*desiredSubset)) { - return false; - } - - // As stated below, libwebp snaps to even left and top. Make sure top and left are even, so we - // decode this exact subset. - // Leave right and bottom unmodified, so we suggest a slightly larger subset than requested. - desiredSubset->fLeft = (desiredSubset->fLeft >> 1) << 1; - desiredSubset->fTop = (desiredSubset->fTop >> 1) << 1; - return true; -} - -int SkWebpCodec::onGetRepetitionCount() { - auto flags = WebPDemuxGetI(fDemux.get(), WEBP_FF_FORMAT_FLAGS); - if (!(flags & ANIMATION_FLAG)) { - return 0; - } - - int loopCount = WebPDemuxGetI(fDemux.get(), WEBP_FF_LOOP_COUNT); - if (0 == loopCount) { - return kRepetitionCountInfinite; - } - - loopCount--; - return loopCount; -} - -int SkWebpCodec::onGetFrameCount() { - auto flags = WebPDemuxGetI(fDemux.get(), WEBP_FF_FORMAT_FLAGS); - if (!(flags & ANIMATION_FLAG)) { - return 1; - } - - const uint32_t oldFrameCount = fFrameHolder.size(); - if (fFailed) { - return oldFrameCount; - } - - const uint32_t frameCount = WebPDemuxGetI(fDemux, WEBP_FF_FRAME_COUNT); - if (oldFrameCount == frameCount) { - // We have already parsed this. - return frameCount; - } - - fFrameHolder.reserve(frameCount); - - for (uint32_t i = oldFrameCount; i < frameCount; i++) { - WebPIterator iter; - SkAutoTCallVProc autoIter(&iter); - - if (!WebPDemuxGetFrame(fDemux.get(), i + 1, &iter)) { - fFailed = true; - break; - } - - // libwebp only reports complete frames of an animated image. - SkASSERT(iter.complete); - - Frame* frame = fFrameHolder.appendNewFrame(iter.has_alpha); - frame->setXYWH(iter.x_offset, iter.y_offset, iter.width, iter.height); - frame->setDisposalMethod(iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND ? - SkCodecAnimation::DisposalMethod::kRestoreBGColor : - SkCodecAnimation::DisposalMethod::kKeep); - frame->setDuration(iter.duration); - if (WEBP_MUX_BLEND != iter.blend_method) { - frame->setBlend(SkCodecAnimation::Blend::kSrc); - } - fFrameHolder.setAlphaAndRequiredFrame(frame); - } - - return fFrameHolder.size(); - -} - -const SkFrame* SkWebpCodec::FrameHolder::onGetFrame(int i) const { - return static_cast(this->frame(i)); -} - -const SkWebpCodec::Frame* SkWebpCodec::FrameHolder::frame(int i) const { - SkASSERT(i >= 0 && i < this->size()); - return &fFrames[i]; -} - -bool SkWebpCodec::onGetFrameInfo(int i, FrameInfo* frameInfo) const { - if (i >= fFrameHolder.size()) { - return false; - } - - const Frame* frame = fFrameHolder.frame(i); - if (!frame) { - return false; - } - - if (frameInfo) { - // libwebp only reports fully received frames for an - // animated image. - frame->fillIn(frameInfo, true); - } - - return true; -} - -static bool is_8888(SkColorType colorType) { - switch (colorType) { - case kRGBA_8888_SkColorType: - case kBGRA_8888_SkColorType: - return true; - default: - return false; - } -} - -// Requires that the src input be unpremultiplied (or opaque). -static void blend_line(SkColorType dstCT, void* dst, - SkColorType srcCT, const void* src, - SkAlphaType dstAt, - bool srcHasAlpha, - int width) { - SkRasterPipeline_MemoryCtx dst_ctx = { dst, 0 }, - src_ctx = { const_cast(src), 0 }; - - SkRasterPipeline_<256> p; - - p.appendLoadDst(dstCT, &dst_ctx); - if (kUnpremul_SkAlphaType == dstAt) { - p.append(SkRasterPipelineOp::premul_dst); - } - - p.appendLoad(srcCT, &src_ctx); - if (srcHasAlpha) { - p.append(SkRasterPipelineOp::premul); - } - - p.append(SkRasterPipelineOp::srcover); - - if (kUnpremul_SkAlphaType == dstAt) { - p.append(SkRasterPipelineOp::unpremul); - } - p.appendStore(dstCT, &dst_ctx); - - p.run(0,0, width,1); -} - -SkCodec::Result SkWebpCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t rowBytes, - const Options& options, int* rowsDecodedPtr) { - const int index = options.fFrameIndex; - SkASSERT(0 == index || index < fFrameHolder.size()); - SkASSERT(0 == index || !options.fSubset); - - WebPDecoderConfig config; - if (0 == WebPInitDecoderConfig(&config)) { - // ABI mismatch. - // FIXME: New enum for this? - return kInvalidInput; - } - - // Free any memory associated with the buffer. Must be called last, so we declare it first. - SkAutoTCallVProc autoFree(&(config.output)); - - WebPIterator frame; - SkAutoTCallVProc autoFrame(&frame); - // If this succeeded in onGetFrameCount(), it should succeed again here. - SkAssertResult(WebPDemuxGetFrame(fDemux, index + 1, &frame)); - - const bool independent = index == 0 ? true : - (fFrameHolder.frame(index)->getRequiredFrame() == kNoFrame); - // Get the frameRect. libwebp will have already signaled an error if this is not fully - // contained by the canvas. - auto frameRect = SkIRect::MakeXYWH(frame.x_offset, frame.y_offset, frame.width, frame.height); - SkASSERT(this->bounds().contains(frameRect)); - const bool frameIsSubset = frameRect != this->bounds(); - if (independent && frameIsSubset) { - SkSampler::Fill(dstInfo, dst, rowBytes, options.fZeroInitialized); - } - - int dstX = frameRect.x(); - int dstY = frameRect.y(); - int subsetWidth = frameRect.width(); - int subsetHeight = frameRect.height(); - if (options.fSubset) { - SkIRect subset = *options.fSubset; - SkASSERT(this->bounds().contains(subset)); - SkASSERT(SkIsAlign2(subset.fLeft) && SkIsAlign2(subset.fTop)); - SkASSERT(this->getValidSubset(&subset) && subset == *options.fSubset); - - if (!SkIRect::Intersects(subset, frameRect)) { - return kSuccess; - } - - int minXOffset = std::min(dstX, subset.x()); - int minYOffset = std::min(dstY, subset.y()); - dstX -= minXOffset; - dstY -= minYOffset; - frameRect.offset(-minXOffset, -minYOffset); - subset.offset(-minXOffset, -minYOffset); - - // Just like we require that the requested subset x and y offset are even, libwebp - // guarantees that the frame x and y offset are even (it's actually impossible to specify - // an odd frame offset). So we can still guarantee that the adjusted offsets are even. - SkASSERT(SkIsAlign2(subset.fLeft) && SkIsAlign2(subset.fTop)); - - SkIRect intersection; - SkAssertResult(intersection.intersect(frameRect, subset)); - subsetWidth = intersection.width(); - subsetHeight = intersection.height(); - - config.options.use_cropping = 1; - config.options.crop_left = subset.x(); - config.options.crop_top = subset.y(); - config.options.crop_width = subsetWidth; - config.options.crop_height = subsetHeight; - } - - // Ignore the frame size and offset when determining if scaling is necessary. - int scaledWidth = subsetWidth; - int scaledHeight = subsetHeight; - SkISize srcSize = options.fSubset ? options.fSubset->size() : this->dimensions(); - if (srcSize != dstInfo.dimensions()) { - config.options.use_scaling = 1; - - if (frameIsSubset) { - float scaleX = ((float) dstInfo.width()) / srcSize.width(); - float scaleY = ((float) dstInfo.height()) / srcSize.height(); - - // We need to be conservative here and floor rather than round. - // Otherwise, we may find ourselves decoding off the end of memory. - dstX = scaleX * dstX; - scaledWidth = scaleX * scaledWidth; - dstY = scaleY * dstY; - scaledHeight = scaleY * scaledHeight; - if (0 == scaledWidth || 0 == scaledHeight) { - return kSuccess; - } - } else { - scaledWidth = dstInfo.width(); - scaledHeight = dstInfo.height(); - } - - config.options.scaled_width = scaledWidth; - config.options.scaled_height = scaledHeight; - } - - const bool blendWithPrevFrame = !independent && frame.blend_method == WEBP_MUX_BLEND - && frame.has_alpha; - - auto webpInfo = dstInfo; - if (!frame.has_alpha) { - webpInfo = webpInfo.makeAlphaType(kOpaque_SkAlphaType); - } else if (this->colorXform() || blendWithPrevFrame) { - // the colorXform and blend_line expect unpremul. - webpInfo = webpInfo.makeAlphaType(kUnpremul_SkAlphaType); - } - if (this->colorXform()) { - // Swizzling between RGBA and BGRA is zero cost in a color transform. So when we have a - // color transform, we should decode to whatever is easiest for libwebp, and then let the - // color transform swizzle if necessary. - // Lossy webp is encoded as YUV (so RGBA and BGRA are the same cost). Lossless webp is - // encoded as BGRA. This means decoding to BGRA is either faster or the same cost as RGBA. - webpInfo = webpInfo.makeColorType(kBGRA_8888_SkColorType); - } - - SkBitmap webpDst; - if ((this->colorXform() && !is_8888(dstInfo.colorType())) || blendWithPrevFrame) { - // We will decode the entire image and then perform the color transform. libwebp - // does not provide a row-by-row API. This is a shame particularly when we do not want - // 8888, since we will need to create another image sized buffer. - webpDst.allocPixels(webpInfo); - } else { - // libwebp can decode directly into the output memory. - webpDst.installPixels(webpInfo, dst, rowBytes); - } - - config.output.colorspace = webp_decode_mode(webpInfo.colorType(), - webpInfo.alphaType() == kPremul_SkAlphaType); - config.output.is_external_memory = 1; - - config.output.u.RGBA.rgba = reinterpret_cast(webpDst.getAddr(dstX, dstY)); - config.output.u.RGBA.stride = static_cast(webpDst.rowBytes()); - config.output.u.RGBA.size = webpDst.computeByteSize(); - - SkAutoTCallVProc idec(WebPIDecode(nullptr, 0, &config)); - if (!idec) { - return kInvalidInput; - } - - int rowsDecoded = 0; - SkCodec::Result result; - switch (WebPIUpdate(idec, frame.fragment.bytes, frame.fragment.size)) { - case VP8_STATUS_OK: - rowsDecoded = scaledHeight; - result = kSuccess; - break; - case VP8_STATUS_SUSPENDED: - if (!WebPIDecGetRGB(idec, &rowsDecoded, nullptr, nullptr, nullptr) - || rowsDecoded <= 0) { - return kInvalidInput; - } - *rowsDecodedPtr = rowsDecoded + dstY; - result = kIncompleteInput; - break; - default: - return kInvalidInput; - } - - const size_t dstBpp = dstInfo.bytesPerPixel(); - dst = SkTAddOffset(dst, dstBpp * dstX + rowBytes * dstY); - const size_t srcRowBytes = config.output.u.RGBA.stride; - - const auto dstCT = dstInfo.colorType(); - if (this->colorXform()) { - uint32_t* xformSrc = (uint32_t*) config.output.u.RGBA.rgba; - SkBitmap tmp; - void* xformDst; - - if (blendWithPrevFrame) { - // Xform into temporary bitmap big enough for one row. - tmp.allocPixels(dstInfo.makeWH(scaledWidth, 1)); - xformDst = tmp.getPixels(); - } else { - xformDst = dst; - } - - for (int y = 0; y < rowsDecoded; y++) { - this->applyColorXform(xformDst, xformSrc, scaledWidth); - if (blendWithPrevFrame) { - blend_line(dstCT, dst, dstCT, xformDst, - dstInfo.alphaType(), frame.has_alpha, scaledWidth); - dst = SkTAddOffset(dst, rowBytes); - } else { - xformDst = SkTAddOffset(xformDst, rowBytes); - } - xformSrc = SkTAddOffset(xformSrc, srcRowBytes); - } - } else if (blendWithPrevFrame) { - const uint8_t* src = config.output.u.RGBA.rgba; - - for (int y = 0; y < rowsDecoded; y++) { - blend_line(dstCT, dst, webpDst.colorType(), src, - dstInfo.alphaType(), frame.has_alpha, scaledWidth); - src = SkTAddOffset(src, srcRowBytes); - dst = SkTAddOffset(dst, rowBytes); - } - } - - return result; -} - -SkWebpCodec::SkWebpCodec(SkEncodedInfo&& info, std::unique_ptr stream, - WebPDemuxer* demux, sk_sp data, SkEncodedOrigin origin) - : INHERITED(std::move(info), skcms_PixelFormat_BGRA_8888, std::move(stream), - origin) - , fDemux(demux) - , fData(std::move(data)) - , fFailed(false) -{ - const auto& eInfo = this->getEncodedInfo(); - fFrameHolder.setScreenSize(eInfo.width(), eInfo.height()); -} - -namespace SkWebpDecoder { -bool IsWebp(const void* data, size_t len) { - return SkWebpCodec::IsWebp(data, len); -} - -std::unique_ptr Decode(std::unique_ptr stream, - SkCodec::Result* outResult, - SkCodecs::DecodeContext) { - SkCodec::Result resultStorage; - if (!outResult) { - outResult = &resultStorage; - } - return SkWebpCodec::MakeFromStream(std::move(stream), outResult); -} - -std::unique_ptr Decode(sk_sp data, - SkCodec::Result* outResult, - SkCodecs::DecodeContext) { - if (!data) { - if (outResult) { - *outResult = SkCodec::kInvalidInput; - } - return nullptr; - } - return Decode(SkMemoryStream::Make(std::move(data)), outResult, nullptr); -} -} // namespace SkWebpDecoder diff --git a/gfx/skia/skia/src/codec/SkWebpCodec.h b/gfx/skia/skia/src/codec/SkWebpCodec.h deleted file mode 100644 index 2390eb372cba..000000000000 --- a/gfx/skia/skia/src/codec/SkWebpCodec.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkWebpCodec_DEFINED -#define SkWebpCodec_DEFINED - -#include "include/codec/SkEncodedImageFormat.h" -#include "include/codec/SkEncodedOrigin.h" -#include "include/core/SkData.h" -#include "include/core/SkRefCnt.h" -#include "include/core/SkTypes.h" -#include "include/private/SkEncodedInfo.h" -#include "include/private/base/SkTemplates.h" -#include "src/codec/SkFrameHolder.h" -#include "src/codec/SkScalingCodec.h" - -#include -#include -#include - -class SkStream; -class SkCodec; -struct SkIRect; -struct SkImageInfo; - -extern "C" { - struct WebPDemuxer; - void WebPDemuxDelete(WebPDemuxer* dmux); -} - -class SkWebpCodec final : public SkScalingCodec { -public: - // Assumes IsWebp was called and returned true. - static std::unique_ptr MakeFromStream(std::unique_ptr, Result*); - static bool IsWebp(const void*, size_t); -protected: - Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, int*) override; - SkEncodedImageFormat onGetEncodedFormat() const override { return SkEncodedImageFormat::kWEBP; } - - bool onGetValidSubset(SkIRect* /* desiredSubset */) const override; - - int onGetFrameCount() override; - bool onGetFrameInfo(int, FrameInfo*) const override; - int onGetRepetitionCount() override; - - const SkFrameHolder* getFrameHolder() const override { - return &fFrameHolder; - } - -private: - SkWebpCodec(SkEncodedInfo&&, std::unique_ptr, WebPDemuxer*, sk_sp, - SkEncodedOrigin); - - SkAutoTCallVProc fDemux; - - // fDemux has a pointer into this data. - // This should not be freed until the decode is completed. - sk_sp fData; - - class Frame : public SkFrame { - public: - Frame(int i, SkEncodedInfo::Alpha alpha) - : INHERITED(i) - , fReportedAlpha(alpha) - {} - - protected: - SkEncodedInfo::Alpha onReportedAlpha() const override { - return fReportedAlpha; - } - - private: - const SkEncodedInfo::Alpha fReportedAlpha; - - using INHERITED = SkFrame; - }; - - class FrameHolder : public SkFrameHolder { - public: - ~FrameHolder() override {} - void setScreenSize(int w, int h) { - fScreenWidth = w; - fScreenHeight = h; - } - Frame* appendNewFrame(bool hasAlpha); - const Frame* frame(int i) const; - int size() const { - return static_cast(fFrames.size()); - } - void reserve(int size) { - fFrames.reserve(size); - } - - protected: - const SkFrame* onGetFrame(int i) const override; - - private: - std::vector fFrames; - }; - - FrameHolder fFrameHolder; - // Set to true if WebPDemuxGetFrame fails. This only means - // that we will cap the frame count to the frames that - // succeed. - bool fFailed; - - using INHERITED = SkScalingCodec; -}; -#endif // SkWebpCodec_DEFINED diff --git a/gfx/skia/skia/src/codec/SkWuffsCodec.cpp b/gfx/skia/skia/src/codec/SkWuffsCodec.cpp deleted file mode 100644 index 732aaecd9b47..000000000000 --- a/gfx/skia/skia/src/codec/SkWuffsCodec.cpp +++ /dev/null @@ -1,1114 +0,0 @@ -/* - * 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 "include/codec/SkCodec.h" -#include "include/codec/SkCodecAnimation.h" -#include "include/codec/SkEncodedImageFormat.h" -#include "include/codec/SkGifDecoder.h" -#include "include/core/SkAlphaType.h" -#include "include/core/SkBitmap.h" -#include "include/core/SkBlendMode.h" -#include "include/core/SkColorType.h" -#include "include/core/SkData.h" -#include "include/core/SkImageInfo.h" -#include "include/core/SkMatrix.h" -#include "include/core/SkPaint.h" -#include "include/core/SkPixmap.h" -#include "include/core/SkRect.h" -#include "include/core/SkRefCnt.h" -#include "include/core/SkSamplingOptions.h" -#include "include/core/SkSize.h" -#include "include/core/SkStream.h" -#include "include/core/SkTypes.h" -#include "include/private/SkEncodedInfo.h" -#include "include/private/base/SkMalloc.h" -#include "include/private/base/SkTo.h" -#include "modules/skcms/skcms.h" -#include "src/codec/SkCodecPriv.h" -#include "src/codec/SkFrameHolder.h" -#include "src/codec/SkSampler.h" -#include "src/codec/SkScalingCodec.h" -#include "src/core/SkDraw.h" -#include "src/core/SkRasterClip.h" -#include "src/core/SkStreamPriv.h" - -#include -#include -#include -#include -#include -#include - -// Documentation on the Wuffs language and standard library (in general) and -// its image decoding API (in particular) is at: -// -// - https://github.com/google/wuffs/tree/master/doc -// - https://github.com/google/wuffs/blob/master/doc/std/image-decoders.md - -// Wuffs ships as a "single file C library" or "header file library" as per -// https://github.com/nothings/stb/blob/master/docs/stb_howto.txt -// -// As we have not #define'd WUFFS_IMPLEMENTATION, the #include here is -// including a header file, even though that file name ends in ".c". -#if defined(WUFFS_IMPLEMENTATION) -#error "SkWuffsCodec should not #define WUFFS_IMPLEMENTATION" -#endif -#include "wuffs-v0.3.c" // NO_G3_REWRITE -// Commit count 2514 is Wuffs 0.3.0-alpha.4. -#if WUFFS_VERSION_BUILD_METADATA_COMMIT_COUNT < 2514 -#error "Wuffs version is too old. Upgrade to the latest version." -#endif - -#define SK_WUFFS_CODEC_BUFFER_SIZE 4096 - -// Configuring a Skia build with -// SK_WUFFS_FAVORS_PERFORMANCE_OVER_ADDITIONAL_MEMORY_SAFETY can improve decode -// performance by some fixed amount (independent of the image size), which can -// be a noticeable proportional improvement if the input is relatively small. -// -// The Wuffs library is still memory-safe either way, in that there are no -// out-of-bounds reads or writes, and the library endeavours not to read -// uninitialized memory. There are just fewer compiler-enforced guarantees -// against reading uninitialized memory. For more detail, see -// https://github.com/google/wuffs/blob/master/doc/note/initialization.md#partial-zero-initialization -#if defined(SK_WUFFS_FAVORS_PERFORMANCE_OVER_ADDITIONAL_MEMORY_SAFETY) -#define SK_WUFFS_INITIALIZE_FLAGS WUFFS_INITIALIZE__LEAVE_INTERNAL_BUFFERS_UNINITIALIZED -#else -#define SK_WUFFS_INITIALIZE_FLAGS WUFFS_INITIALIZE__DEFAULT_OPTIONS -#endif - -static bool fill_buffer(wuffs_base__io_buffer* b, SkStream* s) { - b->compact(); - size_t num_read = s->read(b->data.ptr + b->meta.wi, b->data.len - b->meta.wi); - b->meta.wi += num_read; - // We hard-code false instead of s->isAtEnd(). In theory, Skia's - // SkStream::isAtEnd() method has the same semantics as Wuffs' - // wuffs_base__io_buffer_meta::closed field. Specifically, both are false - // when reading from a network socket when all bytes *available right now* - // have been read but there might be more later. - // - // However, SkStream is designed around synchronous I/O. The SkStream::read - // method does not take a callback and, per its documentation comments, a - // read request for N bytes should block until a full N bytes are - // available. In practice, Blink's SkStream subclass builds on top of async - // I/O and cannot afford to block. While it satisfies "the letter of the - // law", in terms of what the C++ compiler needs, it does not satisfy "the - // spirit of the law". Its read() can return short without blocking and its - // isAtEnd() can return false positives. - // - // When closed is true, Wuffs treats incomplete input as a fatal error - // instead of a recoverable "short read" suspension. We therefore hard-code - // false and return kIncompleteInput (instead of kErrorInInput) up the call - // stack even if the SkStream isAtEnd. The caller usually has more context - // (more than what's in the SkStream) to differentiate the two, like this: - // https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/platform/image-decoders/gif/gif_image_decoder.cc;l=115;drc=277dcc4d810ae4c0286d8af96d270ed9b686c5ff - b->meta.closed = false; - return num_read > 0; -} - -static bool seek_buffer(wuffs_base__io_buffer* b, SkStream* s, uint64_t pos) { - // Try to re-position the io_buffer's meta.ri read-index first, which is - // cheaper than seeking in the backing SkStream. - if ((pos >= b->meta.pos) && (pos - b->meta.pos <= b->meta.wi)) { - b->meta.ri = pos - b->meta.pos; - return true; - } - // Seek in the backing SkStream. - if ((pos > SIZE_MAX) || (!s->seek(pos))) { - return false; - } - b->meta.wi = 0; - b->meta.ri = 0; - b->meta.pos = pos; - b->meta.closed = false; - return true; -} - -static SkCodecAnimation::DisposalMethod wuffs_disposal_to_skia_disposal( - wuffs_base__animation_disposal w) { - switch (w) { - case WUFFS_BASE__ANIMATION_DISPOSAL__RESTORE_BACKGROUND: - return SkCodecAnimation::DisposalMethod::kRestoreBGColor; - case WUFFS_BASE__ANIMATION_DISPOSAL__RESTORE_PREVIOUS: - return SkCodecAnimation::DisposalMethod::kRestorePrevious; - default: - return SkCodecAnimation::DisposalMethod::kKeep; - } -} - -static SkAlphaType to_alpha_type(bool opaque) { - return opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType; -} - -static SkCodec::Result reset_and_decode_image_config(wuffs_gif__decoder* decoder, - wuffs_base__image_config* imgcfg, - wuffs_base__io_buffer* b, - SkStream* s) { - // Calling decoder->initialize will memset most or all of it to zero, - // depending on SK_WUFFS_INITIALIZE_FLAGS. - wuffs_base__status status = - decoder->initialize(sizeof__wuffs_gif__decoder(), WUFFS_VERSION, SK_WUFFS_INITIALIZE_FLAGS); - if (status.repr != nullptr) { - SkCodecPrintf("initialize: %s", status.message()); - return SkCodec::kInternalError; - } - - // See https://bugs.chromium.org/p/skia/issues/detail?id=12055 - decoder->set_quirk_enabled(WUFFS_GIF__QUIRK_IGNORE_TOO_MUCH_PIXEL_DATA, true); - - while (true) { - status = decoder->decode_image_config(imgcfg, b); - if (status.repr == nullptr) { - break; - } else if (status.repr != wuffs_base__suspension__short_read) { - SkCodecPrintf("decode_image_config: %s", status.message()); - return SkCodec::kErrorInInput; - } else if (!fill_buffer(b, s)) { - return SkCodec::kIncompleteInput; - } - } - - // A GIF image's natural color model is indexed color: 1 byte per pixel, - // indexing a 256-element palette. - // - // For Skia, we override that to decode to 4 bytes per pixel, BGRA or RGBA. - uint32_t pixfmt = WUFFS_BASE__PIXEL_FORMAT__INVALID; - switch (kN32_SkColorType) { - case kBGRA_8888_SkColorType: - pixfmt = WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL; - break; - case kRGBA_8888_SkColorType: - pixfmt = WUFFS_BASE__PIXEL_FORMAT__RGBA_NONPREMUL; - break; - default: - return SkCodec::kInternalError; - } - if (imgcfg) { - imgcfg->pixcfg.set(pixfmt, WUFFS_BASE__PIXEL_SUBSAMPLING__NONE, imgcfg->pixcfg.width(), - imgcfg->pixcfg.height()); - } - - return SkCodec::kSuccess; -} - -// -------------------------------- Class definitions - -class SkWuffsCodec; - -class SkWuffsFrame final : public SkFrame { -public: - SkWuffsFrame(wuffs_base__frame_config* fc); - - uint64_t ioPosition() const; - - // SkFrame overrides. - SkEncodedInfo::Alpha onReportedAlpha() const override; - -private: - uint64_t fIOPosition; - SkEncodedInfo::Alpha fReportedAlpha; - - using INHERITED = SkFrame; -}; - -// SkWuffsFrameHolder is a trivial indirector that forwards its calls onto a -// SkWuffsCodec. It is a separate class as SkWuffsCodec would otherwise -// inherit from both SkCodec and SkFrameHolder, and Skia style discourages -// multiple inheritance (e.g. with its "typedef Foo INHERITED" convention). -class SkWuffsFrameHolder final : public SkFrameHolder { -public: - SkWuffsFrameHolder() : INHERITED() {} - - void init(SkWuffsCodec* codec, int width, int height); - - // SkFrameHolder overrides. - const SkFrame* onGetFrame(int i) const override; - -private: - const SkWuffsCodec* fCodec; - - using INHERITED = SkFrameHolder; -}; - -class SkWuffsCodec final : public SkScalingCodec { -public: - SkWuffsCodec(SkEncodedInfo&& encodedInfo, - std::unique_ptr stream, - bool canSeek, - std::unique_ptr dec, - std::unique_ptr workbuf_ptr, - size_t workbuf_len, - wuffs_base__image_config imgcfg, - wuffs_base__io_buffer iobuf); - - const SkWuffsFrame* frame(int i) const; - - std::unique_ptr getEncodedData() const override; - -private: - // SkCodec overrides. - SkEncodedImageFormat onGetEncodedFormat() const override; - Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, int*) override; - const SkFrameHolder* getFrameHolder() const override; - Result onStartIncrementalDecode(const SkImageInfo& dstInfo, - void* dst, - size_t rowBytes, - const SkCodec::Options& options) override; - Result onIncrementalDecode(int* rowsDecoded) override; - int onGetFrameCount() override; - bool onGetFrameInfo(int, FrameInfo*) const override; - int onGetRepetitionCount() override; - - // Two separate implementations of onStartIncrementalDecode and - // onIncrementalDecode, named "one pass" and "two pass" decoding. One pass - // decoding writes directly from the Wuffs image decoder to the dst buffer - // (the dst argument to onStartIncrementalDecode). Two pass decoding first - // writes into an intermediate buffer, and then composites and transforms - // the intermediate buffer into the dst buffer. - // - // In the general case, we need the two pass decoder, because of Skia API - // features that Wuffs doesn't support (e.g. color correction, scaling, - // RGB565). But as an optimization, we use one pass decoding (it's faster - // and uses less memory) if applicable (see the assignment to - // fIncrDecOnePass that calculates when we can do so). - Result onStartIncrementalDecodeOnePass(const SkImageInfo& dstInfo, - uint8_t* dst, - size_t rowBytes, - const SkCodec::Options& options, - uint32_t pixelFormat, - size_t bytesPerPixel); - Result onStartIncrementalDecodeTwoPass(); - Result onIncrementalDecodeOnePass(); - Result onIncrementalDecodeTwoPass(); - - void onGetFrameCountInternal(); - Result seekFrame(int frameIndex); - Result resetDecoder(); - const char* decodeFrameConfig(); - const char* decodeFrame(); - void updateNumFullyReceivedFrames(); - - SkWuffsFrameHolder fFrameHolder; - std::unique_ptr fPrivStream; - std::unique_ptr fWorkbufPtr; - size_t fWorkbufLen; - - std::unique_ptr fDecoder; - - const uint64_t fFirstFrameIOPosition; - wuffs_base__frame_config fFrameConfig; - wuffs_base__pixel_config fPixelConfig; - wuffs_base__pixel_buffer fPixelBuffer; - wuffs_base__io_buffer fIOBuffer; - - // Incremental decoding state. - uint8_t* fIncrDecDst; - size_t fIncrDecRowBytes; - wuffs_base__pixel_blend fIncrDecPixelBlend; - bool fIncrDecOnePass; - bool fFirstCallToIncrementalDecode; - - // Lazily allocated intermediate pixel buffer, for two pass decoding. - std::unique_ptr fTwoPassPixbufPtr; - size_t fTwoPassPixbufLen; - - uint64_t fNumFullyReceivedFrames; - std::vector fFrames; - bool fFramesComplete; - - // If calling an fDecoder method returns an incomplete status, then - // fDecoder is suspended in a coroutine (i.e. waiting on I/O or halted on a - // non-recoverable error). To keep its internal proof-of-safety invariants - // consistent, there's only two things you can safely do with a suspended - // Wuffs object: resume the coroutine, or reset all state (memset to zero - // and start again). - // - // If fDecoderIsSuspended, and we aren't sure that we're going to resume - // the coroutine, then we will need to call this->resetDecoder before - // calling other fDecoder methods. - bool fDecoderIsSuspended; - - uint8_t fBuffer[SK_WUFFS_CODEC_BUFFER_SIZE]; - - const bool fCanSeek; - - using INHERITED = SkScalingCodec; -}; - -// -------------------------------- SkWuffsFrame implementation - -SkWuffsFrame::SkWuffsFrame(wuffs_base__frame_config* fc) - : INHERITED(fc->index()), - fIOPosition(fc->io_position()), - fReportedAlpha(fc->opaque_within_bounds() ? SkEncodedInfo::kOpaque_Alpha - : SkEncodedInfo::kUnpremul_Alpha) { - wuffs_base__rect_ie_u32 r = fc->bounds(); - this->setXYWH(r.min_incl_x, r.min_incl_y, r.width(), r.height()); - this->setDisposalMethod(wuffs_disposal_to_skia_disposal(fc->disposal())); - this->setDuration(fc->duration() / WUFFS_BASE__FLICKS_PER_MILLISECOND); - this->setBlend(fc->overwrite_instead_of_blend() ? SkCodecAnimation::Blend::kSrc - : SkCodecAnimation::Blend::kSrcOver); -} - -uint64_t SkWuffsFrame::ioPosition() const { - return fIOPosition; -} - -SkEncodedInfo::Alpha SkWuffsFrame::onReportedAlpha() const { - return fReportedAlpha; -} - -// -------------------------------- SkWuffsFrameHolder implementation - -void SkWuffsFrameHolder::init(SkWuffsCodec* codec, int width, int height) { - fCodec = codec; - // Initialize SkFrameHolder's (the superclass) fields. - fScreenWidth = width; - fScreenHeight = height; -} - -const SkFrame* SkWuffsFrameHolder::onGetFrame(int i) const { - return fCodec->frame(i); -} - -// -------------------------------- SkWuffsCodec implementation - -SkWuffsCodec::SkWuffsCodec(SkEncodedInfo&& encodedInfo, - std::unique_ptr stream, - bool canSeek, - std::unique_ptr dec, - std::unique_ptr workbuf_ptr, - size_t workbuf_len, - wuffs_base__image_config imgcfg, - wuffs_base__io_buffer iobuf) - : INHERITED(std::move(encodedInfo), - skcms_PixelFormat_RGBA_8888, - // Pass a nullptr SkStream to the SkCodec constructor. We - // manage the stream ourselves, as the default SkCodec behavior - // is too trigger-happy on rewinding the stream. - // - // TODO(https://crbug.com/370522089): See if `SkCodec` can be - // tweaked to avoid the need to hide the stream from it. - nullptr) - , fFrameHolder() - , fPrivStream(std::move(stream)) - , fWorkbufPtr(std::move(workbuf_ptr)) - , fWorkbufLen(workbuf_len) - , fDecoder(std::move(dec)) - , fFirstFrameIOPosition(imgcfg.first_frame_io_position()) - , fFrameConfig(wuffs_base__null_frame_config()) - , fPixelConfig(imgcfg.pixcfg) - , fPixelBuffer(wuffs_base__null_pixel_buffer()) - , fIOBuffer(wuffs_base__empty_io_buffer()) - , fIncrDecDst(nullptr) - , fIncrDecRowBytes(0) - , fIncrDecPixelBlend(WUFFS_BASE__PIXEL_BLEND__SRC) - , fIncrDecOnePass(false) - , fFirstCallToIncrementalDecode(false) - , fTwoPassPixbufPtr(nullptr, &sk_free) - , fTwoPassPixbufLen(0) - , fNumFullyReceivedFrames(0) - , fFramesComplete(false) - , fDecoderIsSuspended(false) - , fCanSeek(canSeek) { - fFrameHolder.init(this, imgcfg.pixcfg.width(), imgcfg.pixcfg.height()); - - // Initialize fIOBuffer's fields, copying any outstanding data from iobuf to - // fIOBuffer, as iobuf's backing array may not be valid for the lifetime of - // this SkWuffsCodec object, but fIOBuffer's backing array (fBuffer) is. - SkASSERT(iobuf.data.len == SK_WUFFS_CODEC_BUFFER_SIZE); - memmove(fBuffer, iobuf.data.ptr, iobuf.meta.wi); - fIOBuffer.data = wuffs_base__make_slice_u8(fBuffer, SK_WUFFS_CODEC_BUFFER_SIZE); - fIOBuffer.meta = iobuf.meta; -} - -const SkWuffsFrame* SkWuffsCodec::frame(int i) const { - if ((0 <= i) && (static_cast(i) < fFrames.size())) { - return &fFrames[i]; - } - return nullptr; -} - -SkEncodedImageFormat SkWuffsCodec::onGetEncodedFormat() const { - return SkEncodedImageFormat::kGIF; -} - -SkCodec::Result SkWuffsCodec::onGetPixels(const SkImageInfo& dstInfo, - void* dst, - size_t rowBytes, - const Options& options, - int* rowsDecoded) { - SkCodec::Result result = this->onStartIncrementalDecode(dstInfo, dst, rowBytes, options); - if (result != kSuccess) { - return result; - } - return this->onIncrementalDecode(rowsDecoded); -} - -const SkFrameHolder* SkWuffsCodec::getFrameHolder() const { - return &fFrameHolder; -} - -SkCodec::Result SkWuffsCodec::onStartIncrementalDecode(const SkImageInfo& dstInfo, - void* dst, - size_t rowBytes, - const SkCodec::Options& options) { - if (!dst) { - return SkCodec::kInvalidParameters; - } - if (options.fSubset) { - return SkCodec::kUnimplemented; - } - SkCodec::Result result = this->seekFrame(options.fFrameIndex); - if (result != SkCodec::kSuccess) { - return result; - } - - const char* status = this->decodeFrameConfig(); - if (status == wuffs_base__suspension__short_read) { - return SkCodec::kIncompleteInput; - } else if (status != nullptr) { - SkCodecPrintf("decodeFrameConfig: %s", status); - return SkCodec::kErrorInInput; - } - - uint32_t pixelFormat = WUFFS_BASE__PIXEL_FORMAT__INVALID; - size_t bytesPerPixel = 0; - - switch (dstInfo.colorType()) { - case kRGB_565_SkColorType: - pixelFormat = WUFFS_BASE__PIXEL_FORMAT__BGR_565; - bytesPerPixel = 2; - break; - case kBGRA_8888_SkColorType: - pixelFormat = WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL; - bytesPerPixel = 4; - break; - case kRGBA_8888_SkColorType: - pixelFormat = WUFFS_BASE__PIXEL_FORMAT__RGBA_NONPREMUL; - bytesPerPixel = 4; - break; - default: - break; - } - - // We can use "one pass" decoding if we have a Skia pixel format that Wuffs - // supports... - fIncrDecOnePass = (pixelFormat != WUFFS_BASE__PIXEL_FORMAT__INVALID) && - // ...and no color profile (as Wuffs does not support them)... - (!getEncodedInfo().profile()) && - // ...and we use the identity transform (as Wuffs does - // not support scaling). - (this->dimensions() == dstInfo.dimensions()); - - result = fIncrDecOnePass ? this->onStartIncrementalDecodeOnePass( - dstInfo, static_cast(dst), rowBytes, options, - pixelFormat, bytesPerPixel) - : this->onStartIncrementalDecodeTwoPass(); - if (result != SkCodec::kSuccess) { - return result; - } - - fIncrDecDst = static_cast(dst); - fIncrDecRowBytes = rowBytes; - fFirstCallToIncrementalDecode = true; - return SkCodec::kSuccess; -} - -SkCodec::Result SkWuffsCodec::onStartIncrementalDecodeOnePass(const SkImageInfo& dstInfo, - uint8_t* dst, - size_t rowBytes, - const SkCodec::Options& options, - uint32_t pixelFormat, - size_t bytesPerPixel) { - wuffs_base__pixel_config pixelConfig; - pixelConfig.set(pixelFormat, WUFFS_BASE__PIXEL_SUBSAMPLING__NONE, dstInfo.width(), - dstInfo.height()); - - wuffs_base__table_u8 table; - table.ptr = dst; - table.width = static_cast(dstInfo.width()) * bytesPerPixel; - table.height = dstInfo.height(); - table.stride = rowBytes; - - wuffs_base__status status = fPixelBuffer.set_from_table(&pixelConfig, table); - if (status.repr != nullptr) { - SkCodecPrintf("set_from_table: %s", status.message()); - return SkCodec::kInternalError; - } - - // SRC is usually faster than SRC_OVER, but for a dependent frame, dst is - // assumed to hold the previous frame's pixels (after processing the - // DisposalMethod). For one-pass decoding, we therefore use SRC_OVER. - if ((options.fFrameIndex != 0) && - (this->frame(options.fFrameIndex)->getRequiredFrame() != SkCodec::kNoFrame)) { - fIncrDecPixelBlend = WUFFS_BASE__PIXEL_BLEND__SRC_OVER; - } else { - SkSampler::Fill(dstInfo, dst, rowBytes, options.fZeroInitialized); - fIncrDecPixelBlend = WUFFS_BASE__PIXEL_BLEND__SRC; - } - - return SkCodec::kSuccess; -} - -SkCodec::Result SkWuffsCodec::onStartIncrementalDecodeTwoPass() { - // Either re-use the previously allocated "two pass" pixel buffer (and - // memset to zero), or allocate (and zero initialize) a new one. - bool already_zeroed = false; - - if (!fTwoPassPixbufPtr) { - uint64_t pixbuf_len = fPixelConfig.pixbuf_len(); - void* pixbuf_ptr_raw = (pixbuf_len <= SIZE_MAX) - ? sk_malloc_flags(pixbuf_len, SK_MALLOC_ZERO_INITIALIZE) - : nullptr; - if (!pixbuf_ptr_raw) { - return SkCodec::kInternalError; - } - fTwoPassPixbufPtr.reset(reinterpret_cast(pixbuf_ptr_raw)); - fTwoPassPixbufLen = SkToSizeT(pixbuf_len); - already_zeroed = true; - } - - wuffs_base__status status = fPixelBuffer.set_from_slice( - &fPixelConfig, wuffs_base__make_slice_u8(fTwoPassPixbufPtr.get(), fTwoPassPixbufLen)); - if (status.repr != nullptr) { - SkCodecPrintf("set_from_slice: %s", status.message()); - return SkCodec::kInternalError; - } - - if (!already_zeroed) { - uint32_t src_bits_per_pixel = fPixelConfig.pixel_format().bits_per_pixel(); - if ((src_bits_per_pixel == 0) || (src_bits_per_pixel % 8 != 0)) { - return SkCodec::kInternalError; - } - size_t src_bytes_per_pixel = src_bits_per_pixel / 8; - - wuffs_base__rect_ie_u32 frame_rect = fFrameConfig.bounds(); - wuffs_base__table_u8 pixels = fPixelBuffer.plane(0); - - uint8_t* ptr = pixels.ptr + (frame_rect.min_incl_y * pixels.stride) + - (frame_rect.min_incl_x * src_bytes_per_pixel); - size_t len = frame_rect.width() * src_bytes_per_pixel; - - // As an optimization, issue a single sk_bzero call, if possible. - // Otherwise, zero out each row separately. - if ((len == pixels.stride) && (frame_rect.min_incl_y < frame_rect.max_excl_y)) { - sk_bzero(ptr, len * (frame_rect.max_excl_y - frame_rect.min_incl_y)); - } else { - for (uint32_t y = frame_rect.min_incl_y; y < frame_rect.max_excl_y; y++) { - sk_bzero(ptr, len); - ptr += pixels.stride; - } - } - } - - fIncrDecPixelBlend = WUFFS_BASE__PIXEL_BLEND__SRC; - return SkCodec::kSuccess; -} - -SkCodec::Result SkWuffsCodec::onIncrementalDecode(int* rowsDecoded) { - if (!fIncrDecDst) { - return SkCodec::kInternalError; - } - - if (rowsDecoded) { - *rowsDecoded = dstInfo().height(); - } - - SkCodec::Result result = - fIncrDecOnePass ? this->onIncrementalDecodeOnePass() : this->onIncrementalDecodeTwoPass(); - if (result == SkCodec::kSuccess) { - fIncrDecDst = nullptr; - fIncrDecRowBytes = 0; - fIncrDecPixelBlend = WUFFS_BASE__PIXEL_BLEND__SRC; - fIncrDecOnePass = false; - } - return result; -} - -SkCodec::Result SkWuffsCodec::onIncrementalDecodeOnePass() { - const char* status = this->decodeFrame(); - if (status != nullptr) { - if (status == wuffs_base__suspension__short_read) { - return SkCodec::kIncompleteInput; - } else { - SkCodecPrintf("decodeFrame: %s", status); - return SkCodec::kErrorInInput; - } - } - return SkCodec::kSuccess; -} - -SkCodec::Result SkWuffsCodec::onIncrementalDecodeTwoPass() { - SkCodec::Result result = SkCodec::kSuccess; - const char* status = this->decodeFrame(); - bool independent; - SkAlphaType alphaType; - const int index = options().fFrameIndex; - if (index == 0) { - independent = true; - alphaType = to_alpha_type(getEncodedInfo().opaque()); - } else { - const SkWuffsFrame* f = this->frame(index); - independent = f->getRequiredFrame() == SkCodec::kNoFrame; - alphaType = to_alpha_type(f->reportedAlpha() == SkEncodedInfo::kOpaque_Alpha); - } - if (status != nullptr) { - if (status == wuffs_base__suspension__short_read) { - result = SkCodec::kIncompleteInput; - } else { - SkCodecPrintf("decodeFrame: %s", status); - result = SkCodec::kErrorInInput; - } - - if (!independent) { - // For a dependent frame, we cannot blend the partial result, since - // that will overwrite the contribution from prior frames. - return result; - } - } - - uint32_t src_bits_per_pixel = fPixelBuffer.pixcfg.pixel_format().bits_per_pixel(); - if ((src_bits_per_pixel == 0) || (src_bits_per_pixel % 8 != 0)) { - return SkCodec::kInternalError; - } - size_t src_bytes_per_pixel = src_bits_per_pixel / 8; - - wuffs_base__rect_ie_u32 frame_rect = fFrameConfig.bounds(); - if (fFirstCallToIncrementalDecode) { - if (frame_rect.width() > (SIZE_MAX / src_bytes_per_pixel)) { - return SkCodec::kInternalError; - } - - auto bounds = SkIRect::MakeLTRB(frame_rect.min_incl_x, frame_rect.min_incl_y, - frame_rect.max_excl_x, frame_rect.max_excl_y); - - // If the frame rect does not fill the output, ensure that those pixels are not - // left uninitialized. - if (independent && (bounds != this->bounds() || result != kSuccess)) { - SkSampler::Fill(dstInfo(), fIncrDecDst, fIncrDecRowBytes, options().fZeroInitialized); - } - fFirstCallToIncrementalDecode = false; - } else { - // Existing clients intend to only show frames beyond the first if they - // are complete (based on FrameInfo::fFullyReceived), since it might - // look jarring to draw a partial frame over an existing frame. If they - // changed their behavior and expected to continue decoding a partial - // frame after the first one, we'll need to update our blending code. - // Otherwise, if the frame were interlaced and not independent, the - // second pass may have an overlapping dirty_rect with the first, - // resulting in blending with the first pass. - SkASSERT(index == 0); - } - - // If the frame's dirty rect is empty, no need to swizzle. - wuffs_base__rect_ie_u32 dirty_rect = fDecoder->frame_dirty_rect(); - if (!dirty_rect.is_empty()) { - wuffs_base__table_u8 pixels = fPixelBuffer.plane(0); - - // The Wuffs model is that the dst buffer is the image, not the frame. - // The expectation is that you allocate the buffer once, but re-use it - // for the N frames, regardless of each frame's top-left co-ordinate. - // - // To get from the start (in the X-direction) of the image to the start - // of the dirty_rect, we adjust s by (dirty_rect.min_incl_x * src_bytes_per_pixel). - uint8_t* s = pixels.ptr + (dirty_rect.min_incl_y * pixels.stride) + - (dirty_rect.min_incl_x * src_bytes_per_pixel); - - // Currently, this is only used for GIF, which will never have an ICC profile. When it is - // used for other formats that might have one, we will need to transform from profiles that - // do not have corresponding SkColorSpaces. - SkASSERT(!getEncodedInfo().profile()); - - auto srcInfo = - getInfo().makeWH(dirty_rect.width(), dirty_rect.height()).makeAlphaType(alphaType); - SkBitmap src; - src.installPixels(srcInfo, s, pixels.stride); - SkPaint paint; - if (independent) { - paint.setBlendMode(SkBlendMode::kSrc); - } - - SkDraw draw; - draw.fDst.reset(dstInfo(), fIncrDecDst, fIncrDecRowBytes); - SkMatrix matrix = SkMatrix::RectToRect(SkRect::Make(this->dimensions()), - SkRect::Make(this->dstInfo().dimensions())); - draw.fCTM = &matrix; - SkRasterClip rc(SkIRect::MakeSize(this->dstInfo().dimensions())); - draw.fRC = &rc; - - SkMatrix translate = SkMatrix::Translate(dirty_rect.min_incl_x, dirty_rect.min_incl_y); - draw.drawBitmap(src, translate, nullptr, SkSamplingOptions(), paint); - } - - if (result == SkCodec::kSuccess) { - // On success, we are done using the "two pass" pixel buffer for this - // frame. We have the option of releasing its memory, but there is a - // trade-off. If decoding a subsequent frame will also need "two pass" - // decoding, it would have to re-allocate the buffer instead of just - // re-using it. On the other hand, if there is no subsequent frame, and - // the SkWuffsCodec object isn't deleted soon, then we are holding - // megabytes of memory longer than we need to. - // - // For example, when the Chromium web browser decodes the tags in - // a HTML page, the SkCodec object can live until navigating away from - // the page, which can be much longer than when the pixels are fully - // decoded, especially for a still (non-animated) image. Even for - // looping animations, caching the decoded frames (at the higher HTML - // renderer layer) may mean that each frame is only decoded once (at - // the lower SkCodec layer), in sequence. - // - // The heuristic we use here is to free the memory if we have decoded - // the last frame of the animation (or, for still images, the only - // frame). The output of the next decode request (if any) should be the - // same either way, but the steady state memory use should hopefully be - // lower than always keeping the fTwoPassPixbufPtr buffer up until the - // SkWuffsCodec destructor runs. - // - // This only applies to "two pass" decoding. "One pass" decoding does - // not allocate, free or otherwise use fTwoPassPixbufPtr. - if (fFramesComplete && (static_cast(options().fFrameIndex) == fFrames.size() - 1)) { - fTwoPassPixbufPtr.reset(nullptr); - fTwoPassPixbufLen = 0; - } - } - - return result; -} - -int SkWuffsCodec::onGetFrameCount() { - if (!fCanSeek) { - return 1; - } - - // It is valid, in terms of the SkCodec API, to call SkCodec::getFrameCount - // while in an incremental decode (after onStartIncrementalDecode returns - // and before onIncrementalDecode returns kSuccess). - // - // We should not advance the SkWuffsCodec' stream while doing so, even - // though other SkCodec implementations can return increasing values from - // onGetFrameCount when given more data. If we tried to do so, the - // subsequent resume of the incremental decode would continue reading from - // a different position in the I/O stream, leading to an incorrect error. - // - // Other SkCodec implementations can move the stream forward during - // onGetFrameCount because they assume that the stream is rewindable / - // seekable. For example, an alternative GIF implementation may choose to - // store, for each frame walked past when merely counting the number of - // frames, the I/O position of each of the frame's GIF data blocks. (A GIF - // frame's compressed data can have multiple data blocks, each at most 255 - // bytes in length). Obviously, this can require O(numberOfFrames) extra - // memory to store these I/O positions. The constant factor is small, but - // it's still O(N), not O(1). - // - // Wuffs and SkWuffsCodec try to minimize relying on the rewindable / - // seekable assumption. By design, Wuffs per se aims for O(1) memory use - // (after any pixel buffers are allocated) instead of O(N), and its I/O - // type, wuffs_base__io_buffer, is not necessarily rewindable or seekable. - // - // The Wuffs API provides a limited, optional form of seeking, to the start - // of an animation frame's data, but does not provide arbitrary save and - // load of its internal state whilst in the middle of an animation frame. - bool incrementalDecodeIsInProgress = fIncrDecDst != nullptr; - - if (!fFramesComplete && !incrementalDecodeIsInProgress) { - this->onGetFrameCountInternal(); - this->updateNumFullyReceivedFrames(); - } - return fFrames.size(); -} - -void SkWuffsCodec::onGetFrameCountInternal() { - size_t n = fFrames.size(); - int i = n ? n - 1 : 0; - if (this->seekFrame(i) != SkCodec::kSuccess) { - return; - } - - // Iterate through the frames, converting from Wuffs' - // wuffs_base__frame_config type to Skia's SkWuffsFrame type. - for (; i < INT_MAX; i++) { - const char* status = this->decodeFrameConfig(); - if (status == nullptr) { - // No-op. - } else if (status == wuffs_base__note__end_of_data) { - break; - } else { - return; - } - - if (static_cast(i) < fFrames.size()) { - continue; - } - fFrames.emplace_back(&fFrameConfig); - SkWuffsFrame* f = &fFrames[fFrames.size() - 1]; - fFrameHolder.setAlphaAndRequiredFrame(f); - } - - fFramesComplete = true; -} - -bool SkWuffsCodec::onGetFrameInfo(int i, SkCodec::FrameInfo* frameInfo) const { - if (!fCanSeek) { - // We haven't read forward in the stream, so this info isn't available. - return false; - } - - const SkWuffsFrame* f = this->frame(i); - if (!f) { - return false; - } - if (frameInfo) { - f->fillIn(frameInfo, static_cast(i) < this->fNumFullyReceivedFrames); - } - return true; -} - -int SkWuffsCodec::onGetRepetitionCount() { - // Convert from Wuffs's loop count to Skia's repeat count. Wuffs' uint32_t - // number is how many times to play the loop. Skia's int number is how many - // times to play the loop *after the first play*. Wuffs and Skia use 0 and - // kRepetitionCountInfinite respectively to mean loop forever. - uint32_t n = fDecoder->num_animation_loops(); - if (n == 0) { - return SkCodec::kRepetitionCountInfinite; - } - n--; - return n < INT_MAX ? n : INT_MAX; -} - -SkCodec::Result SkWuffsCodec::seekFrame(int frameIndex) { - if (fDecoderIsSuspended) { - SkCodec::Result res = this->resetDecoder(); - if (res != SkCodec::kSuccess) { - return res; - } - } - - uint64_t pos = 0; - if (frameIndex < 0) { - return SkCodec::kInternalError; - } else if (frameIndex == 0) { - pos = fFirstFrameIOPosition; - } else if (static_cast(frameIndex) < fFrames.size()) { - pos = fFrames[frameIndex].ioPosition(); - } else { - return SkCodec::kInternalError; - } - - if (!seek_buffer(&fIOBuffer, fPrivStream.get(), pos)) { - return SkCodec::kInternalError; - } - wuffs_base__status status = - fDecoder->restart_frame(frameIndex, fIOBuffer.reader_io_position()); - if (status.repr != nullptr) { - return SkCodec::kInternalError; - } - return SkCodec::kSuccess; -} - -SkCodec::Result SkWuffsCodec::resetDecoder() { - if (!fPrivStream->rewind()) { - return SkCodec::kInternalError; - } - fIOBuffer.meta = wuffs_base__empty_io_buffer_meta(); - - SkCodec::Result result = - reset_and_decode_image_config(fDecoder.get(), nullptr, &fIOBuffer, fPrivStream.get()); - if (result == SkCodec::kIncompleteInput) { - return SkCodec::kInternalError; - } else if (result != SkCodec::kSuccess) { - return result; - } - - fDecoderIsSuspended = false; - return SkCodec::kSuccess; -} - -const char* SkWuffsCodec::decodeFrameConfig() { - while (true) { - wuffs_base__status status = - fDecoder->decode_frame_config(&fFrameConfig, &fIOBuffer); - if ((status.repr == wuffs_base__suspension__short_read) && - fill_buffer(&fIOBuffer, fPrivStream.get())) { - continue; - } - fDecoderIsSuspended = !status.is_complete(); - this->updateNumFullyReceivedFrames(); - return status.repr; - } -} - -const char* SkWuffsCodec::decodeFrame() { - while (true) { - wuffs_base__status status = fDecoder->decode_frame( - &fPixelBuffer, &fIOBuffer, fIncrDecPixelBlend, - wuffs_base__make_slice_u8(fWorkbufPtr.get(), fWorkbufLen), nullptr); - if ((status.repr == wuffs_base__suspension__short_read) && - fill_buffer(&fIOBuffer, fPrivStream.get())) { - continue; - } - fDecoderIsSuspended = !status.is_complete(); - this->updateNumFullyReceivedFrames(); - return status.repr; - } -} - -void SkWuffsCodec::updateNumFullyReceivedFrames() { - // num_decoded_frames's return value, n, can change over time, both up and - // down, as we seek back and forth in the underlying stream. - // fNumFullyReceivedFrames is the highest n we've seen. - uint64_t n = fDecoder->num_decoded_frames(); - if (fNumFullyReceivedFrames < n) { - fNumFullyReceivedFrames = n; - } -} - -// We cannot use the SkCodec implementation since we pass nullptr to the superclass out of -// an abundance of caution w/r to rewinding the stream. -// -// TODO(https://crbug.com/370522089): See if `SkCodec` can be tweaked to avoid -// the need to hide the stream from it. -std::unique_ptr SkWuffsCodec::getEncodedData() const { - SkASSERT(fPrivStream); - return fPrivStream->duplicate(); -} - -namespace SkGifDecoder { - -bool IsGif(const void* buf, size_t bytesRead) { - constexpr const char* gif_ptr = "GIF8"; - constexpr size_t gif_len = 4; - return (bytesRead >= gif_len) && (memcmp(buf, gif_ptr, gif_len) == 0); -} - -std::unique_ptr MakeFromStream(std::unique_ptr stream, - SkCodec::SelectionPolicy selectionPolicy, - SkCodec::Result* result) { - SkASSERT(result); - if (!stream) { - *result = SkCodec::kInvalidInput; - return nullptr; - } - - bool canSeek = stream->hasPosition() && stream->hasLength(); - - if (selectionPolicy != SkCodec::SelectionPolicy::kPreferStillImage) { - // Some clients (e.g. Android) need to be able to seek the stream, but may - // not provide a seekable stream. Copy the stream to one that can seek. - if (!canSeek) { - auto data = SkCopyStreamToData(stream.get()); - stream = std::make_unique(std::move(data)); - canSeek = true; - } - } - - uint8_t buffer[SK_WUFFS_CODEC_BUFFER_SIZE]; - wuffs_base__io_buffer iobuf = - wuffs_base__make_io_buffer(wuffs_base__make_slice_u8(buffer, SK_WUFFS_CODEC_BUFFER_SIZE), - wuffs_base__empty_io_buffer_meta()); - wuffs_base__image_config imgcfg = wuffs_base__null_image_config(); - - // Wuffs is primarily a C library, not a C++ one. Furthermore, outside of - // the wuffs_base__etc types, the sizeof a file format specific type like - // GIF's wuffs_gif__decoder can vary between Wuffs versions. If p is of - // type wuffs_gif__decoder*, then the supported API treats p as a pointer - // to an opaque type: a private implementation detail. The API is always - // "set_foo(p, etc)" and not "p->foo = etc". - // - // See https://en.wikipedia.org/wiki/Opaque_pointer#C - // - // Thus, we don't use C++'s new operator (which requires knowing the sizeof - // the struct at compile time). Instead, we use sk_malloc_canfail, with - // sizeof__wuffs_gif__decoder returning the appropriate value for the - // (statically or dynamically) linked version of the Wuffs library. - // - // As a C (not C++) library, none of the Wuffs types have constructors or - // destructors. - // - // In RAII style, we can still use std::unique_ptr with these pointers, but - // we pair the pointer with sk_free instead of C++'s delete. - void* decoder_raw = sk_malloc_canfail(sizeof__wuffs_gif__decoder()); - if (!decoder_raw) { - *result = SkCodec::kInternalError; - return nullptr; - } - std::unique_ptr decoder( - reinterpret_cast(decoder_raw), &sk_free); - - SkCodec::Result reset_result = - reset_and_decode_image_config(decoder.get(), &imgcfg, &iobuf, stream.get()); - if (reset_result != SkCodec::kSuccess) { - *result = reset_result; - return nullptr; - } - - uint32_t width = imgcfg.pixcfg.width(); - uint32_t height = imgcfg.pixcfg.height(); - if ((width == 0) || (width > INT_MAX) || (height == 0) || (height > INT_MAX)) { - *result = SkCodec::kInvalidInput; - return nullptr; - } - - uint64_t workbuf_len = decoder->workbuf_len().max_incl; - void* workbuf_ptr_raw = nullptr; - if (workbuf_len) { - workbuf_ptr_raw = workbuf_len <= SIZE_MAX ? sk_malloc_canfail(workbuf_len) : nullptr; - if (!workbuf_ptr_raw) { - *result = SkCodec::kInternalError; - return nullptr; - } - } - std::unique_ptr workbuf_ptr( - reinterpret_cast(workbuf_ptr_raw), &sk_free); - - SkEncodedInfo::Color color = - (imgcfg.pixcfg.pixel_format().repr == WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL) - ? SkEncodedInfo::kBGRA_Color - : SkEncodedInfo::kRGBA_Color; - - // In Skia's API, the alpha we calculate here and return is only for the - // first frame. - SkEncodedInfo::Alpha alpha = imgcfg.first_frame_is_opaque() ? SkEncodedInfo::kOpaque_Alpha - : SkEncodedInfo::kBinary_Alpha; - - SkEncodedInfo encodedInfo = SkEncodedInfo::Make(width, height, color, alpha, 8); - - *result = SkCodec::kSuccess; - return std::unique_ptr(new SkWuffsCodec(std::move(encodedInfo), std::move(stream), - canSeek, - std::move(decoder), std::move(workbuf_ptr), - workbuf_len, imgcfg, iobuf)); -} - -std::unique_ptr Decode(std::unique_ptr stream, - SkCodec::Result* outResult, - SkCodecs::DecodeContext ctx) { - SkCodec::Result resultStorage; - if (!outResult) { - outResult = &resultStorage; - } - auto policy = SkCodec::SelectionPolicy::kPreferStillImage; - if (ctx) { - policy = *static_cast(ctx); - } - return MakeFromStream(std::move(stream), policy, outResult); -} - -std::unique_ptr Decode(sk_sp data, - SkCodec::Result* outResult, - SkCodecs::DecodeContext ctx) { - if (!data) { - if (outResult) { - *outResult = SkCodec::kInvalidInput; - } - return nullptr; - } - return Decode(SkMemoryStream::Make(std::move(data)), outResult, ctx); -} -} // namespace SkGifDecoder - diff --git a/gfx/skia/skia/src/codec/SkXmp.cpp b/gfx/skia/skia/src/codec/SkXmp.cpp deleted file mode 100644 index 662c7752d423..000000000000 --- a/gfx/skia/skia/src/codec/SkXmp.cpp +++ /dev/null @@ -1,669 +0,0 @@ -/* - * Copyright 2023 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "include/private/SkXmp.h" - -#include "include/core/SkColor.h" -#include "include/core/SkData.h" -#include "include/core/SkScalar.h" -#include "include/core/SkStream.h" -#include "include/private/SkGainmapInfo.h" -#include "include/utils/SkParse.h" -#include "src/codec/SkCodecPriv.h" -#include "src/xml/SkDOM.h" - -#include -#include -#include -#include -#include -#include - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// XMP parsing helper functions - -const char* kXmlnsPrefix = "xmlns:"; -const size_t kXmlnsPrefixLength = 6; - -static const char* get_namespace_prefix(const char* name) { - if (strlen(name) <= kXmlnsPrefixLength) { - return nullptr; - } - return name + kXmlnsPrefixLength; -} - -/* - * Given a node, see if that node has only one child with the indicated name. If so, see if that - * child has only a single child of its own, and that child is text. If all of that is the case - * then return the text, otherwise return nullptr. - * - * In the following example, innerText will be returned. - * innerText - * - * In the following examples, nullptr will be returned (because there are multiple children with - * childName in the first case, and because the child has children of its own in the second). - * innerTextAinnerTextB - * innerText - */ -static const char* get_unique_child_text(const SkDOM& dom, - const SkDOM::Node* node, - const std::string& childName) { - // Fail if there are multiple children with childName. - if (dom.countChildren(node, childName.c_str()) != 1) { - return nullptr; - } - const auto* child = dom.getFirstChild(node, childName.c_str()); - if (!child) { - return nullptr; - } - // Fail if the child has any children besides text. - if (dom.countChildren(child) != 1) { - return nullptr; - } - const auto* grandChild = dom.getFirstChild(child); - if (dom.getType(grandChild) != SkDOM::kText_Type) { - return nullptr; - } - // Return the text. - return dom.getName(grandChild); -} - -/* - * Given a node, find a child node of the specified type. - * - * If there exists a child node with name |prefix| + ":" + |type|, then return that child. - * - * If there exists a child node with name "rdf:type" that has attribute "rdf:resource" with value - * of |type|, then if there also exists a child node with name "rdf:value" with attribute - * "rdf:parseType" of "Resource", then return that child node with name "rdf:value". See Example - * 3 in section 7.9.2.5: RDF Typed Nodes. - * TODO(ccameron): This should also accept a URI for the type. - */ -static const SkDOM::Node* get_typed_child(const SkDOM* dom, - const SkDOM::Node* node, - const std::string& prefix, - const std::string& type) { - const auto name = prefix + std::string(":") + type; - const SkDOM::Node* child = dom->getFirstChild(node, name.c_str()); - if (child) { - return child; - } - - const SkDOM::Node* typeChild = dom->getFirstChild(node, "rdf:type"); - if (!typeChild) { - return nullptr; - } - const char* typeChildResource = dom->findAttr(typeChild, "rdf:resource"); - if (!typeChildResource || typeChildResource != type) { - return nullptr; - } - - const SkDOM::Node* valueChild = dom->getFirstChild(node, "rdf:value"); - if (!valueChild) { - return nullptr; - } - const char* valueChildParseType = dom->findAttr(valueChild, "rdf:parseType"); - if (!valueChildParseType || strcmp(valueChildParseType, "Resource") != 0) { - return nullptr; - } - return valueChild; -} - -/* - * Given a node, return its value for the specified attribute. - * - * This will first look for an attribute with the name |prefix| + ":" + |key|, and return the value - * for that attribute. - * - * This will then look for a child node of name |prefix| + ":" + |key|, and return the field value - * for that child. - */ -static const char* get_attr(const SkDOM* dom, - const SkDOM::Node* node, - const std::string& prefix, - const std::string& key) { - const auto name = prefix + ":" + key; - const char* attr = dom->findAttr(node, name.c_str()); - if (attr) { - return attr; - } - return get_unique_child_text(*dom, node, name); -} - -// Perform get_attr and parse the result as a bool. -static bool get_attr_bool(const SkDOM* dom, - const SkDOM::Node* node, - const std::string& prefix, - const std::string& key, - bool* outValue) { - const char* attr = get_attr(dom, node, prefix, key); - if (!attr) { - return false; - } - switch (SkParse::FindList(attr, "False,True")) { - case 0: - *outValue = false; - return true; - case 1: - *outValue = true; - return true; - default: - break; - } - return false; -} - -// Perform get_attr and parse the result as an int32_t. -static bool get_attr_int32(const SkDOM* dom, - const SkDOM::Node* node, - const std::string& prefix, - const std::string& key, - int32_t* value) { - const char* attr = get_attr(dom, node, prefix, key); - if (!attr) { - return false; - } - if (!SkParse::FindS32(attr, value)) { - return false; - } - return true; -} - -// Perform get_attr and parse the result as a float. -static bool get_attr_float(const SkDOM* dom, - const SkDOM::Node* node, - const std::string& prefix, - const std::string& key, - float* outValue) { - const char* attr = get_attr(dom, node, prefix, key); - if (!attr) { - return false; - } - SkScalar value = 0.f; - if (SkParse::FindScalar(attr, &value)) { - *outValue = value; - return true; - } - return false; -} - -// Perform get_attr and parse the result as three comma-separated floats. Return the result as an -// SkColor4f with the alpha component set to 1. -static bool get_attr_float3_as_list(const SkDOM* dom, - const SkDOM::Node* node, - const std::string& prefix, - const std::string& key, - SkColor4f* outValue) { - const auto name = prefix + ":" + key; - - // Fail if there are multiple children with childName. - if (dom->countChildren(node, name.c_str()) != 1) { - return false; - } - // Find the child. - const auto* child = dom->getFirstChild(node, name.c_str()); - if (!child) { - return false; - } - - // Search for the rdf:Seq child. - const auto* seq = dom->getFirstChild(child, "rdf:Seq"); - if (!seq) { - return false; - } - - size_t count = 0; - SkScalar values[3] = {0.f, 0.f, 0.f}; - for (const auto* liNode = dom->getFirstChild(seq, "rdf:li"); liNode; - liNode = dom->getNextSibling(liNode, "rdf:li")) { - if (count > 2) { - SkCodecPrintf("Too many items in list.\n"); - return false; - } - if (dom->countChildren(liNode) != 1) { - SkCodecPrintf("Item can only have one child.\n"); - return false; - } - const auto* liTextNode = dom->getFirstChild(liNode); - if (dom->getType(liTextNode) != SkDOM::kText_Type) { - SkCodecPrintf("Item's only child must be text.\n"); - return false; - } - const char* liText = dom->getName(liTextNode); - if (!liText) { - SkCodecPrintf("Failed to get item's text.\n"); - return false; - } - if (!SkParse::FindScalar(liText, values + count)) { - SkCodecPrintf("Failed to parse item's text to float.\n"); - return false; - } - count += 1; - } - if (count < 3) { - SkCodecPrintf("List didn't have enough items.\n"); - return false; - } - *outValue = {values[0], values[1], values[2], 1.f}; - return true; -} - -static bool get_attr_float3(const SkDOM* dom, - const SkDOM::Node* node, - const std::string& prefix, - const std::string& key, - SkColor4f* outValue) { - if (get_attr_float3_as_list(dom, node, prefix, key, outValue)) { - return true; - } - SkScalar value = -1.0; - if (get_attr_float(dom, node, prefix, key, &value)) { - *outValue = {value, value, value, 1.f}; - return true; - } - return false; -} - -static void find_uri_namespaces(const SkDOM& dom, - const SkDOM::Node* node, - size_t count, - const char* uris[], - const char* outNamespaces[]) { - // Search all attributes for xmlns:NAMESPACEi="URIi". - for (const auto* attr = dom.getFirstAttr(node); attr; attr = dom.getNextAttr(node, attr)) { - const char* attrName = dom.getAttrName(node, attr); - const char* attrValue = dom.getAttrValue(node, attr); - if (!attrName || !attrValue) { - continue; - } - // Make sure the name starts with "xmlns:". - if (strlen(attrName) <= kXmlnsPrefixLength) { - continue; - } - if (memcmp(attrName, kXmlnsPrefix, kXmlnsPrefixLength) != 0) { - continue; - } - // Search for a requested URI that matches. - for (size_t i = 0; i < count; ++i) { - if (strcmp(attrValue, uris[i]) != 0) { - continue; - } - outNamespaces[i] = attrName; - } - } -} - -// See SkXmp::findUriNamespaces. This function has the same behavior, but only searches -// a single SkDOM. -static const SkDOM::Node* find_uri_namespaces(const SkDOM& dom, - size_t count, - const char* uris[], - const char* outNamespaces[]) { - const SkDOM::Node* root = dom.getRootNode(); - if (!root) { - return nullptr; - } - - // Ensure that the root node identifies itself as XMP metadata. - const char* rootName = dom.getName(root); - if (!rootName || strcmp(rootName, "x:xmpmeta") != 0) { - return nullptr; - } - - // Iterate the children with name rdf:RDF. - const char* kRdf = "rdf:RDF"; - for (const auto* rdf = dom.getFirstChild(root, kRdf); rdf; - rdf = dom.getNextSibling(rdf, kRdf)) { - std::vector rdfNamespaces(count, nullptr); - find_uri_namespaces(dom, rdf, count, uris, rdfNamespaces.data()); - - // Iterate the children with name rdf::Description. - const char* kDesc = "rdf:Description"; - for (const auto* desc = dom.getFirstChild(rdf, kDesc); desc; - desc = dom.getNextSibling(desc, kDesc)) { - std::vector descNamespaces = rdfNamespaces; - find_uri_namespaces(dom, desc, count, uris, descNamespaces.data()); - - // If we have a match for all the requested URIs, return. - bool foundAllUris = true; - for (size_t i = 0; i < count; ++i) { - if (!descNamespaces[i]) { - foundAllUris = false; - break; - } - } - if (foundAllUris) { - for (size_t i = 0; i < count; ++i) { - outNamespaces[i] = descNamespaces[i]; - } - return desc; - } - } - } - return nullptr; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// SkXmpImpl - -class SkXmpImpl final : public SkXmp { -public: - SkXmpImpl() = default; - - bool getGainmapInfoAdobe(SkGainmapInfo* info) const override; - bool getGainmapInfoApple(float exifHdrHeadroom, SkGainmapInfo* info) const override; - bool getContainerGainmapLocation(size_t* offset, size_t* size) const override; - const char* getExtendedXmpGuid() const override; - // Parse the given xmp data and store it into either the standard (main) DOM or the extended - // DOM. Returns true on successful parsing. - bool parseDom(sk_sp xmpData, bool extended); - -private: - bool findUriNamespaces(size_t count, - const char* uris[], - const char* outNamespaces[], - const SkDOM** outDom, - const SkDOM::Node** outNode) const; - - SkDOM fStandardDOM; - SkDOM fExtendedDOM; -}; - -const char* SkXmpImpl::getExtendedXmpGuid() const { - const char* namespaces[1] = {nullptr}; - const char* uris[1] = {"http://ns.adobe.com/xmp/note/"}; - const auto* extendedNode = find_uri_namespaces(fStandardDOM, 1, uris, namespaces); - if (!extendedNode) { - return nullptr; - } - const auto xmpNotePrefix = get_namespace_prefix(namespaces[0]); - // Extract the GUID (the MD5 hash) of the extended metadata. - return get_attr(&fStandardDOM, extendedNode, xmpNotePrefix, "HasExtendedXMP"); -} - -bool SkXmpImpl::findUriNamespaces(size_t count, - const char* uris[], - const char* outNamespaces[], - const SkDOM** outDom, - const SkDOM::Node** outNode) const { - // See XMP Specification Part 3: Storage in files, Section 1.1.3.1: Extended XMP in JPEG: - // A JPEG reader must recompose the StandardXMP and ExtendedXMP into a single data model tree - // containing all of the XMP for the JPEG file, and remove the xmpNote:HasExtendedXMP property. - // This code does not do that. Instead, it maintains the two separate trees and searches them - // sequentially. - *outNode = find_uri_namespaces(fStandardDOM, count, uris, outNamespaces); - if (*outNode) { - *outDom = &fStandardDOM; - return true; - } - *outNode = find_uri_namespaces(fExtendedDOM, count, uris, outNamespaces); - if (*outNode) { - *outDom = &fExtendedDOM; - return true; - } - *outDom = nullptr; - return false; -} - -bool SkXmpImpl::getContainerGainmapLocation(size_t* outOffset, size_t* outSize) const { - // Find a node that matches the requested namespaces and URIs. - const char* namespaces[2] = {nullptr, nullptr}; - const char* uris[2] = {"http://ns.google.com/photos/1.0/container/", - "http://ns.google.com/photos/1.0/container/item/"}; - const SkDOM* dom = nullptr; - const SkDOM::Node* node = nullptr; - if (!findUriNamespaces(2, uris, namespaces, &dom, &node)) { - return false; - } - const char* containerPrefix = get_namespace_prefix(namespaces[0]); - const char* itemPrefix = get_namespace_prefix(namespaces[1]); - - // The node must have a Container:Directory child. - const auto* directory = get_typed_child(dom, node, containerPrefix, "Directory"); - if (!directory) { - SkCodecPrintf("Missing Container Directory"); - return false; - } - - // That Container:Directory must have a sequence of items. - const auto* seq = dom->getFirstChild(directory, "rdf:Seq"); - if (!seq) { - SkCodecPrintf("Missing rdf:Seq"); - return false; - } - - // Iterate through the items in the Container:Directory's sequence. Keep a running sum of the - // Item:Length of all items that appear before the GainMap. - bool isFirstItem = true; - size_t offset = 0; - for (const auto* li = dom->getFirstChild(seq, "rdf:li"); li; - li = dom->getNextSibling(li, "rdf:li")) { - // Each list item must contain a Container:Item. - const auto* item = get_typed_child(dom, li, containerPrefix, "Item"); - if (!item) { - SkCodecPrintf("List item does not have container Item.\n"); - return false; - } - // A Semantic is required for every item. - const char* itemSemantic = get_attr(dom, item, itemPrefix, "Semantic"); - if (!itemSemantic) { - SkCodecPrintf("Item is missing Semantic.\n"); - return false; - } - // A Mime is required for every item. - const char* itemMime = get_attr(dom, item, itemPrefix, "Mime"); - if (!itemMime) { - SkCodecPrintf("Item is missing Mime.\n"); - return false; - } - - if (isFirstItem) { - isFirstItem = false; - // The first item must be Primary. - if (strcmp(itemSemantic, "Primary") != 0) { - SkCodecPrintf("First item is not Primary.\n"); - return false; - } - // The first item has mime type image/jpeg (we are decoding a jpeg). - if (strcmp(itemMime, "image/jpeg") != 0) { - SkCodecPrintf("Primary does not report that it is image/jpeg.\n"); - return false; - } - // The first media item can contain a Padding attribute, which specifies additional - // padding between the end of the encoded primary image and the beginning of the next - // media item. Only the first media item can contain a Padding attribute. - int32_t padding = 0; - if (get_attr_int32(dom, item, itemPrefix, "Padding", &padding)) { - if (padding < 0) { - SkCodecPrintf("Item padding must be non-negative."); - return false; - } - offset += padding; - } - } else { - // A Length is required for all non-Primary items. - int32_t length = 0; - if (!get_attr_int32(dom, item, itemPrefix, "Length", &length)) { - SkCodecPrintf("Item length is absent."); - return false; - } - if (length < 0) { - SkCodecPrintf("Item length must be non-negative."); - return false; - } - // If this is not the recovery map, then read past it. - if (strcmp(itemSemantic, "GainMap") != 0) { - offset += length; - continue; - } - // The recovery map must have mime type image/jpeg in this implementation. - if (strcmp(itemMime, "image/jpeg") != 0) { - SkCodecPrintf("GainMap does not report that it is image/jpeg.\n"); - return false; - } - - // Populate the location in the file at which to find the gainmap image. - *outOffset = offset; - *outSize = length; - return true; - } - } - return false; -} - -// Return true if the specified XMP metadata identifies this image as an HDR gainmap. -bool SkXmpImpl::getGainmapInfoApple(float exifHdrHeadroom, SkGainmapInfo* info) const { - // Find a node that matches the requested namespaces and URIs. - const char* namespaces[2] = {nullptr, nullptr}; - const char* uris[2] = {"http://ns.apple.com/pixeldatainfo/1.0/", - "http://ns.apple.com/HDRGainMap/1.0/"}; - const SkDOM* dom = nullptr; - const SkDOM::Node* node = nullptr; - if (!findUriNamespaces(2, uris, namespaces, &dom, &node)) { - return false; - } - const char* adpiPrefix = get_namespace_prefix(namespaces[0]); - const char* hdrGainMapPrefix = get_namespace_prefix(namespaces[1]); - - const char* auxiliaryImageType = get_attr(dom, node, adpiPrefix, "AuxiliaryImageType"); - if (!auxiliaryImageType) { - SkCodecPrintf("Did not find AuxiliaryImageType.\n"); - return false; - } - if (strcmp(auxiliaryImageType, "urn:com:apple:photo:2020:aux:hdrgainmap") != 0) { - SkCodecPrintf("AuxiliaryImageType was not HDR gain map.\n"); - return false; - } - - // Require that the gainmap version be present, but do not require a specific version. - int32_t version = 0; - if (!get_attr_int32(dom, node, hdrGainMapPrefix, "HDRGainMapVersion", &version)) { - SkCodecPrintf("Did not find HDRGainMapVersion.\n"); - return false; - } - - // If the XMP also specifies a HDRGainMapHeadroom parameter, then prefer that parameter to the - // parameter specified in the base image Exif. - float hdrHeadroom = exifHdrHeadroom; - float xmpHdrHeadroom = 0.f; - if (get_attr_float(dom, node, hdrGainMapPrefix, "HDRGainMapHeadroom", &xmpHdrHeadroom)) { - hdrHeadroom = xmpHdrHeadroom; - } - - // This node will often have StoredFormat and NativeFormat children that have inner text that - // specifies the integer 'L008' (also known as kCVPixelFormatType_OneComponent8). - info->fGainmapRatioMin = {1.f, 1.f, 1.f, 1.f}; - info->fGainmapRatioMax = {hdrHeadroom, hdrHeadroom, hdrHeadroom, 1.f}; - info->fGainmapGamma = {1.f, 1.f, 1.f, 1.f}; - info->fEpsilonSdr = {0.f, 0.f, 0.f, 1.f}; - info->fEpsilonHdr = {0.f, 0.f, 0.f, 1.f}; - info->fDisplayRatioSdr = 1.f; - info->fDisplayRatioHdr = hdrHeadroom; - info->fBaseImageType = SkGainmapInfo::BaseImageType::kSDR; - info->fType = SkGainmapInfo::Type::kApple; - return true; -} - -bool SkXmpImpl::getGainmapInfoAdobe(SkGainmapInfo* outGainmapInfo) const { - // Find a node that matches the requested namespace and URI. - const char* namespaces[1] = {nullptr}; - const char* uris[1] = {"http://ns.adobe.com/hdr-gain-map/1.0/"}; - const SkDOM* dom = nullptr; - const SkDOM::Node* node = nullptr; - if (!findUriNamespaces(1, uris, namespaces, &dom, &node)) { - return false; - } - const char* hdrgmPrefix = get_namespace_prefix(namespaces[0]); - - // Require that hdrgm:Version="1.0" be present. - const char* version = get_attr(dom, node, hdrgmPrefix, "Version"); - if (!version) { - SkCodecPrintf("Version attribute is absent.\n"); - return false; - } - if (strcmp(version, "1.0") != 0) { - SkCodecPrintf("Version is \"%s\", not \"1.0\".\n", version); - return false; - } - - // Initialize the parameters to their defaults. - bool baseRenditionIsHDR = false; - SkColor4f gainMapMin = {0.f, 0.f, 0.f, 1.f}; // log2 value - SkColor4f gainMapMax = {1.f, 1.f, 1.f, 1.f}; // log2 value - SkColor4f gamma = {1.f, 1.f, 1.f, 1.f}; - SkColor4f offsetSdr = {1.f / 64.f, 1.f / 64.f, 1.f / 64.f, 0.f}; - SkColor4f offsetHdr = {1.f / 64.f, 1.f / 64.f, 1.f / 64.f, 0.f}; - SkScalar hdrCapacityMin = 0.f; // log2 value - SkScalar hdrCapacityMax = 1.f; // log2 value - - // Read all parameters that are present. - get_attr_bool(dom, node, hdrgmPrefix, "BaseRenditionIsHDR", &baseRenditionIsHDR); - get_attr_float3(dom, node, hdrgmPrefix, "GainMapMin", &gainMapMin); - get_attr_float3(dom, node, hdrgmPrefix, "GainMapMax", &gainMapMax); - get_attr_float3(dom, node, hdrgmPrefix, "Gamma", &gamma); - get_attr_float3(dom, node, hdrgmPrefix, "OffsetSDR", &offsetSdr); - get_attr_float3(dom, node, hdrgmPrefix, "OffsetHDR", &offsetHdr); - get_attr_float(dom, node, hdrgmPrefix, "HDRCapacityMin", &hdrCapacityMin); - get_attr_float(dom, node, hdrgmPrefix, "HDRCapacityMax", &hdrCapacityMax); - - // Translate all parameters to SkGainmapInfo's expected format. - if (!outGainmapInfo) { - return true; - } - const float kLog2 = std::log(2.f); - outGainmapInfo->fGainmapRatioMin = {std::exp(gainMapMin.fR * kLog2), - std::exp(gainMapMin.fG * kLog2), - std::exp(gainMapMin.fB * kLog2), - 1.f}; - outGainmapInfo->fGainmapRatioMax = {std::exp(gainMapMax.fR * kLog2), - std::exp(gainMapMax.fG * kLog2), - std::exp(gainMapMax.fB * kLog2), - 1.f}; - outGainmapInfo->fGainmapGamma = {1.f / gamma.fR, 1.f / gamma.fG, 1.f / gamma.fB, 1.f}; - outGainmapInfo->fEpsilonSdr = offsetSdr; - outGainmapInfo->fEpsilonHdr = offsetHdr; - outGainmapInfo->fDisplayRatioSdr = std::exp(hdrCapacityMin * kLog2); - outGainmapInfo->fDisplayRatioHdr = std::exp(hdrCapacityMax * kLog2); - if (baseRenditionIsHDR) { - outGainmapInfo->fBaseImageType = SkGainmapInfo::BaseImageType::kHDR; - } else { - outGainmapInfo->fBaseImageType = SkGainmapInfo::BaseImageType::kSDR; - } - return true; -} - -bool SkXmpImpl::parseDom(sk_sp xmpData, bool extended) { - SkDOM* dom = extended ? &fExtendedDOM : &fStandardDOM; - auto xmpdStream = SkMemoryStream::Make(std::move(xmpData)); - if (!dom->build(*xmpdStream)) { - SkCodecPrintf("Failed to parse XMP %s metadata.\n", extended ? "extended" : "standard"); - return false; - } - return true; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// SkXmp - -std::unique_ptr SkXmp::Make(sk_sp xmpData) { - std::unique_ptr xmp(new SkXmpImpl); - if (!xmp->parseDom(std::move(xmpData), /*extended=*/false)) { - return nullptr; - } - return xmp; -} - -std::unique_ptr SkXmp::Make(sk_sp xmpStandard, sk_sp xmpExtended) { - std::unique_ptr xmp(new SkXmpImpl); - if (!xmp->parseDom(std::move(xmpStandard), /*extended=*/false)) { - return nullptr; - } - // Try to parse extended xmp but ignore the return value: if parsing fails, we'll still return - // the standard xmp. - (void)xmp->parseDom(std::move(xmpExtended), /*extended=*/true); - return xmp; -} diff --git a/gfx/skia/skia/src/core/Sk4px.h b/gfx/skia/skia/src/core/Sk4px.h index ec7653f34c78..d5d481e7e300 100644 --- a/gfx/skia/skia/src/core/Sk4px.h +++ b/gfx/skia/skia/src/core/Sk4px.h @@ -9,8 +9,8 @@ #define Sk4px_DEFINED #include "include/core/SkColor.h" -#include "include/private/SkColorData.h" #include "src/base/SkVx.h" +#include "src/core/SkColorData.h" // 1, 2 or 4 SkPMColors, generally vectorized. class Sk4px { diff --git a/gfx/skia/skia/src/core/SkAAClip.cpp b/gfx/skia/skia/src/core/SkAAClip.cpp index 787f23413832..79e9d4fa8cc0 100644 --- a/gfx/skia/skia/src/core/SkAAClip.cpp +++ b/gfx/skia/skia/src/core/SkAAClip.cpp @@ -11,7 +11,6 @@ #include "include/core/SkPath.h" #include "include/core/SkRegion.h" #include "include/core/SkTypes.h" -#include "include/private/SkColorData.h" #include "include/private/base/SkCPUTypes.h" #include "include/private/base/SkDebug.h" #include "include/private/base/SkMacros.h" @@ -20,6 +19,7 @@ #include "include/private/base/SkTDArray.h" #include "include/private/base/SkTo.h" #include "src/core/SkBlitter.h" +#include "src/core/SkColorData.h" #include "src/core/SkMask.h" #include "src/core/SkScan.h" @@ -1412,8 +1412,10 @@ bool SkAAClip::setPath(const SkPath& path, const SkIRect& clip, bool doAA) { ibounds = clip; } else { path.getBounds().roundOut(&ibounds); - // Since clip is already validated with isEmpty, it is safe to use isEmpty64 - // for ibounds as it will be intersected with clip after. + // It's possible the bounds of our path might exceed SK_MaxS32 in width + // but since our clip is within that width (otherwise isEmpty() above + // would catch it), we can use isEmpty64() safely here. blitPath will + // interesect the two bounds before drawing. if (ibounds.isEmpty64() || !ibounds.intersect(clip)) { return this->setEmpty(); } diff --git a/gfx/skia/skia/src/core/SkAnalyticEdge.cpp b/gfx/skia/skia/src/core/SkAnalyticEdge.cpp index a9284c864307..1c27e6ce4fb4 100644 --- a/gfx/skia/skia/src/core/SkAnalyticEdge.cpp +++ b/gfx/skia/skia/src/core/SkAnalyticEdge.cpp @@ -132,11 +132,11 @@ static inline SkFixed quick_inverse(SkFDot6 x) { } static inline SkFixed quick_div(SkFDot6 a, SkFDot6 b) { - const int kMinBits = 3; // abs(b) should be at least (1 << kMinBits) for quick division - const int kMaxBits = 31; // Number of bits available in signed int + constexpr int kMinBits = 3; // abs(b) should be at least (1 << kMinBits) for quick division + constexpr int kMaxBits = 31; // Number of bits available in signed int // Given abs(b) <= (1 << kMinBits), the inverse of abs(b) is at most 1 << (22 - kMinBits) in // SkFixed format. Hence abs(a) should be less than kMaxAbsA - const int kMaxAbsA = 1 << (kMaxBits - (22 - kMinBits)); + constexpr int kMaxAbsA = 1 << (kMaxBits - (22 - kMinBits)); SkFDot6 abs_a = SkAbs32(a); SkFDot6 abs_b = SkAbs32(b); if (abs_b >= (1 << kMinBits) && abs_b < kInverseTableSize && abs_a < kMaxAbsA) { @@ -155,27 +155,27 @@ static inline SkFixed quick_div(SkFDot6 a, SkFDot6 b) { bool SkAnalyticEdge::setLine(const SkPoint& p0, const SkPoint& p1) { // We must set X/Y using the same way (e.g., times 4, to FDot6, then to Fixed) as Quads/Cubics. // Otherwise the order of the edge might be wrong due to precision limit. - const int accuracy = kDefaultAccuracy; + constexpr int accuracy = kDefaultAccuracy; #ifdef SK_RASTERIZE_EVEN_ROUNDING SkFixed x0 = SkFDot6ToFixed(SkScalarRoundToFDot6(p0.fX, accuracy)) >> accuracy; SkFixed y0 = SnapY(SkFDot6ToFixed(SkScalarRoundToFDot6(p0.fY, accuracy)) >> accuracy); SkFixed x1 = SkFDot6ToFixed(SkScalarRoundToFDot6(p1.fX, accuracy)) >> accuracy; SkFixed y1 = SnapY(SkFDot6ToFixed(SkScalarRoundToFDot6(p1.fY, accuracy)) >> accuracy); #else - const int multiplier = (1 << kDefaultAccuracy); + constexpr int multiplier = (1 << kDefaultAccuracy); SkFixed x0 = SkFDot6ToFixed(SkScalarToFDot6(p0.fX * multiplier)) >> accuracy; SkFixed y0 = SnapY(SkFDot6ToFixed(SkScalarToFDot6(p0.fY * multiplier)) >> accuracy); SkFixed x1 = SkFDot6ToFixed(SkScalarToFDot6(p1.fX * multiplier)) >> accuracy; SkFixed y1 = SnapY(SkFDot6ToFixed(SkScalarToFDot6(p1.fY * multiplier)) >> accuracy); #endif - int winding = 1; + Winding winding = Winding::kCW; if (y0 > y1) { using std::swap; swap(x0, x1); swap(y0, y1); - winding = -1; + winding = Winding::kCCW; } // are we a zero-height line? @@ -196,14 +196,18 @@ bool SkAnalyticEdge::setLine(const SkPoint& p0, const SkPoint& p1) { fDY = dx == 0 || slope == 0 ? SK_MaxS32 : absSlope < kInverseTableSize ? quick_inverse(absSlope) : SkAbs32(quick_div(dy, dx)); - fEdgeType = kLine_Type; + fEdgeType = Type::kLine; fCurveCount = 0; - fWinding = SkToS8(winding); + fWinding = winding; fCurveShift = 0; return true; } +static SkAnalyticEdge::Winding swap_winding(SkAnalyticEdge::Winding w) { + return static_cast(static_cast(w) * -1); +} + // This will become a bottleneck for small ovals rendering if we call SkFixedDiv twice here. // Therefore, we'll let the outter function compute the slope once and send in the value. // Moreover, we'll compute fDY by quickly lookup the inverse table (if possible). @@ -211,7 +215,7 @@ bool SkAnalyticEdge::updateLine(SkFixed x0, SkFixed y0, SkFixed x1, SkFixed y1, // Since we send in the slope, we can no longer snap y inside this function. // If we don't send in the slope, or we do some more sophisticated snapping, this function // could be a performance bottleneck. - SkASSERT(fWinding == 1 || fWinding == -1); + SkASSERT(fWinding == Winding::kCW || fWinding == Winding::kCCW); SkASSERT(fCurveCount != 0); // We don't chop at y extrema for cubics so the y is not guaranteed to be increasing for them. @@ -220,7 +224,7 @@ bool SkAnalyticEdge::updateLine(SkFixed x0, SkFixed y0, SkFixed x1, SkFixed y1, using std::swap; swap(x0, x1); swap(y0, y1); - fWinding = -fWinding; + fWinding = swap_winding(fWinding); } SkASSERT(y0 <= y1); @@ -277,7 +281,7 @@ bool SkAnalyticQuadraticEdge::setQuadratic(const SkPoint pts[3]) { fQEdge.fQLastY = SnapY(fQEdge.fQLastY); fWinding = fQEdge.fWinding; - fEdgeType = kQuad_Type; + fEdgeType = Type::kQuad; fCurveCount = fQEdge.fCurveCount; fCurveShift = fQEdge.fCurveShift; @@ -305,7 +309,9 @@ bool SkAnalyticQuadraticEdge::updateQuadratic() { { newx = oldx + (dx >> shift); newy = oldy + (dy >> shift); - if (SkAbs32(dy >> shift) >= SK_Fixed1 * 2) { // only snap when dy is large enough + // only snap when dy is large enough and dx/dy isn't too large + if (SkAbs32(dy >> shift) >= SK_Fixed1 * 2 && + SkLeftShift((int64_t) SkAbs32(dy), 6) > SkAbs32(dx)) { SkFDot6 diffY = SkFixedToFDot6(newy - fSnappedY); slope = diffY ? quick_div(SkFixedToFDot6(newx - fSnappedX), diffY) : SK_MaxS32; @@ -327,8 +333,8 @@ bool SkAnalyticQuadraticEdge::updateQuadratic() { newy = fQEdge.fQLastY; newSnappedY = newy; newSnappedX = newx; - SkFDot6 diffY = (newy - fSnappedY) >> 10; - slope = diffY ? quick_div((newx - fSnappedX) >> 10, diffY) : SK_MaxS32; + SkFDot6 diffY = SkFixedToFDot6(newy - fSnappedY); + slope = diffY ? quick_div(SkFixedToFDot6(newx - fSnappedX), diffY) : SK_MaxS32; } if (slope < SK_MaxS32) { success = this->updateLine(fSnappedX, fSnappedY, newSnappedX, newSnappedY, slope); @@ -368,7 +374,7 @@ bool SkAnalyticCubicEdge::setCubic(const SkPoint pts[4], bool sortY) { fCEdge.fCLastY = SnapY(fCEdge.fCLastY); fWinding = fCEdge.fWinding; - fEdgeType = kCubic_Type; + fEdgeType = Type::kCubic; fCurveCount = fCEdge.fCurveCount; fCurveShift = fCEdge.fCurveShift; fCubicDShift = fCEdge.fCubicDShift; diff --git a/gfx/skia/skia/src/core/SkAnalyticEdge.h b/gfx/skia/skia/src/core/SkAnalyticEdge.h index 5952dfc95363..724ad3cdf8ac 100644 --- a/gfx/skia/skia/src/core/SkAnalyticEdge.h +++ b/gfx/skia/skia/src/core/SkAnalyticEdge.h @@ -20,11 +20,8 @@ struct SkPoint; struct SkAnalyticEdge { // Similar to SkEdge, the conic edges will be converted to quadratic edges - enum Type { - kLine_Type, - kQuad_Type, - kCubic_Type - }; + using Type = SkEdge::Type; + using Winding = SkEdge::Winding; SkAnalyticEdge* fNext; SkAnalyticEdge* fPrev; @@ -38,17 +35,17 @@ struct SkAnalyticEdge { SkFixed fDY; // abs(1/fDX); may be SK_MaxS32 when fDX is close to 0. // fDY is only used for blitting trapezoids. - Type fEdgeType; // Remembers the *initial* edge type + Type fEdgeType; // Remembers the *initial* edge type int8_t fCurveCount; // only used by kQuad(+) and kCubic(-) uint8_t fCurveShift; // appled to all Dx/DDx/DDDx except for fCubicDShift exception uint8_t fCubicDShift; // applied to fCDx and fCDy only in cubic - int8_t fWinding; // 1 or -1 + Winding fWinding; - static const int kDefaultAccuracy = 2; // default accuracy for snapping + static constexpr int kDefaultAccuracy = 2; // default accuracy for snapping static inline SkFixed SnapY(SkFixed y) { - const int accuracy = kDefaultAccuracy; + constexpr int accuracy = kDefaultAccuracy; // This approach is safer than left shift, round, then right shift return ((unsigned)y + (SK_Fixed1 >> (accuracy + 1))) >> (16 - accuracy) << (16 - accuracy); } @@ -82,8 +79,12 @@ struct SkAnalyticEdge { #ifdef SK_DEBUG void dump() const { SkDebugf("edge: upperY:%d lowerY:%d y:%g x:%g dx:%g w:%d\n", - fUpperY, fLowerY, SkFixedToFloat(fY), SkFixedToFloat(fX), - SkFixedToFloat(fDX), fWinding); + fUpperY, + fLowerY, + SkFixedToFloat(fY), + SkFixedToFloat(fX), + SkFixedToFloat(fDX), + static_cast(fWinding)); } void validate() const { @@ -92,7 +93,7 @@ struct SkAnalyticEdge { SkASSERT(fNext->fPrev == this); SkASSERT(fUpperY < fLowerY); - SkASSERT(SkAbs32(fWinding) == 1); + SkASSERT(fWinding == Winding::kCW || fWinding == Winding::kCCW); } #endif }; diff --git a/gfx/skia/skia/src/core/SkAutoBlitterChoose.h b/gfx/skia/skia/src/core/SkAutoBlitterChoose.h index ba1e5af53927..6e91bd92feec 100644 --- a/gfx/skia/skia/src/core/SkAutoBlitterChoose.h +++ b/gfx/skia/skia/src/core/SkAutoBlitterChoose.h @@ -25,15 +25,17 @@ public: SkAutoBlitterChoose(const SkDrawBase& draw, const SkMatrix* ctm, const SkPaint& paint, - bool drawCoverage = false) { + SkDrawCoverage drawCoverage = SkDrawCoverage::kNo) { this->choose(draw, ctm, paint, drawCoverage); } SkBlitter* operator->() { return fBlitter; } SkBlitter* get() const { return fBlitter; } - SkBlitter* choose(const SkDrawBase& draw, const SkMatrix* ctm, - const SkPaint& paint, bool drawCoverage = false) { + SkBlitter* choose(const SkDrawBase& draw, + const SkMatrix* ctm, + const SkPaint& paint, + SkDrawCoverage drawCoverage = SkDrawCoverage::kNo) { SkASSERT(!fBlitter); fBlitter = draw.fBlitterChooser(draw.fDst, ctm ? *ctm : *draw.fCTM, @@ -49,7 +51,14 @@ private: // Owned by fAlloc, which will handle the delete. SkBlitter* fBlitter = nullptr; - SkSTArenaAlloc fAlloc; + // This was determined experimentally by adding logging to SkSTArenaAlloc's destructor + // to see what the biggest size observed was while doing some browsing on Chromium. + // It's a bit tricky to determine this value statically, as the SkRasterPipelineBuilder + // uses the allocator for several things, as do the shaders which make use of the legacy + // shader context. In other cases it's easier because the allocator only has the blitter + // itself and one could do a static_assert using sizeof(). + static constexpr size_t kStackMemory = 2736; + SkSTArenaAlloc fAlloc; }; #endif diff --git a/gfx/skia/skia/src/core/SkBitmapDevice.cpp b/gfx/skia/skia/src/core/SkBitmapDevice.cpp index ae4149d69372..fc53cd966946 100644 --- a/gfx/skia/skia/src/core/SkBitmapDevice.cpp +++ b/gfx/skia/skia/src/core/SkBitmapDevice.cpp @@ -11,7 +11,6 @@ #include "include/core/SkBlender.h" #include "include/core/SkClipOp.h" #include "include/core/SkColorType.h" -#include "include/core/SkImage.h" #include "include/core/SkImageInfo.h" #include "include/core/SkMatrix.h" #include "include/core/SkPaint.h" @@ -58,10 +57,8 @@ struct Bounder { }; class SkDrawTiler { - enum { - // 8K is 1 too big, since 8K << supersample == 32768 which is too big for SkFixed - kMaxDim = 8192 - 1 - }; + // 8K is 1 too big, since 8K << supersample == 32768 which is too big for SkFixed + static constexpr int kMaxDim = 8192 - 1; SkBitmapDevice* fDevice; SkPixmap fRootPixmap; @@ -355,19 +352,11 @@ void SkBitmapDevice::drawRect(const SkRect& r, const SkPaint& paint) { } void SkBitmapDevice::drawOval(const SkRect& oval, const SkPaint& paint) { - // call the VIRTUAL version, so any subclasses who do handle drawPath aren't - // required to override drawOval. this->drawPath(SkPath::Oval(oval), paint, true); } void SkBitmapDevice::drawRRect(const SkRRect& rrect, const SkPaint& paint) { -#ifdef SK_IGNORE_BLURRED_RRECT_OPT - // call the VIRTUAL version, so any subclasses who do handle drawPath aren't - // required to override drawRRect. - this->drawPath(SkPath::RRect(rrect), paint, true); -#else LOOP_TILER( drawRRect(rrect, paint), Bounder(rrect.getBounds(), paint)) -#endif } void SkBitmapDevice::drawPath(const SkPath& path, @@ -589,15 +578,6 @@ void SkBitmapDevice::drawSpecial(SkSpecialImage* src, draw.drawBitmap(resultBM, SkMatrix::I(), nullptr, sampling, paint); } } -sk_sp SkBitmapDevice::makeSpecial(const SkBitmap& bitmap) { - return SkSpecialImages::MakeFromRaster(bitmap.bounds(), bitmap, this->surfaceProps()); -} - -sk_sp SkBitmapDevice::makeSpecial(const SkImage* image) { - return SkSpecialImages::MakeFromRaster(SkIRect::MakeWH(image->width(), image->height()), - image->makeNonTextureImage(), - this->surfaceProps()); -} sk_sp SkBitmapDevice::snapSpecial(const SkIRect& bounds, bool forceCopy) { if (forceCopy) { diff --git a/gfx/skia/skia/src/core/SkBitmapDevice.h b/gfx/skia/skia/src/core/SkBitmapDevice.h index f7696637075d..76a69fb0c063 100644 --- a/gfx/skia/skia/src/core/SkBitmapDevice.h +++ b/gfx/skia/skia/src/core/SkBitmapDevice.h @@ -42,7 +42,7 @@ struct SkPoint; struct SkRSXform; /////////////////////////////////////////////////////////////////////////////// -class SkBitmapDevice : public SkDevice { +class SkBitmapDevice final : public SkDevice { public: /** * Construct a new device with the specified bitmap as its backend. It is @@ -103,8 +103,6 @@ public: void drawSpecial(SkSpecialImage*, const SkMatrix&, const SkSamplingOptions&, const SkPaint&, SkCanvas::SrcRectConstraint) override; - sk_sp makeSpecial(const SkBitmap&) override; - sk_sp makeSpecial(const SkImage*) override; sk_sp snapSpecial(const SkIRect&, bool forceCopy = false) override; sk_sp createDevice(const CreateInfo&, const SkPaint*) override; @@ -116,7 +114,6 @@ public: void* getRasterHandle() const override { return fRasterHandle; } private: - // friend class SkCanvas; friend class SkDraw; friend class SkDrawBase; friend class SkDrawTiler; diff --git a/gfx/skia/skia/src/core/SkBitmapProcState.cpp b/gfx/skia/skia/src/core/SkBitmapProcState.cpp index 1e1fc794a7bc..0d0707657099 100644 --- a/gfx/skia/skia/src/core/SkBitmapProcState.cpp +++ b/gfx/skia/skia/src/core/SkBitmapProcState.cpp @@ -8,12 +8,12 @@ #include "src/core/SkBitmapProcState.h" #include "include/core/SkAlphaType.h" -#include "include/core/SkColorPriv.h" #include "include/core/SkColorType.h" #include "include/core/SkImageInfo.h" #include "include/core/SkTileMode.h" #include "include/private/base/SkMacros.h" #include "include/private/base/SkTPin.h" +#include "src/core/SkColorPriv.h" #include "src/core/SkMemset.h" #include "src/core/SkMipmapAccessor.h" diff --git a/gfx/skia/skia/src/core/SkBlendMode.cpp b/gfx/skia/skia/src/core/SkBlendMode.cpp index db5aed50e85a..fdc01b8d5758 100644 --- a/gfx/skia/skia/src/core/SkBlendMode.cpp +++ b/gfx/skia/skia/src/core/SkBlendMode.cpp @@ -9,9 +9,9 @@ #include "include/core/SkColor.h" #include "include/core/SkPaint.h" -#include "include/private/SkColorData.h" #include "src/base/SkVx.h" #include "src/core/SkBlendModePriv.h" +#include "src/core/SkColorData.h" #include "src/core/SkRasterPipeline.h" #include "src/core/SkRasterPipelineOpContexts.h" #include "src/core/SkRasterPipelineOpList.h" @@ -95,41 +95,41 @@ bool SkBlendMode_AsCoeff(SkBlendMode mode, SkBlendModeCoeff* src, SkBlendModeCoe } void SkBlendMode_AppendStages(SkBlendMode mode, SkRasterPipeline* p) { - auto stage = SkRasterPipelineOp::srcover; switch (mode) { - case SkBlendMode::kClear: stage = SkRasterPipelineOp::clear; break; + case SkBlendMode::kClear: p->append(SkRasterPipelineOp::clear); return; case SkBlendMode::kSrc: return; // This stage is a no-op. - case SkBlendMode::kDst: stage = SkRasterPipelineOp::move_dst_src; break; - case SkBlendMode::kSrcOver: stage = SkRasterPipelineOp::srcover; break; - case SkBlendMode::kDstOver: stage = SkRasterPipelineOp::dstover; break; - case SkBlendMode::kSrcIn: stage = SkRasterPipelineOp::srcin; break; - case SkBlendMode::kDstIn: stage = SkRasterPipelineOp::dstin; break; - case SkBlendMode::kSrcOut: stage = SkRasterPipelineOp::srcout; break; - case SkBlendMode::kDstOut: stage = SkRasterPipelineOp::dstout; break; - case SkBlendMode::kSrcATop: stage = SkRasterPipelineOp::srcatop; break; - case SkBlendMode::kDstATop: stage = SkRasterPipelineOp::dstatop; break; - case SkBlendMode::kXor: stage = SkRasterPipelineOp::xor_; break; - case SkBlendMode::kPlus: stage = SkRasterPipelineOp::plus_; break; - case SkBlendMode::kModulate: stage = SkRasterPipelineOp::modulate; break; + case SkBlendMode::kDst: p->append(SkRasterPipelineOp::move_dst_src); return; + case SkBlendMode::kSrcOver: p->append(SkRasterPipelineOp::srcover); return; + case SkBlendMode::kDstOver: p->append(SkRasterPipelineOp::dstover); return; + case SkBlendMode::kSrcIn: p->append(SkRasterPipelineOp::srcin); return; + case SkBlendMode::kDstIn: p->append(SkRasterPipelineOp::dstin); return; + case SkBlendMode::kSrcOut: p->append(SkRasterPipelineOp::srcout); return; + case SkBlendMode::kDstOut: p->append(SkRasterPipelineOp::dstout); return; + case SkBlendMode::kSrcATop: p->append(SkRasterPipelineOp::srcatop); return; + case SkBlendMode::kDstATop: p->append(SkRasterPipelineOp::dstatop); return; + case SkBlendMode::kXor: p->append(SkRasterPipelineOp::xor_); return; + case SkBlendMode::kPlus: p->append(SkRasterPipelineOp::plus_); return; + case SkBlendMode::kModulate: p->append(SkRasterPipelineOp::modulate); return; - case SkBlendMode::kScreen: stage = SkRasterPipelineOp::screen; break; - case SkBlendMode::kOverlay: stage = SkRasterPipelineOp::overlay; break; - case SkBlendMode::kDarken: stage = SkRasterPipelineOp::darken; break; - case SkBlendMode::kLighten: stage = SkRasterPipelineOp::lighten; break; - case SkBlendMode::kColorDodge: stage = SkRasterPipelineOp::colordodge; break; - case SkBlendMode::kColorBurn: stage = SkRasterPipelineOp::colorburn; break; - case SkBlendMode::kHardLight: stage = SkRasterPipelineOp::hardlight; break; - case SkBlendMode::kSoftLight: stage = SkRasterPipelineOp::softlight; break; - case SkBlendMode::kDifference: stage = SkRasterPipelineOp::difference; break; - case SkBlendMode::kExclusion: stage = SkRasterPipelineOp::exclusion; break; - case SkBlendMode::kMultiply: stage = SkRasterPipelineOp::multiply; break; + case SkBlendMode::kScreen: p->append(SkRasterPipelineOp::screen); return; + case SkBlendMode::kOverlay: p->append(SkRasterPipelineOp::overlay); return; + case SkBlendMode::kDarken: p->append(SkRasterPipelineOp::darken); return; + case SkBlendMode::kLighten: p->append(SkRasterPipelineOp::lighten); return; + case SkBlendMode::kColorDodge: p->append(SkRasterPipelineOp::colordodge); return; + case SkBlendMode::kColorBurn: p->append(SkRasterPipelineOp::colorburn); return; + case SkBlendMode::kHardLight: p->append(SkRasterPipelineOp::hardlight); return; + case SkBlendMode::kSoftLight: p->append(SkRasterPipelineOp::softlight); return; + case SkBlendMode::kDifference: p->append(SkRasterPipelineOp::difference); return; + case SkBlendMode::kExclusion: p->append(SkRasterPipelineOp::exclusion); return; + case SkBlendMode::kMultiply: p->append(SkRasterPipelineOp::multiply); return; - case SkBlendMode::kHue: stage = SkRasterPipelineOp::hue; break; - case SkBlendMode::kSaturation: stage = SkRasterPipelineOp::saturation; break; - case SkBlendMode::kColor: stage = SkRasterPipelineOp::color; break; - case SkBlendMode::kLuminosity: stage = SkRasterPipelineOp::luminosity; break; + case SkBlendMode::kHue: p->append(SkRasterPipelineOp::hue); return; + case SkBlendMode::kSaturation: p->append(SkRasterPipelineOp::saturation); return; + case SkBlendMode::kColor: p->append(SkRasterPipelineOp::color); return; + case SkBlendMode::kLuminosity: p->append(SkRasterPipelineOp::luminosity); return; + default: p->append(SkRasterPipelineOp::srcover); return; } - p->append(stage); + SkUNREACHABLE; } SkPMColor4f SkBlendMode_Apply(SkBlendMode mode, const SkPMColor4f& src, const SkPMColor4f& dst) { @@ -151,9 +151,8 @@ SkPMColor4f SkBlendMode_Apply(SkBlendMode mode, const SkPMColor4f& src, const Sk SkPMColor4f src_storage = src, dst_storage = dst, res_storage; - SkRasterPipeline_MemoryCtx src_ctx = { &src_storage, 0 }, - dst_ctx = { &dst_storage, 0 }, - res_ctx = { &res_storage, 0 }; + SkRasterPipelineContexts::MemoryCtx src_ctx = {&src_storage, 0}, dst_ctx = {&dst_storage, 0}, + res_ctx = {&res_storage, 0}; p.append(SkRasterPipelineOp::load_f32, &dst_ctx); p.append(SkRasterPipelineOp::move_src_dst); diff --git a/gfx/skia/skia/src/core/SkBlendModePriv.h b/gfx/skia/skia/src/core/SkBlendModePriv.h index 180470aa4e4f..ad27a6f099eb 100644 --- a/gfx/skia/skia/src/core/SkBlendModePriv.h +++ b/gfx/skia/skia/src/core/SkBlendModePriv.h @@ -10,7 +10,7 @@ #include "include/core/SkBlendMode.h" #include "include/core/SkColor.h" -#include "include/private/SkColorData.h" +#include "src/core/SkColorData.h" class SkRasterPipeline; class SkPaint; diff --git a/gfx/skia/skia/src/core/SkBlitRow_D32.cpp b/gfx/skia/skia/src/core/SkBlitRow_D32.cpp index 35958ef195c7..bcbf2e66bd46 100644 --- a/gfx/skia/skia/src/core/SkBlitRow_D32.cpp +++ b/gfx/skia/skia/src/core/SkBlitRow_D32.cpp @@ -6,11 +6,11 @@ */ #include "include/core/SkColor.h" -#include "include/core/SkColorPriv.h" #include "include/core/SkTypes.h" -#include "include/private/SkColorData.h" #include "include/private/base/SkCPUTypes.h" #include "src/core/SkBlitRow.h" +#include "src/core/SkColorData.h" +#include "src/core/SkColorPriv.h" #include "src/core/SkMemset.h" #include @@ -548,8 +548,7 @@ SkBlitRow::Proc32 SkBlitRow::Factory32(unsigned flags) { SkASSERT(flags < std::size(kProcs)); flags &= std::size(kProcs) - 1; // just to be safe - return flags == 2 ? SkOpts::blit_row_s32a_opaque - : kProcs[flags]; + return flags == Flags32::kSrcPixelAlpha_Flag32 ? SkOpts::blit_row_s32a_opaque : kProcs[flags]; } void SkBlitRow::Color32(SkPMColor dst[], int count, SkPMColor color) { diff --git a/gfx/skia/skia/src/core/SkBlitter.cpp b/gfx/skia/skia/src/core/SkBlitter.cpp index 5d84bc6fb0d5..7bfb73bc8326 100644 --- a/gfx/skia/skia/src/core/SkBlitter.cpp +++ b/gfx/skia/skia/src/core/SkBlitter.cpp @@ -27,6 +27,7 @@ #include "src/core/SkBlendModePriv.h" #include "src/core/SkBlitter_A8.h" #include "src/core/SkCoreBlitters.h" +#include "src/core/SkDrawTypes.h" #include "src/core/SkMask.h" #include "src/core/SkMaskFilterBase.h" #include "src/core/SkMemset.h" @@ -45,21 +46,7 @@ bool gSkForceRasterPipelineBlitter{false}; SkBlitter::~SkBlitter() {} -bool SkBlitter::isNullBlitter() const { return false; } - -/* -void SkBlitter::blitH(int x, int y, int width) { - SkDEBUGFAIL("unimplemented"); -} - - -void SkBlitter::blitAntiH(int x, int y, const SkAlpha antialias[], - const int16_t runs[]) { - SkDEBUGFAIL("unimplemented"); -} - */ - -inline static SkAlpha ScalarToAlpha(SkScalar a) { +inline static SkAlpha scalar_to_alpha(SkScalar a) { SkAlpha alpha = (SkAlpha)(a * 255); return alpha > 247 ? 0xFF : alpha < 8 ? 0 : alpha; } @@ -98,20 +85,20 @@ void SkBlitter::blitFatAntiRect(const SkRect& rect) { partialT = rect.fBottom - rect.fTop; } - alphas[0] = ScalarToAlpha(partialL * partialT); - alphas[1] = ScalarToAlpha(partialT); - alphas[bounds.width() - 1] = ScalarToAlpha(partialR * partialT); + alphas[0] = scalar_to_alpha(partialL * partialT); + alphas[1] = scalar_to_alpha(partialT); + alphas[bounds.width() - 1] = scalar_to_alpha(partialR * partialT); this->blitAntiH(bounds.fLeft, bounds.fTop, alphas, runs); if (bounds.height() > 2) { this->blitAntiRect(bounds.fLeft, bounds.fTop + 1, bounds.width() - 2, bounds.height() - 2, - ScalarToAlpha(partialL), ScalarToAlpha(partialR)); + scalar_to_alpha(partialL), scalar_to_alpha(partialR)); } if (bounds.height() > 1) { - alphas[0] = ScalarToAlpha(partialL * partialB); - alphas[1] = ScalarToAlpha(partialB); - alphas[bounds.width() - 1] = ScalarToAlpha(partialR * partialB); + alphas[0] = scalar_to_alpha(partialL * partialB); + alphas[1] = scalar_to_alpha(partialB); + alphas[bounds.width() - 1] = scalar_to_alpha(partialR * partialB); this->blitAntiH(bounds.fLeft, bounds.fBottom - 1, alphas, runs); } } @@ -315,21 +302,6 @@ void SkBlitter::blitRegion(const SkRegion& clip) { /////////////////////////////////////////////////////////////////////////////// -void SkNullBlitter::blitH(int x, int y, int width) {} - -void SkNullBlitter::blitAntiH(int x, int y, const SkAlpha antialias[], - const int16_t runs[]) {} - -void SkNullBlitter::blitV(int x, int y, int height, SkAlpha alpha) {} - -void SkNullBlitter::blitRect(int x, int y, int width, int height) {} - -void SkNullBlitter::blitMask(const SkMask& mask, const SkIRect& clip) {} - -bool SkNullBlitter::isNullBlitter() const { return true; } - -/////////////////////////////////////////////////////////////////////////////// - static int compute_anti_width(const int16_t runs[]) { int width = 0; @@ -686,7 +658,7 @@ SkBlitter* SkBlitter::Choose(const SkPixmap& device, const SkMatrix& ctm, const SkPaint& origPaint, SkArenaAlloc* alloc, - bool drawCoverage, + SkDrawCoverage drawCoverage, sk_sp clipShader, const SkSurfaceProps& props) { SkASSERT(alloc); @@ -727,7 +699,7 @@ SkBlitter* SkBlitter::Choose(const SkPixmap& device, } SkASSERT(!paint->getColorFilter()); - if (drawCoverage) { + if (drawCoverage == SkDrawCoverage::kYes) { if (device.colorType() == kAlpha_8_SkColorType) { SkASSERT(!paint->getShader()); SkASSERT(paint->isSrcOver()); @@ -787,11 +759,10 @@ SkBlitter* SkBlitter::Choose(const SkPixmap& device, /////////////////////////////////////////////////////////////////////////////// -SkShaderBlitter::SkShaderBlitter(const SkPixmap& device, const SkPaint& paint, +SkShaderBlitter::SkShaderBlitter(const SkPixmap& device, + const SkPaint& paint, SkShaderBase::Context* shaderContext) - : INHERITED(device) - , fShader(paint.refShader()) - , fShaderContext(shaderContext) { + : SkRasterBlitter(device), fShader(paint.refShader()), fShaderContext(shaderContext) { SkASSERT(fShader); SkASSERT(fShaderContext); } diff --git a/gfx/skia/skia/src/core/SkBlitter.h b/gfx/skia/skia/src/core/SkBlitter.h index 4007229232e2..de9dbf051f0a 100644 --- a/gfx/skia/skia/src/core/SkBlitter.h +++ b/gfx/skia/skia/src/core/SkBlitter.h @@ -28,6 +28,7 @@ class SkPixmap; class SkShader; class SkSurfaceProps; struct SkMask; +enum class SkDrawCoverage : bool; /** SkBlitter and its subclasses are responsible for actually writing pixels into memory. Besides efficiency, they handle clipping and antialiasing. @@ -38,6 +39,11 @@ struct SkMask; class SkBlitter { public: virtual ~SkBlitter(); + SkBlitter() = default; + SkBlitter(const SkBlitter&) = delete; + SkBlitter(SkBlitter&&) = delete; + SkBlitter& operator=(const SkBlitter&) = delete; + SkBlitter& operator=(SkBlitter&&) = delete; /// Blit a horizontal run of one or more pixels. virtual void blitH(int x, int y, int width) = 0; @@ -106,13 +112,6 @@ public: this->blitAntiH(x, y + 1, aa, runs); } - /** - * Special method just to identify the null blitter, which is returned - * from Choose() if the request cannot be fulfilled. Default impl - * returns false. - */ - virtual bool isNullBlitter() const; - /** * Special methods for blitters that can blit more than one row at a time. * This function returns the number of rows that this blitter could optimally @@ -146,7 +145,7 @@ public: const SkMatrix& ctm, const SkPaint& paint, SkArenaAlloc*, - bool drawCoverage, + SkDrawCoverage, sk_sp clipShader, const SkSurfaceProps& props); @@ -165,21 +164,20 @@ protected: /** This blitter silently never draws anything. */ -class SkNullBlitter : public SkBlitter { +class SkNullBlitter final : public SkBlitter { public: - void blitH(int x, int y, int width) override; - void blitAntiH(int x, int y, const SkAlpha[], const int16_t runs[]) override; - void blitV(int x, int y, int height, SkAlpha alpha) override; - void blitRect(int x, int y, int width, int height) override; - void blitMask(const SkMask&, const SkIRect& clip) override; - bool isNullBlitter() const override; + void blitH(int x, int y, int width) override {} + void blitAntiH(int x, int y, const SkAlpha[], const int16_t runs[]) override {} + void blitV(int x, int y, int height, SkAlpha alpha) override {} + void blitRect(int x, int y, int width, int height) override {} + void blitMask(const SkMask&, const SkIRect& clip) override {} }; /** Wraps another (real) blitter, and ensures that the real blitter is only called with coordinates that have been clipped by the specified clipRect. This means the caller need not perform the clipping ahead of time. */ -class SkRectClipBlitter : public SkBlitter { +class SkRectClipBlitter final : public SkBlitter { public: void init(SkBlitter* blitter, const SkIRect& clipRect) { SkASSERT(!clipRect.isEmpty()); @@ -212,7 +210,7 @@ private: called with coordinates that have been clipped by the specified clipRgn. This means the caller need not perform the clipping ahead of time. */ -class SkRgnClipBlitter : public SkBlitter { +class SkRgnClipBlitter final : public SkBlitter { public: void init(SkBlitter* blitter, const SkRegion* clipRgn) { SkASSERT(clipRgn && !clipRgn->isEmpty()); @@ -242,7 +240,7 @@ private: }; #ifdef SK_DEBUG -class SkRectClipCheckBlitter : public SkBlitter { +class SkRectClipCheckBlitter final : public SkBlitter { public: void init(SkBlitter* blitter, const SkIRect& clipRect) { SkASSERT(blitter); @@ -290,7 +288,4 @@ private: SkRgnClipBlitter fRgnBlitter; }; -// A good size for creating shader contexts on the stack. -enum {kSkBlitterContextSize = 3332}; - #endif diff --git a/gfx/skia/skia/src/core/SkBlitter_A8.cpp b/gfx/skia/skia/src/core/SkBlitter_A8.cpp index 90307afa9827..9ca477612f1f 100644 --- a/gfx/skia/skia/src/core/SkBlitter_A8.cpp +++ b/gfx/skia/skia/src/core/SkBlitter_A8.cpp @@ -15,6 +15,7 @@ #include "include/core/SkTypes.h" #include "include/private/base/SkDebug.h" #include "src/base/SkArenaAlloc.h" +#include "src/core/SkDrawTypes.h" #include "src/core/SkMask.h" #include @@ -287,7 +288,7 @@ SkBlitter* SkA8Blitter_Choose(const SkPixmap& dst, const SkMatrix& ctm, const SkPaint& paint, SkArenaAlloc* alloc, - bool drawCoverage, + SkDrawCoverage drawCoverage, sk_sp clipShader, const SkSurfaceProps&) { if (dst.colorType() != SkColorType::kAlpha_8_SkColorType) { @@ -300,7 +301,7 @@ SkBlitter* SkA8Blitter_Choose(const SkPixmap& dst, return nullptr; // would not be hard to support ...? } - if (drawCoverage) { + if (drawCoverage == SkDrawCoverage::kYes) { return alloc->make(dst, paint); } else { // we only support certain blendmodes... @@ -311,4 +312,3 @@ SkBlitter* SkA8Blitter_Choose(const SkPixmap& dst, } return nullptr; } - diff --git a/gfx/skia/skia/src/core/SkBlitter_A8.h b/gfx/skia/skia/src/core/SkBlitter_A8.h index 3aecf9149e3e..1d96863ef333 100644 --- a/gfx/skia/skia/src/core/SkBlitter_A8.h +++ b/gfx/skia/skia/src/core/SkBlitter_A8.h @@ -22,6 +22,7 @@ class SkShader; class SkSurfaceProps; struct SkIRect; struct SkMask; +enum class SkDrawCoverage : bool; class SkA8_Coverage_Blitter : public SkBlitter { public: @@ -40,7 +41,7 @@ SkBlitter* SkA8Blitter_Choose(const SkPixmap& dst, const SkMatrix& ctm, const SkPaint& paint, SkArenaAlloc*, - bool drawCoverage, + SkDrawCoverage, sk_sp clipShader, const SkSurfaceProps&); diff --git a/gfx/skia/skia/src/core/SkBlitter_ARGB32.cpp b/gfx/skia/skia/src/core/SkBlitter_ARGB32.cpp index 199b90796b84..3e965e441a83 100644 --- a/gfx/skia/skia/src/core/SkBlitter_ARGB32.cpp +++ b/gfx/skia/skia/src/core/SkBlitter_ARGB32.cpp @@ -6,13 +6,12 @@ */ #include "include/core/SkColor.h" -#include "include/core/SkColorPriv.h" #include "include/core/SkColorType.h" #include "include/core/SkPaint.h" #include "include/core/SkPixmap.h" #include "include/core/SkRect.h" #include "include/core/SkTypes.h" -#include "include/private/SkColorData.h" +#include "include/private/base/SkAlign.h" #include "include/private/base/SkCPUTypes.h" #include "include/private/base/SkDebug.h" #include "include/private/base/SkMalloc.h" @@ -21,6 +20,8 @@ #include "src/base/SkVx.h" #include "src/core/SkBlitMask.h" #include "src/core/SkBlitRow.h" +#include "src/core/SkColorData.h" +#include "src/core/SkColorPriv.h" #include "src/core/SkCoreBlitters.h" #include "src/core/SkMask.h" #include "src/core/SkMemset.h" @@ -352,8 +353,8 @@ static inline SkPMColor blend_lcd16_opaque(int srcR, int srcG, int srcB, srcA = SkAlpha255To256(srcA); if (width >= 4) { - SkASSERT(((size_t)dst & 0x03) == 0); - while (((size_t)dst & 0x0F) != 0) { + SkASSERT(SkIsAlign4((uintptr_t) dst)); + while (!SkIsAlign16((uintptr_t) dst)) { *dst = blend_lcd16(srcA, srcR, srcG, srcB, *dst, *mask); mask++; dst++; @@ -372,7 +373,8 @@ static inline SkPMColor blend_lcd16_opaque(int srcR, int srcG, int srcB, // Load four destination pixels into dst_sse. __m128i dst_sse = _mm_load_si128(d); // Load four 16-bit masks into lower half of mask_sse. - __m128i mask_sse = _mm_loadl_epi64((const __m128i*)mask); + // mask does *not* actually need to be 16 byte alligned to use this command + __m128i mask_sse = _mm_loadl_epi64(reinterpret_cast(mask)); // Check whether masks are equal to 0 and get the highest bit // of each byte of result, if masks are all zero, we will get @@ -410,7 +412,7 @@ static inline SkPMColor blend_lcd16_opaque(int srcR, int srcG, int srcB, } void blit_row_lcd16_opaque(SkPMColor dst[], const uint16_t mask[], - SkColor src, int width, SkPMColor opaqueDst) { + SkColor src, int width, SkPMColor opaqueDst) { if (width <= 0) { return; } @@ -420,8 +422,8 @@ static inline SkPMColor blend_lcd16_opaque(int srcR, int srcG, int srcB, int srcB = SkColorGetB(src); if (width >= 4) { - SkASSERT(((size_t)dst & 0x03) == 0); - while (((size_t)dst & 0x0F) != 0) { + SkASSERT(SkIsAlign4((uintptr_t) dst)); + while (!SkIsAlign16((uintptr_t) dst)) { *dst = blend_lcd16_opaque(srcR, srcG, srcB, *dst, *mask, opaqueDst); mask++; dst++; @@ -438,7 +440,8 @@ static inline SkPMColor blend_lcd16_opaque(int srcR, int srcG, int srcB, // Load four destination pixels into dst_sse. __m128i dst_sse = _mm_load_si128(d); // Load four 16-bit masks into lower half of mask_sse. - __m128i mask_sse = _mm_loadl_epi64((const __m128i*)mask); + // mask does *not* actually need to be 16 byte alligned to use this command + __m128i mask_sse = _mm_loadl_epi64(reinterpret_cast(mask)); // Check whether masks are equal to 0 and get the highest bit // of each byte of result, if masks are all zero, we will get @@ -874,8 +877,8 @@ static inline SkPMColor blend_lcd16_opaque(int srcR, int srcG, int srcB, srcA = SkAlpha255To256(srcA); if (width >= 8) { - SkASSERT(((size_t)dst & 0x03) == 0); - while (((size_t)dst & 0x0F) != 0) { + SkASSERT(SkIsAlign4((uintptr_t) dst)); + while (!SkIsAlign16((uintptr_t) dst)) { *dst = blend_lcd16(srcA, srcR, srcG, srcB, *dst, *mask); mask++; dst++; @@ -941,8 +944,8 @@ static inline SkPMColor blend_lcd16_opaque(int srcR, int srcG, int srcB, __m256i xv_zero = __lasx_xvldi(0); if (width >= 8) { - SkASSERT(((size_t)dst & 0x03) == 0); - while (((size_t)dst & 0x0F) != 0) { + SkASSERT(SkIsAlign4((uintptr_t) dst)); + while (!SkIsAlign16((uintptr_t) dst)) { *dst = blend_lcd16_opaque(srcR, srcG, srcB, *dst, *mask, opaqueDst); mask++; dst++; @@ -1231,8 +1234,8 @@ static inline SkPMColor blend_lcd16_opaque(int srcR, int srcG, int srcB, srcA = SkAlpha255To256(srcA); if (width >= 4) { - SkASSERT(((size_t)dst & 0x03) == 0); - while (((size_t)dst & 0x0F) != 0) { + SkASSERT(SkIsAlign4((uintptr_t) dst)); + while (!SkIsAlign16((uintptr_t) dst)) { *dst = blend_lcd16(srcA, srcR, srcG, srcB, *dst, *mask); mask++; dst++; @@ -1297,8 +1300,8 @@ static inline SkPMColor blend_lcd16_opaque(int srcR, int srcG, int srcB, __m128i v_zero = __lsx_vldi(0); if (width >= 4) { - SkASSERT(((size_t)dst & 0x03) == 0); - while (((size_t)dst & 0x0F) != 0) { + SkASSERT(SkIsAlign4((uintptr_t) dst)); + while (!SkIsAlign16((uintptr_t) dst)) { *dst = blend_lcd16_opaque(srcR, srcG, srcB, *dst, *mask, opaqueDst); mask++; dst++; @@ -1443,17 +1446,11 @@ static void SkARGB32_Blit32(const SkPixmap& device, const SkMask& mask, ////////////////////////////////////////////////////////////////////////////////////// SkARGB32_Blitter::SkARGB32_Blitter(const SkPixmap& device, const SkPaint& paint) - : INHERITED(device) { + : SkRasterBlitter(device) { SkColor color = paint.getColor(); fColor = color; - fSrcA = SkColorGetA(color); - unsigned scale = SkAlpha255To256(fSrcA); - fSrcR = SkAlphaMul(SkColorGetR(color), scale); - fSrcG = SkAlphaMul(SkColorGetG(color), scale); - fSrcB = SkAlphaMul(SkColorGetB(color), scale); - - fPMColor = SkPackARGB32(fSrcA, fSrcR, fSrcG, fSrcB); + fPMColor = SkPreMultiplyColor(fColor); } #if defined _WIN32 // disable warning : local variable used without having been initialized @@ -1470,13 +1467,12 @@ void SkARGB32_Blitter::blitH(int x, int y, int width) { void SkARGB32_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) { + SkASSERT(fSrcA != 0xFF); // There is an opaque specialization if (fSrcA == 0) { return; } - uint32_t color = fPMColor; - uint32_t* device = fDevice.writable_addr32(x, y); - unsigned opaqueMask = fSrcA; // if fSrcA is 0xFF, then we will catch the fast opaque case + uint32_t* device = fDevice.writable_addr32(x, y); for (;;) { int count = runs[0]; @@ -1484,14 +1480,10 @@ void SkARGB32_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], if (count <= 0) { return; } - unsigned aa = antialias[0]; + SkAlpha aa = antialias[0]; if (aa) { - if ((opaqueMask & aa) == 255) { - SkOpts::memset32(device, color, count); - } else { - uint32_t sc = SkAlphaMulQ(color, SkAlpha255To256(aa)); - SkBlitRow::Color32(device, count, sc); - } + SkPMColor sc = SkAlphaMulQ(fPMColor, SkAlpha255To256(aa)); + SkBlitRow::Color32(device, count, sc); } runs += count; antialias += count; @@ -1582,6 +1574,7 @@ void SkARGB32_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) { void SkARGB32_Opaque_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) { + SkASSERT(fSrcA == 0xFF); SkASSERT(mask.fBounds.contains(clip)); if (blit_color(fDevice, mask, clip, fColor)) { @@ -1600,6 +1593,32 @@ void SkARGB32_Opaque_Blitter::blitMask(const SkMask& mask, } } +void SkARGB32_Opaque_Blitter::blitAntiH(int x, + int y, + const SkAlpha antialias[], + const int16_t runs[]) { + SkASSERT(fSrcA == 0xFF); + + uint32_t* device = fDevice.writable_addr32(x, y); + for (;;) { + int count = runs[0]; + SkASSERT(count >= 0); + if (count <= 0) { + return; + } + SkAlpha aa = antialias[0]; + if (aa == 255) { + SkOpts::memset32(device, fPMColor, count); + } else if (aa > 0) { + SkPMColor sc = SkAlphaMulQ(fPMColor, SkAlpha255To256(aa)); + SkBlitRow::Color32(device, count, sc); + } + runs += count; + antialias += count; + device += count; + } +} + void SkARGB32_Opaque_Blitter::blitAntiH2(int x, int y, U8CPU a0, U8CPU a1) { uint32_t* device = fDevice.writable_addr32(x, y); SkDEBUGCODE((void)fDevice.writable_addr32(x + 1, y);) @@ -1625,14 +1644,14 @@ void SkARGB32_Blitter::blitV(int x, int y, int height, SkAlpha alpha) { } uint32_t* device = fDevice.writable_addr32(x, y); - uint32_t color = fPMColor; + SkPMColor color = fPMColor; if (alpha != 255) { color = SkAlphaMulQ(color, SkAlpha255To256(alpha)); } - unsigned dst_scale = SkAlpha255To256(255 - SkGetPackedA32(color)); - size_t rowBytes = fDevice.rowBytes(); + const unsigned dst_scale = SkAlpha255To256(255 - SkGetPackedA32(color)); + const size_t rowBytes = fDevice.rowBytes(); while (--height >= 0) { device[0] = color + SkAlphaMulQ(device[0], dst_scale); device = (uint32_t*)((char*)device + rowBytes); @@ -1646,15 +1665,14 @@ void SkARGB32_Blitter::blitRect(int x, int y, int width, int height) { return; } - uint32_t* device = fDevice.writable_addr32(x, y); - uint32_t color = fPMColor; - size_t rowBytes = fDevice.rowBytes(); + uint32_t* device = fDevice.writable_addr32(x, y); + const size_t rowBytes = fDevice.rowBytes(); if (SkGetPackedA32(fPMColor) == 0xFF) { - SkOpts::rect_memset32(device, color, width, rowBytes, height); + SkOpts::rect_memset32(device, fPMColor, width, rowBytes, height); } else { while (height --> 0) { - SkBlitRow::Color32(device, width, color); + SkBlitRow::Color32(device, width, fPMColor); device = (uint32_t*)((char*)device + rowBytes); } } @@ -1669,7 +1687,7 @@ void SkARGB32_Blitter::blitRect(int x, int y, int width, int height) { void SkARGB32_Black_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) { uint32_t* device = fDevice.writable_addr32(x, y); - SkPMColor black = (SkPMColor)(SK_A32_MASK << SK_A32_SHIFT); + static constexpr SkPMColor kBlack = (SkPMColor)(SK_A32_MASK << SK_A32_SHIFT); for (;;) { int count = runs[0]; @@ -1680,10 +1698,10 @@ void SkARGB32_Black_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], unsigned aa = antialias[0]; if (aa) { if (aa == 255) { - SkOpts::memset32(device, black, count); + SkOpts::memset32(device, kBlack, count); } else { - SkPMColor src = aa << SK_A32_SHIFT; - unsigned dst_scale = 256 - aa; + const SkPMColor src = aa << SK_A32_SHIFT; + const unsigned dst_scale = SkAlpha255To256(255 - aa); int n = count; do { --n; @@ -1717,9 +1735,9 @@ void SkARGB32_Black_Blitter::blitAntiV2(int x, int y, U8CPU a0, U8CPU a1) { /////////////////////////////////////////////////////////////////////////////// SkARGB32_Shader_Blitter::SkARGB32_Shader_Blitter(const SkPixmap& device, - const SkPaint& paint, SkShaderBase::Context* shaderContext) - : INHERITED(device, paint, shaderContext) -{ + const SkPaint& paint, + SkShaderBase::Context* shaderContext) + : SkShaderBlitter(device, paint, shaderContext) { fBuffer = (SkPMColor*)sk_malloc_throw(device.width() * (sizeof(SkPMColor))); SkASSERT(paint.isSrcOver()); @@ -1760,7 +1778,7 @@ void SkARGB32_Shader_Blitter::blitRect(int x, int y, int width, int height) { x + width <= fDevice.width() && y + height <= fDevice.height()); uint32_t* device = fDevice.writable_addr32(x, y); - size_t deviceRB = fDevice.rowBytes(); + const size_t deviceRB = fDevice.rowBytes(); auto* shaderContext = fShaderContext; SkPMColor* span = fBuffer; @@ -1970,7 +1988,7 @@ void SkARGB32_Shader_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) } else if (mask.fFormat == SkMask::kLCD16_Format) { blend_row = blend_row_lcd16; } else { - this->INHERITED::blitMask(mask, clip); + this->SkShaderBlitter::blitMask(mask, clip); return; } @@ -1999,7 +2017,7 @@ void SkARGB32_Shader_Blitter::blitV(int x, int y, int height, SkAlpha alpha) { SkASSERT(x >= 0 && y >= 0 && y + height <= fDevice.height()); uint32_t* device = fDevice.writable_addr32(x, y); - size_t deviceRB = fDevice.rowBytes(); + const size_t deviceRB = fDevice.rowBytes(); if (fShadeDirectlyIntoDevice) { if (255 == alpha) { diff --git a/gfx/skia/skia/src/core/SkBlitter_Sprite.cpp b/gfx/skia/skia/src/core/SkBlitter_Sprite.cpp index 4be4ab35bbda..28b3c6fd40dc 100644 --- a/gfx/skia/skia/src/core/SkBlitter_Sprite.cpp +++ b/gfx/skia/skia/src/core/SkBlitter_Sprite.cpp @@ -80,7 +80,7 @@ class SkSpriteBlitter_Memcpy final : public SkSpriteBlitter { public: static bool Supports(const SkPixmap& dst, const SkPixmap& src, const SkPaint& paint) { // the caller has already inspected the colorspace on src and dst - SkASSERT(0 == SkColorSpaceXformSteps(src,dst).flags.mask()); + SkASSERT(0 == SkColorSpaceXformSteps(src,dst).fFlags.mask()); if (dst.colorType() != src.colorType()) { return false; @@ -183,7 +183,7 @@ public: private: SkArenaAlloc* fAlloc; SkBlitter* fBlitter; - SkRasterPipeline_MemoryCtx fSrcPtr; + SkRasterPipelineContexts::MemoryCtx fSrcPtr; SkColor4f fPaintColor; sk_sp fClipShader; @@ -215,7 +215,7 @@ SkBlitter* SkBlitter::ChooseSprite(const SkPixmap& dst, const SkPaint& paint, if (gSkForceRasterPipelineBlitter) { // Do not use any of these optimized memory blitters - } else if (0 == SkColorSpaceXformSteps(source,dst).flags.mask() && !clipShader) { + } else if (0 == SkColorSpaceXformSteps(source,dst).fFlags.mask() && !clipShader) { if (!blitter && SkSpriteBlitter_Memcpy::Supports(dst, source, paint)) { blitter = alloc->make(source); } diff --git a/gfx/skia/skia/src/core/SkBlurEngine.cpp b/gfx/skia/skia/src/core/SkBlurEngine.cpp index 2c18eb0fb2e7..bfaab0f728ce 100644 --- a/gfx/skia/skia/src/core/SkBlurEngine.cpp +++ b/gfx/skia/skia/src/core/SkBlurEngine.cpp @@ -769,9 +769,12 @@ public: PassMaker* makerX = makeMaker(sigma.width()); PassMaker* makerY = makeMaker(sigma.height()); + +#if !defined(SK_AVOID_SLOW_RASTER_PIPELINE_BLURS) // A blur with a sigma smaller than the successive box-blurs accuracy should have been // routed to the shader-based algorithm. SkASSERT(makerX->window() > 1 || makerY->window() > 1); +#endif SkIRect srcBounds = originalSrcBounds; SkIRect dstBounds = originalDstBounds; @@ -860,6 +863,15 @@ public: } } +#if defined(SK_AVOID_SLOW_RASTER_PIPELINE_BLURS) + // When avoiding the shader-based algorithm, handle the box identity case. + if (makerX->window() == 1 && makerY->window() == 1) { + dst.writePixels(src.pixmap(), + srcBounds.left() - dstBounds.left(), + srcBounds.top() - dstBounds.top()); + } +#endif + dstBounds = originalDstBounds.makeOffset(-dstOrigin); // Make relative to dst's pixels return SkSpecialImages::MakeFromRaster(dstBounds, dst, SkSurfaceProps{}); } @@ -879,11 +891,17 @@ public: class RasterBlurEngine : public SkBlurEngine { public: const Algorithm* findAlgorithm(SkSize sigma, SkColorType colorType) const override { +#if defined(SK_AVOID_SLOW_RASTER_PIPELINE_BLURS) + // For large source images, the shader-based blur can be prohibitively slow so setting this + // to zero means it'll never be used for 8888 color types. + static constexpr float kBoxBlurMinSigma = 0.f; +#else static constexpr float kBoxBlurMinSigma = 2.f; // If the sigma is larger than kBoxBlurMinSigma, we should assume that we won't encounter // an identity window assertion later on. SkASSERT(SkBlurEngine::BoxBlurWindow(kBoxBlurMinSigma) > 1); +#endif // Using the shader-based blur for small blur sigmas only happens if both axes require a // small blur. It's assumed that any inaccuracy along one axis is hidden by the large enough diff --git a/gfx/skia/skia/src/core/SkBlurMask.cpp b/gfx/skia/skia/src/core/SkBlurMask.cpp index 73c95b859063..45c8e75158a4 100644 --- a/gfx/skia/skia/src/core/SkBlurMask.cpp +++ b/gfx/skia/skia/src/core/SkBlurMask.cpp @@ -8,7 +8,6 @@ #include "src/core/SkBlurMask.h" #include "include/core/SkBlurTypes.h" -#include "include/core/SkColorPriv.h" #include "include/core/SkPoint.h" #include "include/core/SkRect.h" #include "include/private/base/SkMath.h" @@ -17,14 +16,13 @@ #include "include/private/base/SkTemplates.h" #include "include/private/base/SkTo.h" #include "src/base/SkMathPriv.h" +#include "src/core/SkColorPriv.h" #include "src/core/SkMaskBlurFilter.h" #include #include #include -class SkRRect; - using namespace skia_private; // This constant approximates the scaling done in the software path's @@ -106,15 +104,12 @@ static void clamp_outer_with_orig(uint8_t dst[], int dstRowBytes, } /////////////////////////////////////////////////////////////////////////////// -// we use a local function to wrap the class static method to work around -// a bug in gcc98 -void SkMask_FreeImage(uint8_t* image); -void SkMask_FreeImage(uint8_t* image) { - SkMaskBuilder::FreeImage(image); -} - -bool SkBlurMask::BoxBlur(SkMaskBuilder* dst, const SkMask& src, SkScalar sigma, SkBlurStyle style, - SkIPoint* margin) { +bool SkBlurMask::BoxBlur(SkMaskBuilder* dst, + const SkMask& src, + SkScalar sigma, + SkBlurStyle style, + SkIVector* margin) { + SkASSERT(dst); if (src.fFormat != SkMask::kBW_Format && src.fFormat != SkMask::kA8_Format && src.fFormat != SkMask::kARGB32_Format && @@ -136,15 +131,18 @@ bool SkBlurMask::BoxBlur(SkMaskBuilder* dst, const SkMask& src, SkScalar sigma, // This filter will disregard the src.fImage completely. // The margin is actually {-(src.fBounds.width() / 2), -(src.fBounds.height() / 2)} // but it is not clear if callers will fall over with negative margins. - *margin = SkIPoint{0,0}; + *margin = SkIVector{0, 0}; } return true; } return false; } - const SkIPoint border = blurFilter.blur(src, dst); - // If src.fImage is null, then this call is only to calculate the border. + const SkIVector border = blurFilter.blur(src, dst); + if (src.fImage != nullptr && dst->fImage == nullptr) { + // The call to blur() failed to set our destination image up (e.g. an overflow). + // Note that if src.fImage was null, dst->fImage will also be null and that's + // *not* an error case - the code should continue to calculate the border. return false; } @@ -404,9 +402,12 @@ void SkBlurMask::ComputeBlurredScanline(uint8_t *pixels, const uint8_t *profile, } } -bool SkBlurMask::BlurRect(SkScalar sigma, SkMaskBuilder *dst, - const SkRect &src, SkBlurStyle style, - SkIPoint *margin, SkMaskBuilder::CreateMode createMode) { +bool SkBlurMask::BlurRect(SkScalar sigma, + SkMaskBuilder* dst, + const SkRect& src, + SkBlurStyle style, + SkIVector* margin, + SkMaskBuilder::CreateMode createMode) { int profileSize = SkScalarCeilToInt(6*sigma); if (profileSize <= 0) { return false; // no blur to compute @@ -501,22 +502,15 @@ bool SkBlurMask::BlurRect(SkScalar sigma, SkMaskBuilder *dst, return true; } -bool SkBlurMask::BlurRRect(SkScalar sigma, SkMaskBuilder *dst, - const SkRRect &src, SkBlurStyle style, - SkIPoint *margin, SkMaskBuilder::CreateMode createMode) { - // Temporary for now -- always fail, should cause caller to fall back - // to old path. Plumbing just to land API and parallelize effort. - - return false; -} - // The "simple" blur is a direct implementation of separable convolution with a discrete // gaussian kernel. It's "ground truth" in a sense; too slow to be used, but very // useful for correctness comparisons. -bool SkBlurMask::BlurGroundTruth(SkScalar sigma, SkMaskBuilder* dst, const SkMask& src, - SkBlurStyle style, SkIPoint* margin) { - +bool SkBlurMask::BlurGroundTruth(SkScalar sigma, + SkMaskBuilder* dst, + const SkMask& src, + SkBlurStyle style, + SkIVector* margin) { if (src.fFormat != SkMask::kA8_Format) { return false; } diff --git a/gfx/skia/skia/src/core/SkBlurMask.h b/gfx/skia/skia/src/core/SkBlurMask.h index 107d32d27d68..4324871ee76d 100644 --- a/gfx/skia/skia/src/core/SkBlurMask.h +++ b/gfx/skia/skia/src/core/SkBlurMask.h @@ -8,6 +8,7 @@ #ifndef SkBlurMask_DEFINED #define SkBlurMask_DEFINED +#include "include/core/SkPoint.h" #include "include/core/SkScalar.h" #include "include/core/SkTypes.h" #include "src/core/SkMask.h" @@ -16,17 +17,16 @@ class SkRRect; enum SkBlurStyle : int; -struct SkIPoint; struct SkRect; class SkBlurMask { public: [[nodiscard]] static bool BlurRect(SkScalar sigma, SkMaskBuilder *dst, const SkRect &src, - SkBlurStyle, SkIPoint *margin = nullptr, + SkBlurStyle, SkIVector *margin = nullptr, SkMaskBuilder::CreateMode createMode = SkMaskBuilder::kComputeBoundsAndRenderImage_CreateMode); [[nodiscard]] static bool BlurRRect(SkScalar sigma, SkMaskBuilder *dst, const SkRRect &src, - SkBlurStyle, SkIPoint *margin = nullptr, + SkBlurStyle, SkIVector *margin = nullptr, SkMaskBuilder::CreateMode createMode = SkMaskBuilder::kComputeBoundsAndRenderImage_CreateMode); @@ -41,15 +41,19 @@ public: // * failure - if src.fImage is not null, failure is signal with dst->fImage being // null. - [[nodiscard]] static bool BoxBlur(SkMaskBuilder* dst, const SkMask& src, - SkScalar sigma, SkBlurStyle style, - SkIPoint* margin = nullptr); + [[nodiscard]] static bool BoxBlur(SkMaskBuilder* dst, + const SkMask& src, + SkScalar sigma, + SkBlurStyle style, + SkIVector* margin = nullptr); // the "ground truth" blur does a gaussian convolution; it's slow // but useful for comparison purposes. - [[nodiscard]] static bool BlurGroundTruth(SkScalar sigma, SkMaskBuilder* dst, + [[nodiscard]] static bool BlurGroundTruth(SkScalar sigma, + SkMaskBuilder* dst, const SkMask& src, - SkBlurStyle, SkIPoint* margin = nullptr); + SkBlurStyle, + SkIVector* margin = nullptr); // If radius > 0, return the corresponding sigma, else return 0 static SkScalar SK_SPI ConvertRadiusToSigma(SkScalar radius); diff --git a/gfx/skia/skia/src/core/SkBlurMaskFilterImpl.cpp b/gfx/skia/skia/src/core/SkBlurMaskFilterImpl.cpp index 4a6ab78bb48d..67eefbca155b 100644 --- a/gfx/skia/skia/src/core/SkBlurMaskFilterImpl.cpp +++ b/gfx/skia/skia/src/core/SkBlurMaskFilterImpl.cpp @@ -122,15 +122,6 @@ bool SkBlurMaskFilterImpl::filterRectMask(SkMaskBuilder* dst, const SkRect& r, return SkBlurMask::BlurRect(sigma, dst, r, fBlurStyle, margin, createMode); } -bool SkBlurMaskFilterImpl::filterRRectMask(SkMaskBuilder* dst, const SkRRect& r, - const SkMatrix& matrix, - SkIPoint* margin, - SkMaskBuilder::CreateMode createMode) const { - SkScalar sigma = computeXformedSigma(matrix); - - return SkBlurMask::BlurRRect(sigma, dst, r, fBlurStyle, margin, createMode); -} - static bool prepare_to_draw_into_mask(const SkRect& bounds, SkMaskBuilder* mask) { SkASSERT(mask != nullptr); @@ -177,9 +168,10 @@ template bool draw_into_mask(SkMaskBuilder* mask, const SkRect& return true; } -static bool draw_rects_into_mask(const SkRect rects[], int count, SkMaskBuilder* mask) { +static bool draw_rects_into_mask(SkSpan rects, SkMaskBuilder* mask) { + SkASSERT(rects.size() == 1 || rects.size() == 2); return draw_into_mask(mask, rects[0], [&](SkDrawBase& draw, const SkPaint& paint) { - if (1 == count) { + if (rects.size() == 1) { draw.drawRect(rects[0], paint); } else { // todo: do I need a fast way to do this? @@ -187,12 +179,12 @@ static bool draw_rects_into_mask(const SkRect rects[], int count, SkMaskBuilder* .addRect(rects[1]) .setFillType(SkPathFillType::kEvenOdd) .detach(); - draw.drawPath(path, paint); + draw.drawPath(path, paint, nullptr, true); } }); } -static bool draw_rrect_into_mask(const SkRRect rrect, SkMaskBuilder* mask) { +static bool draw_rrect_into_mask(const SkRRect& rrect, SkMaskBuilder* mask) { return draw_into_mask(mask, rrect.rect(), [&](SkDrawBase& draw, const SkPaint& paint) { draw.drawRRect(rrect, paint); }); @@ -228,40 +220,40 @@ static SkCachedData* add_cached_rrect(SkMaskBuilder* mask, SkScalar sigma, SkBlu return cache; } -static SkCachedData* find_cached_rects(SkTLazy* mask, SkScalar sigma, SkBlurStyle style, - const SkRect rects[], int count) { - return SkMaskCache::FindAndRef(sigma, style, rects, count, mask); +static SkCachedData* find_cached_rects(SkTLazy* mask, + SkScalar sigma, + SkBlurStyle style, + SkSpan rects) { + return SkMaskCache::FindAndRef(sigma, style, rects, mask); } -static SkCachedData* add_cached_rects(SkMaskBuilder* mask, SkScalar sigma, SkBlurStyle style, - const SkRect rects[], int count) { +static SkCachedData* add_cached_rects(SkMaskBuilder* mask, + SkScalar sigma, + SkBlurStyle style, + SkSpan rects) { SkCachedData* cache = copy_mask_to_cacheddata(mask); if (cache) { - SkMaskCache::Add(sigma, style, rects, count, *mask, cache); + SkMaskCache::Add(sigma, style, rects, *mask, cache); } return cache; } -static const bool c_analyticBlurRRect{true}; - -SkMaskFilterBase::FilterReturn +std::optional SkBlurMaskFilterImpl::filterRRectToNine(const SkRRect& rrect, const SkMatrix& matrix, - const SkIRect& clipBounds, - SkTLazy* patch) const { - SkASSERT(patch != nullptr); + const SkIRect& clipBounds) const { switch (rrect.getType()) { case SkRRect::kEmpty_Type: // Nothing to draw. - return kFalse_FilterReturn; + return std::nullopt; case SkRRect::kRect_Type: // We should have caught this earlier. - SkASSERT(false); - [[fallthrough]]; + SkDEBUGFAIL("Should use a different special case"); + return std::nullopt; case SkRRect::kOval_Type: // The nine patch special case does not handle ovals, and we // already have code for rectangles. - return kUnimplemented_FilterReturn; + return std::nullopt; // These three can take advantage of this fast path. case SkRRect::kSimple_Type: @@ -273,139 +265,178 @@ SkBlurMaskFilterImpl::filterRRectToNine(const SkRRect& rrect, const SkMatrix& ma // TODO: report correct metrics for innerstyle, where we do not grow the // total bounds, but we do need an inset the size of our blur-radius if (kInner_SkBlurStyle == fBlurStyle) { - return kUnimplemented_FilterReturn; + return std::nullopt; } // TODO: take clipBounds into account to limit our coordinates up front // for now, just skip too-large src rects (to take the old code path). if (rect_exceeds(rrect.rect(), SkIntToScalar(32767))) { - return kUnimplemented_FilterReturn; + return std::nullopt; } - SkIPoint margin; + // To better understand the following code, consider the concrete example + // where the passed in rrect is a 16x12 rounded rectangle with (rX,rY) + // being (2.5, 2.0) and the sigma is 0.5 (which was turned into matrix). + // The full non-blurred rrect would look something like this (intensity + // ranges from 0-F and each letter is one pixel in the bitmap). + // 4BFFFFFFFFFFFFB4 + // CFFFFFFFFFFFFFFC + // FFFFFFFFFFFFFFFF + // FFFFFFFFFFFFFFFF + // FFFFFFFFFFFFFFFF + // FFFFFFFFFFFFFFFF + // FFFFFFFFFFFFFFFF + // FFFFFFFFFFFFFFFF + // FFFFFFFFFFFFFFFF + // FFFFFFFFFFFFFFFF + // CFFFFFFFFFFFFFFC + // 4BFFFFFFFFFFFFB4 + + // We first figure out how much we need to expand the mask (the margin) to account + // for the blurred area. In the example, margin will be 1x1 + SkIVector margin; SkMaskBuilder srcM(nullptr, rrect.rect().roundOut(), 0, SkMask::kA8_Format), dstM; - - bool filterResult = false; - if (c_analyticBlurRRect) { - // special case for fast round rect blur - // don't actually do the blur the first time, just compute the correct size - filterResult = this->filterRRectMask(&dstM, rrect, matrix, &margin, - SkMaskBuilder::kJustComputeBounds_CreateMode); + if (!this->filterMask(&dstM, srcM, matrix, &margin)) { + return std::nullopt; } - if (!filterResult) { - filterResult = this->filterMask(&dstM, srcM, matrix, &margin); - } + // Most of the pixels in the center of the bitmap are the same as their neighbors, so + // blurring is a waste of compute. If we made a smaller nine-patch rrect, blurred + // that, we could then expand the result later, saving cycles. As a bonus, we can cache + // that smaller rectangle and re-use it for other rrects with the same radii/sigma + // combinations. - if (!filterResult) { - return kFalse_FilterReturn; - } - - // Now figure out the appropriate width and height of the smaller round rectangle - // to stretch. It will take into account the larger radius per side as well as double - // the margin, to account for inner and outer blur. + // To figure out the appropriate width and height of the nine patch rrect, we use the + // larger radius per side as well as the margin, to account for inner blur. In the example, + // the minimum nine patch is 9 x 7 + // + // width: ceil(2.5) + 1 (margin) + 1 (stretch) + 1 (margin) + ceil(2.5) = 9 + // height: ceil(2.0) + 1 (margin) + 1 (stretch) + 1 (margin) + ceil(2.0) = 7 + // + // 4BFF F FFB4 + // CFFF F FFFC + // FFFF F FFFF + // + // FFFF F FFFF + // + // FFFF F FFFF + // CFFF F FFFC + // 4BFF F FFB4 const SkVector& UL = rrect.radii(SkRRect::kUpperLeft_Corner); const SkVector& UR = rrect.radii(SkRRect::kUpperRight_Corner); const SkVector& LR = rrect.radii(SkRRect::kLowerRight_Corner); const SkVector& LL = rrect.radii(SkRRect::kLowerLeft_Corner); - const SkScalar leftUnstretched = std::max(UL.fX, LL.fX) + SkIntToScalar(2 * margin.fX); - const SkScalar rightUnstretched = std::max(UR.fX, LR.fX) + SkIntToScalar(2 * margin.fX); + // If there's a fractional radii, round up so that our final rrect is an integer width + // to allow for symmetrical blurring across the x and y axes. + const int32_t leftUnstretched = SkScalarCeilToInt(std::max(UL.fX, LL.fX)) + margin.fX; + const int32_t rightUnstretched = SkScalarCeilToInt(std::max(UR.fX, LR.fX)) + margin.fX; - // Extra space in the middle to ensure an unchanging piece for stretching. Use 3 to cover - // any fractional space on either side plus 1 for the part to stretch. - const SkScalar stretchSize = SkIntToScalar(3); + // Extra space in the middle to ensure an unchanging piece for stretching. + const int32_t stretchSize = 1; - const SkScalar totalSmallWidth = leftUnstretched + rightUnstretched + stretchSize; + const int32_t totalSmallWidth = leftUnstretched + rightUnstretched + stretchSize; if (totalSmallWidth >= rrect.rect().width()) { // There is no valid piece to stretch. - return kUnimplemented_FilterReturn; + return std::nullopt; } - const SkScalar topUnstretched = std::max(UL.fY, UR.fY) + SkIntToScalar(2 * margin.fY); - const SkScalar bottomUnstretched = std::max(LL.fY, LR.fY) + SkIntToScalar(2 * margin.fY); + const int32_t topUnstretched = SkScalarCeilToInt(std::max(UL.fY, UR.fY)) + margin.fY; + const int32_t botUnstretched = SkScalarCeilToInt(std::max(LL.fY, LR.fY)) + margin.fY; - const SkScalar totalSmallHeight = topUnstretched + bottomUnstretched + stretchSize; + const int32_t totalSmallHeight = topUnstretched + botUnstretched + stretchSize; if (totalSmallHeight >= rrect.rect().height()) { // There is no valid piece to stretch. - return kUnimplemented_FilterReturn; + return std::nullopt; } + // Now make that scaled down nine patch rrect. SkRect smallR = SkRect::MakeWH(totalSmallWidth, totalSmallHeight); - SkRRect smallRR; - SkVector radii[4]; - radii[SkRRect::kUpperLeft_Corner] = UL; - radii[SkRRect::kUpperRight_Corner] = UR; - radii[SkRRect::kLowerRight_Corner] = LR; - radii[SkRRect::kLowerLeft_Corner] = LL; - smallRR.setRectRadii(smallR, radii); + smallRR.setRectRadii(smallR, rrect.radii().begin()); - const SkScalar sigma = this->computeXformedSigma(matrix); + const float sigma = this->computeXformedSigma(matrix); + // If we've already blurred this small rrect, pull it out of the cache and we are done SkTLazy cachedMask; SkCachedData* cache = find_cached_rrect(&cachedMask, sigma, fBlurStyle, smallRR); if (!cache) { + // Blit the small rrect into a buffer (9x7) + // 4BFFFFFB4 + // CFFFFFFFC + // FFFFFFFFF + // FFFFFFFFF + // FFFFFFFFF + // CFFFFFFFC + // 4BFFFFFB4 + if (!draw_rrect_into_mask(smallRR, &srcM)) { + return std::nullopt; + } + SkAutoMaskFreeImage amf(srcM.image()); // delete small rrect's pixels when done + + // Blur the small rrect. This will expand the mask on all sides by margin to account for + // outer blur (11x9) + // 00111111100 + // 04ADEEEDA40 + // 1BFFFFFFFB1 + // 1EFFFFFFFE1 + // 1EFFFFFFFE1 + // 1EFFFFFFFE1 + // 1BFFFFFFFB1 + // 04ADEEEDA40 + // 00111111100 SkMaskBuilder filterM; - bool analyticBlurWorked = false; - if (c_analyticBlurRRect) { - analyticBlurWorked = - this->filterRRectMask(&filterM, smallRR, matrix, &margin, - SkMaskBuilder::kComputeBoundsAndRenderImage_CreateMode); + if (!this->filterMask(&filterM, srcM, matrix, nullptr)) { + return std::nullopt; } + SkASSERT(filterM.fBounds.width() == (srcM.fBounds.width() + 2*margin.fX)); + SkASSERT(filterM.fBounds.height() == (srcM.fBounds.height() + 2*margin.fY)); - if (!analyticBlurWorked) { - if (!draw_rrect_into_mask(smallRR, &srcM)) { - return kFalse_FilterReturn; - } - SkAutoMaskFreeImage amf(srcM.image()); - - if (!this->filterMask(&filterM, srcM, matrix, &margin)) { - return kFalse_FilterReturn; - } - } cache = add_cached_rrect(&filterM, sigma, fBlurStyle, smallRR); cachedMask.init(filterM); } + // The bounds of the blurred mask are at -margin, so we need to offset it back to 0,0 SkIRect bounds = cachedMask->fBounds; bounds.offsetTo(0, 0); - patch->init(SkMask{cachedMask->fImage, bounds, cachedMask->fRowBytes, cachedMask->fFormat}, - dstM.fBounds, - SkIPoint{SkScalarCeilToInt(leftUnstretched) + 1, - SkScalarCeilToInt(topUnstretched) + 1}, - cache); // transfer ownership to patch - return kTrue_FilterReturn; + // The ninepatch could be asymmetrical (e.g. if the left rX are wider than the right), so + // we must tell the caller where the center stretchy bit is in both directions. We added + // margin once before to unstretched to account for inner blur, but now we add to account + // for the outer blur. In the example, this will be (5, 4) [note that it's 0-indexed] + SkIPoint center = SkIPoint{margin.fX + leftUnstretched, + margin.fY + topUnstretched}; + return std::optional( + std::in_place, + SkMask{cachedMask->fImage, bounds, cachedMask->fRowBytes, cachedMask->fFormat}, + dstM.fBounds, + center, + cache); // transfer ownership to patch } -// Use the faster analytic blur approach for ninepatch rects -static const bool c_analyticBlurNinepatch{true}; - SkMaskFilterBase::FilterReturn -SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count, +SkBlurMaskFilterImpl::filterRectsToNine(SkSpan rects, const SkMatrix& matrix, const SkIRect& clipBounds, - SkTLazy* patch) const { - if (count < 1 || count > 2) { - return kUnimplemented_FilterReturn; - } + std::optional* patch) const { + SkASSERT(patch != nullptr); + SkASSERT(rects.size() == 1 || rects.size() == 2); // TODO: report correct metrics for innerstyle, where we do not grow the // total bounds, but we do need an inset the size of our blur-radius if (kInner_SkBlurStyle == fBlurStyle || kOuter_SkBlurStyle == fBlurStyle) { - return kUnimplemented_FilterReturn; + return FilterReturn::kUnimplemented; } // TODO: take clipBounds into account to limit our coordinates up front // for now, just skip too-large src rects (to take the old code path). if (rect_exceeds(rects[0], SkIntToScalar(32767))) { - return kUnimplemented_FilterReturn; + return FilterReturn::kUnimplemented; } SkIPoint margin; SkMaskBuilder srcM(nullptr, rects[0].roundOut(), 0, SkMask::kA8_Format), dstM; bool filterResult = false; - if (count == 1 && c_analyticBlurNinepatch) { + if (rects.size() == 1) { // special case for fast rect blur // don't actually do the blur the first time, just compute the correct size filterResult = this->filterRectMask(&dstM, rects[0], matrix, &margin, @@ -415,7 +446,7 @@ SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count, } if (!filterResult) { - return kFalse_FilterReturn; + return FilterReturn::kFalse; } /* @@ -433,6 +464,7 @@ SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count, * with our outer-rect (dstM.fBounds) */ SkRect smallR[2]; + int rectCount; SkIPoint center; // +2 is from +1 for each edge (to account for possible fractional edges @@ -440,11 +472,12 @@ SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count, int smallH = dstM.fBounds.height() - srcM.fBounds.height() + 2; SkIRect innerIR; - if (1 == count) { + if (rects.size() == 1) { + rectCount = 1; innerIR = srcM.fBounds; center.set(smallW, smallH); } else { - SkASSERT(2 == count); + rectCount = 2; rects[1].roundIn(&innerIR); center.set(smallW + (innerIR.left() - srcM.fBounds.left()), smallH + (innerIR.top() - srcM.fBounds.top())); @@ -461,15 +494,15 @@ SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count, if (dx < 0 || dy < 0) { // we're too small, relative to our blur, to break into nine-patch, // so we ask to have our normal filterMask() be called. - return kUnimplemented_FilterReturn; + return FilterReturn::kUnimplemented; } smallR[0].setLTRB(rects[0].left(), rects[0].top(), rects[0].right() - dx, rects[0].bottom() - dy); if (smallR[0].width() < 2 || smallR[0].height() < 2) { - return kUnimplemented_FilterReturn; + return FilterReturn::kUnimplemented; } - if (2 == count) { + if (rectCount == 2) { smallR[1].setLTRB(rects[1].left(), rects[1].top(), rects[1].right() - dx, rects[1].bottom() - dy); SkASSERT(!smallR[1].isEmpty()); @@ -477,33 +510,36 @@ SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count, const SkScalar sigma = this->computeXformedSigma(matrix); SkTLazy cachedMask; - SkCachedData* cache = find_cached_rects(&cachedMask, sigma, fBlurStyle, smallR, count); + SkSpan smallRects = SkSpan(smallR, rectCount); + SkCachedData* cache = find_cached_rects(&cachedMask, sigma, fBlurStyle, smallRects); if (!cache) { SkMaskBuilder filterM; - if (count > 1 || !c_analyticBlurNinepatch) { - if (!draw_rects_into_mask(smallR, count, &srcM)) { - return kFalse_FilterReturn; + if (rectCount == 2) { + if (!draw_rects_into_mask(smallRects, &srcM)) { + return FilterReturn::kFalse; } SkAutoMaskFreeImage amf(srcM.image()); - if (!this->filterMask(&filterM, srcM, matrix, &margin)) { - return kFalse_FilterReturn; + if (!this->filterMask(&filterM, srcM, matrix, nullptr)) { + return FilterReturn::kFalse; } } else { - if (!this->filterRectMask(&filterM, smallR[0], matrix, &margin, + if (!this->filterRectMask(&filterM, smallR[0], matrix, nullptr, SkMaskBuilder::kComputeBoundsAndRenderImage_CreateMode)) { - return kFalse_FilterReturn; + return FilterReturn::kFalse; } } - cache = add_cached_rects(&filterM, sigma, fBlurStyle, smallR, count); + cache = add_cached_rects(&filterM, sigma, fBlurStyle, smallRects); cachedMask.init(filterM); } SkIRect bounds = cachedMask->fBounds; bounds.offsetTo(0, 0); - patch->init(SkMask{cachedMask->fImage, bounds, cachedMask->fRowBytes, cachedMask->fFormat}, - dstM.fBounds, center, cache); // transfer ownership to patch - return kTrue_FilterReturn; + patch->emplace(SkMask{cachedMask->fImage, bounds, cachedMask->fRowBytes, cachedMask->fFormat}, + dstM.fBounds, + center, + cache); // transfer ownership to patch + return FilterReturn::kTrue; } void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src, diff --git a/gfx/skia/skia/src/core/SkBlurMaskFilterImpl.h b/gfx/skia/skia/src/core/SkBlurMaskFilterImpl.h index 014a785af984..0ee03524289d 100644 --- a/gfx/skia/skia/src/core/SkBlurMaskFilterImpl.h +++ b/gfx/skia/skia/src/core/SkBlurMaskFilterImpl.h @@ -11,9 +11,12 @@ #include "include/core/SkFlattenable.h" #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" +#include "include/core/SkSpan.h" #include "src/core/SkMask.h" #include "src/core/SkMaskFilterBase.h" +#include + class SkImageFilter; class SkMatrix; class SkRRect; @@ -23,7 +26,6 @@ enum SkBlurStyle : int; struct SkIPoint; struct SkIRect; struct SkRect; -template class SkTLazy; class SkBlurMaskFilterImpl : public SkMaskFilterBase { public: @@ -46,18 +48,20 @@ public: bool ignoreXform() const { return !fRespectCTM; } private: - FilterReturn filterRectsToNine(const SkRect[], int count, const SkMatrix&, + FilterReturn filterRectsToNine(SkSpan, + const SkMatrix&, const SkIRect& clipBounds, - SkTLazy*) const override; + std::optional*) const override; - FilterReturn filterRRectToNine(const SkRRect&, const SkMatrix&, - const SkIRect& clipBounds, - SkTLazy*) const override; + std::optional filterRRectToNine(const SkRRect&, + const SkMatrix&, + const SkIRect& clipBounds) const override; - bool filterRectMask(SkMaskBuilder* dstM, const SkRect& r, const SkMatrix& matrix, - SkIPoint* margin, SkMaskBuilder::CreateMode createMode) const; - bool filterRRectMask(SkMaskBuilder* dstM, const SkRRect& r, const SkMatrix& matrix, - SkIPoint* margin, SkMaskBuilder::CreateMode createMode) const; + bool filterRectMask(SkMaskBuilder* dstM, + const SkRect& r, + const SkMatrix& matrix, + SkIPoint* margin, + SkMaskBuilder::CreateMode createMode) const; SK_FLATTENABLE_HOOKS(SkBlurMaskFilterImpl) diff --git a/gfx/skia/skia/src/core/SkCanvas.cpp b/gfx/skia/skia/src/core/SkCanvas.cpp index faf395bab14a..0d77521e62e9 100644 --- a/gfx/skia/skia/src/core/SkCanvas.cpp +++ b/gfx/skia/skia/src/core/SkCanvas.cpp @@ -303,11 +303,6 @@ void SkCanvas::resetForNextPicture(const SkIRect& bounds) { } void SkCanvas::init(sk_sp device) { - // SkCanvas.h declares internal storage for the hidden struct MCRec, and this - // assert ensure it's sufficient. <= is used because the struct has pointer fields, so the - // declared size is an upper bound across architectures. When the size is smaller, more stack - static_assert(sizeof(MCRec) <= kMCRecSize); - if (!device) { device = sk_make_sp(SkIRect::MakeEmpty(), fProps); } @@ -536,7 +531,7 @@ int SkCanvas::only_axis_aligned_saveBehind(const SkRect* bounds) { // Helper function to compute the center reference point used for scale decomposition under // non-linear transformations. static skif::ParameterSpace compute_decomposition_center( - const SkMatrix& dstToLocal, + const SkM44& dstToLocal, std::optional> contentBounds, const skif::DeviceSpace& targetOutput) { // Will use the inverse and center of the device bounds if the content bounds aren't provided. @@ -545,7 +540,9 @@ static skif::ParameterSpace compute_decomposition_center( if (!contentBounds) { // Theoretically, the inverse transform could put center's homogeneous coord behind W = 0, // but that case is handled automatically in Mapping::decomposeCTM later. - dstToLocal.mapPoints(¢er, 1); + SkV4 mappedCenter = dstToLocal.map(center.fX, center.fY, 0.f, 1.f); + center = {sk_ieee_float_divide(mappedCenter.x, mappedCenter.w), + sk_ieee_float_divide(mappedCenter.y, mappedCenter.w)}; } return skif::ParameterSpace(center); @@ -573,11 +570,11 @@ struct FilterToSpan { static std::optional>> get_layer_mapping_and_bounds( SkCanvas::FilterSpan filters, - const SkMatrix& localToDst, + const SkM44& localToDst, const skif::DeviceSpace& targetOutput, std::optional> contentBounds = {}, SkScalar scaleFactor = 1.0f) { - SkMatrix dstToLocal; + SkM44 dstToLocal; if (!localToDst.isFinite() || !localToDst.invert(&dstToLocal)) { return {}; @@ -601,7 +598,7 @@ get_layer_mapping_and_bounds( // Push scale factor into layer matrix and device matrix (net no change, but the layer will have // its resolution adjusted in comparison to the final device). if (scaleFactor != 1.0f && - !mapping.adjustLayerSpace(SkMatrix::Scale(scaleFactor, scaleFactor))) { + !mapping.adjustLayerSpace(SkM44::Scale(scaleFactor, scaleFactor))) { return {}; } @@ -657,9 +654,8 @@ get_layer_mapping_and_bounds( skif::LayerSpace newLayerBounds( SkIRect::MakeWH(std::min(layerBounds.width(), maxLayerDim), std::min(layerBounds.height(), maxLayerDim))); - SkMatrix adjust = SkMatrix::MakeRectToRect(SkRect::Make(SkIRect(layerBounds)), - SkRect::Make(SkIRect(newLayerBounds)), - SkMatrix::kFill_ScaleToFit); + SkM44 adjust = SkM44::RectToRect(SkRect::Make(SkIRect(layerBounds)), + SkRect::Make(SkIRect(newLayerBounds))); if (!mapping.adjustLayerSpace(adjust)) { return {}; } else { @@ -724,8 +720,7 @@ void SkCanvas::internalDrawDeviceWithFilter(SkDevice* src, // in this device space (referred to as the "layer" space). However, the filter // parameters need to respect the current matrix, which is not necessarily the local matrix that // was set on 'src' (e.g. because we've popped src off the stack already). - // TODO (michaelludwig): Stay in SkM44 once skif::Mapping supports SkM44 instead of SkMatrix. - SkMatrix localToSrc = src ? (src->globalToDevice() * fMCRec->fMatrix).asM33() : SkMatrix::I(); + SkM44 localToSrc = src ? (src->globalToDevice() * fMCRec->fMatrix) : SkM44(); SkISize srcDims = src ? src->imageInfo().dimensions() : SkISize::Make(0, 0); // Whether or not we need to make a transformed tmp image from 'src', and what that transform is @@ -754,7 +749,7 @@ void SkCanvas::internalDrawDeviceWithFilter(SkDevice* src, // Compute the image filter mapping by decomposing the local->device matrix of dst and // re-determining the required input. auto mappingAndBounds = get_layer_mapping_and_bounds( - filters, dst->localToDevice(), outputBounds, {}, SkTPin(scaleFactor, 0.f, 1.f)); + filters, dst->localToDevice44(), outputBounds, {}, SkTPin(scaleFactor, 0.f, 1.f)); if (!mappingAndBounds) { return; } @@ -765,12 +760,11 @@ void SkCanvas::internalDrawDeviceWithFilter(SkDevice* src, // The above mapping transforms from local to dst's device space, where the layer // space represents the intermediate buffer. Now we need to determine the transform // from src to intermediate to prepare the input to the filter. - SkMatrix srcToLocal; + SkM44 srcToLocal; if (!localToSrc.invert(&srcToLocal)) { return; } - srcToLayer = skif::LayerSpace(SkMatrix::Concat(mapping.layerMatrix(), - srcToLocal)); + srcToLayer = skif::LayerSpace((mapping.layerMatrix()*srcToLocal).asM33()); } // Else no input is needed which can happen if a backdrop filter that doesn't use src } else { // Trust the caller that no input was required, but keep the calculated mapping @@ -867,10 +861,11 @@ void SkCanvas::internalDrawDeviceWithFilter(SkDevice* src, // through drawCoverageMask that requires an image (vs a coverage shader)? auto [coverageMask, origin] = result.imageAndOffset(ctx); if (coverageMask) { - SkMatrix deviceMatrixWithOffset = mapping.layerToDevice(); + SkM44 deviceMatrixWithOffset = mapping.layerToDevice(); deviceMatrixWithOffset.preTranslate(origin.x(), origin.y()); dst->drawCoverageMask( - coverageMask.get(), deviceMatrixWithOffset, result.sampling(), paint); + coverageMask.get(), deviceMatrixWithOffset.asM33(), + result.sampling(), paint); } } else { result = apply_alpha_and_colorfilter(ctx, result, paint); @@ -951,7 +946,7 @@ void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, } auto mappingAndBounds = get_layer_mapping_and_bounds( - filters, priorDevice->localToDevice(), outputBounds, contentBounds); + filters, priorDevice->localToDevice44(), outputBounds, contentBounds); auto abortLayer = [this]() { // The filtered content would not draw anything, or the new device space has an invalid @@ -1063,9 +1058,9 @@ void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, // 'newLayerMapping' only defines the transforms between the two devices and it must be updated // to the global coordinate system. newDevice->setDeviceCoordinateSystem( - priorDevice->deviceToGlobal() * SkM44(newLayerMapping.layerToDevice()), - SkM44(newLayerMapping.deviceToLayer()) * priorDevice->globalToDevice(), - SkM44(newLayerMapping.layerMatrix()), + priorDevice->deviceToGlobal() * newLayerMapping.layerToDevice(), + newLayerMapping.deviceToLayer() * priorDevice->globalToDevice(), + newLayerMapping.layerMatrix(), layerBounds.left(), layerBounds.top()); @@ -1962,7 +1957,12 @@ void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[], } } -static const SkBlurMaskFilterImpl* can_attempt_blurred_rrect_draw(const SkPaint& paint) { +const SkBlurMaskFilterImpl* SkCanvas::canAttemptBlurredRRectDraw(const SkPaint& paint) const { + if (!this->topDevice()->useDrawCoverageMaskForMaskFilters()) { + // Perform a regular draw in the legacy mask filter case. + return nullptr; + } + if (paint.getPathEffect()) { return nullptr; } @@ -1983,31 +1983,24 @@ static const SkBlurMaskFilterImpl* can_attempt_blurred_rrect_draw(const SkPaint& return nullptr; } - return blurMaskFilter; -} - -std::optional SkCanvas::attemptBlurredRRectDraw( - const SkRRect& rrect, const SkPaint& paint, SkEnumBitMask flags) { - SkASSERT(!(flags & PredrawFlags::kSkipMaskFilterAutoLayer)); - const SkRect& bounds = rrect.getBounds(); - - if (!this->topDevice()->useDrawCoverageMaskForMaskFilters()) { - // Regular draw in the legacy mask filter case. - return this->aboutToDraw(paint, &bounds, flags); - } - if (!this->getTotalMatrix().isSimilarity()) { // TODO: If the CTM does more than just translation, rotation, and uniform scale, then the // results of analytic blurring will be different than mask filter blurring. Skip the // specialized path in this case. - return this->aboutToDraw(paint, &bounds, flags); + return nullptr; } - const SkBlurMaskFilterImpl* blurMaskFilter = can_attempt_blurred_rrect_draw(paint); - if (!blurMaskFilter) { - // Can't attempt a specialized blurred draw, so do a regular draw. - return this->aboutToDraw(paint, &bounds, flags); - } + return blurMaskFilter; +} + +std::optional SkCanvas::attemptBlurredRRectDraw( + const SkRRect& rrect, + const SkBlurMaskFilterImpl* blurMaskFilter, + const SkPaint& paint, + SkEnumBitMask flags) { + SkASSERT(blurMaskFilter && blurMaskFilter == this->canAttemptBlurredRRectDraw(paint) && + !(flags & PredrawFlags::kSkipMaskFilterAutoLayer)); + const SkRect& bounds = rrect.getBounds(); auto layer = this->aboutToDraw(paint, &bounds, flags | PredrawFlags::kSkipMaskFilterAutoLayer); if (!layer) { @@ -2033,9 +2026,16 @@ void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) { return; } - // Returns a layer if a blurred draw is not applicable or was unsuccessful. - std::optional layer = this->attemptBlurredRRectDraw( - SkRRect::MakeRect(r), paint, PredrawFlags::kCheckForOverwrite); + std::optional layer; + constexpr PredrawFlags kPredrawFlags = PredrawFlags::kCheckForOverwrite; + + if (const SkBlurMaskFilterImpl* blurMaskFilter = this->canAttemptBlurredRRectDraw(paint)) { + // Returns a layer if a blurred draw was unsuccessful. + layer = this->attemptBlurredRRectDraw( + SkRRect::MakeRect(r), blurMaskFilter, paint, kPredrawFlags); + } else { + layer = this->aboutToDraw(paint, &r, kPredrawFlags); + } if (layer) { this->topDevice()->drawRect(r, layer->paint()); @@ -2085,7 +2085,7 @@ void SkCanvas::onDrawBehind(const SkPaint& paint) { { // We also have to temporarily whack the device matrix since clipRegion is affected by the // global-to-device matrix and clipRect is affected by the local-to-device. - SkAutoDeviceTransformRestore adtr(dev, SkMatrix::I()); + SkAutoDeviceTransformRestore adtr(dev, SkM44()); dev->clipRect(SkRect::Make(bounds), SkClipOp::kIntersect, /* aa */ false); // ~adtr will reset the local-to-device matrix so that drawPaint() shades correctly. } @@ -2104,9 +2104,15 @@ void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) { return; } - // Returns a layer if a blurred draw is not applicable or was unsuccessful. - std::optional layer = - this->attemptBlurredRRectDraw(SkRRect::MakeOval(oval), paint, PredrawFlags::kNone); + std::optional layer; + + if (const SkBlurMaskFilterImpl* blurMaskFilter = this->canAttemptBlurredRRectDraw(paint)) { + // Returns a layer if a blurred draw was unsuccessful. + layer = this->attemptBlurredRRectDraw( + SkRRect::MakeOval(oval), blurMaskFilter, paint, PredrawFlags::kNone); + } else { + layer = this->aboutToDraw(paint, &oval); + } if (layer) { this->topDevice()->drawOval(oval, layer->paint()); @@ -2121,7 +2127,18 @@ void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle, return; } - auto layer = this->aboutToDraw(paint, &oval); + std::optional layer; + + // Arcs with sweeps >= 360° are ovals. In this case, attempt a specialized blurred draw. + if (const SkBlurMaskFilterImpl* blurMaskFilter = this->canAttemptBlurredRRectDraw(paint); + blurMaskFilter && SkScalarAbs(sweepAngle) >= 360.f) { + // Returns a layer if a blurred draw was unsuccessful. + layer = this->attemptBlurredRRectDraw( + SkRRect::MakeOval(oval), blurMaskFilter, paint, PredrawFlags::kNone); + } else { + layer = this->aboutToDraw(paint, &oval); + } + if (layer) { this->topDevice()->drawArc(SkArc::Make(oval, startAngle, sweepAngle, useCenter), layer->paint()); @@ -2146,9 +2163,14 @@ void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) { return; } - // Returns a layer if a blurred draw is not applicable or was unsuccessful. - std::optional layer = - this->attemptBlurredRRectDraw(rrect, paint, PredrawFlags::kNone); + std::optional layer; + + if (const SkBlurMaskFilterImpl* blurMaskFilter = this->canAttemptBlurredRRectDraw(paint)) { + // Returns a layer if a blurred draw was unsuccessful. + layer = this->attemptBlurredRRectDraw(rrect, blurMaskFilter, paint, PredrawFlags::kNone); + } else { + layer = this->aboutToDraw(paint, &bounds); + } if (layer) { this->topDevice()->drawRRect(rrect, layer->paint()); @@ -2183,7 +2205,7 @@ void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { auto layer = this->aboutToDraw(paint, path.isInverseFillType() ? nullptr : &pathBounds); if (layer) { - this->topDevice()->drawPath(path, layer->paint()); + this->topDevice()->drawPath(path, layer->paint(), false); } } @@ -2263,7 +2285,7 @@ void SkCanvas::onDrawImageRect2(const SkImage* image, const SkRect& src, const S skif::DeviceSpace outputBounds{device->devClipBounds()}; FilterToSpan filterAsSpan(realPaint.getImageFilter()); auto mappingAndBounds = get_layer_mapping_and_bounds(filterAsSpan, - device->localToDevice(), + device->localToDevice44(), outputBounds, imageBounds); if (!mappingAndBounds) { diff --git a/gfx/skia/skia/src/core/SkColor.cpp b/gfx/skia/skia/src/core/SkColor.cpp index 02a79e43eb51..e76c01ea5ce0 100644 --- a/gfx/skia/skia/src/core/SkColor.cpp +++ b/gfx/skia/skia/src/core/SkColor.cpp @@ -6,10 +6,11 @@ */ #include "include/core/SkColor.h" -#include "include/core/SkColorPriv.h" -#include "include/private/SkColorData.h" #include "include/private/base/SkTPin.h" +#include "include/private/chromium/SkPMColor.h" #include "src/base/SkVx.h" +#include "src/core/SkColorData.h" +#include "src/core/SkColorPriv.h" #include "src/core/SkSwizzlePriv.h" #include @@ -23,6 +24,26 @@ SkPMColor SkPreMultiplyColor(SkColor c) { SkColorGetG(c), SkColorGetB(c)); } +SkPMColor SkPMColorSetARGB(uint8_t a, uint8_t r, uint8_t g, uint8_t b) { + return SkPackARGB32(a, r, g, b); +} + +SkAlpha SkPMColorGetA(SkPMColor c) { + return SkGetPackedA32(c); +} + +uint8_t SkPMColorGetR(SkPMColor c) { + return SkGetPackedR32(c); +} + +uint8_t SkPMColorGetG(SkPMColor c) { + return SkGetPackedG32(c); +} + +uint8_t SkPMColorGetB(SkPMColor c) { + return SkGetPackedB32(c); +} + /////////////////////////////////////////////////////////////////////////////// static inline SkScalar ByteToScalar(U8CPU x) { diff --git a/gfx/skia/skia/include/private/SkColorData.h b/gfx/skia/skia/src/core/SkColorData.h similarity index 91% rename from gfx/skia/skia/include/private/SkColorData.h rename to gfx/skia/skia/src/core/SkColorData.h index 0f6a3e9aa51e..8357089701d4 100644 --- a/gfx/skia/skia/include/private/SkColorData.h +++ b/gfx/skia/skia/src/core/SkColorData.h @@ -8,9 +8,15 @@ #ifndef SkColorData_DEFINED #define SkColorData_DEFINED +#include "include/core/SkAlphaType.h" #include "include/core/SkColor.h" -#include "include/core/SkColorPriv.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 "src/core/SkColorPriv.h" + +#include //////////////////////////////////////////////////////////////////////////////////////////// // Convert a 16bit pixel to a 32bit pixel @@ -183,9 +189,9 @@ static inline SkPMColor SkFourByteInterp(SkPMColor src, SkPMColor dst, U8CPU src * 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; + static constexpr uint32_t kMask = 0x00FF00FF; + *ag = (color >> 8) & kMask; + *rb = color & kMask; } /** @@ -193,10 +199,10 @@ static inline void SkSplay(uint32_t color, uint32_t* ag, uint32_t* rb) { * (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 + static constexpr uint32_t kMask = 0x00FF00FF; + uint64_t agrb = (color >> 8) & kMask; // 0x0000000000AA00GG + agrb <<= 32; // 0x00AA00GG00000000 + agrb |= color & kMask; // 0x00AA00GG00RR00BB return agrb; } @@ -204,8 +210,8 @@ static inline uint64_t SkSplay(uint32_t color) { * 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); + static constexpr uint32_t kMask = 0xFF00FF00; + return (ag & kMask) | ((rb & kMask) >> 8); } /** @@ -213,10 +219,10 @@ static inline uint32_t SkUnsplay(uint32_t ag, uint32_t rb) { * (note, AGRB -> ARGB) */ static inline uint32_t SkUnsplay(uint64_t agrb) { - const uint32_t mask = 0xFF00FF00; + static constexpr uint32_t kMask = 0xFF00FF00; return SkPMColor( - ((agrb & mask) >> 8) | // 0x00RR00BB - ((agrb >> 32) & mask)); // 0xAARRGGBB + ((agrb & kMask) >> 8) | // 0x00RR00BB + ((agrb >> 32) & kMask)); // 0xAARRGGBB } static inline SkPMColor SkFastFourByteInterp256_32(SkPMColor src, SkPMColor dst, unsigned scale) { @@ -277,15 +283,15 @@ static inline SkPMColor SkBlendARGB32(SkPMColor src, SkPMColor dst, U8CPU aa) { unsigned src_scale = SkAlpha255To256(aa); unsigned dst_scale = SkAlphaMulInv256(SkGetPackedA32(src), src_scale); - const uint32_t mask = 0xFF00FF; + static constexpr uint32_t kMask = 0x00FF00FF; - uint32_t src_rb = (src & mask) * src_scale; - uint32_t src_ag = ((src >> 8) & mask) * src_scale; + uint32_t src_rb = (src & kMask) * src_scale; + uint32_t src_ag = ((src >> 8) & kMask) * src_scale; - uint32_t dst_rb = (dst & mask) * dst_scale; - uint32_t dst_ag = ((dst >> 8) & mask) * dst_scale; + uint32_t dst_rb = (dst & kMask) * dst_scale; + uint32_t dst_ag = ((dst >> 8) & kMask) * dst_scale; - return (((src_rb + dst_rb) >> 8) & mask) | ((src_ag + dst_ag) & ~mask); + return (((src_rb + dst_rb) >> 8) & kMask) | ((src_ag + dst_ag) & ~kMask); } //////////////////////////////////////////////////////////////////////////////////////////// diff --git a/gfx/skia/skia/src/core/SkColorFilter.cpp b/gfx/skia/skia/src/core/SkColorFilter.cpp index 8cc7791d29a9..640d755b58ca 100644 --- a/gfx/skia/skia/src/core/SkColorFilter.cpp +++ b/gfx/skia/skia/src/core/SkColorFilter.cpp @@ -11,9 +11,8 @@ #include "include/core/SkColorSpace.h" #include "include/core/SkFlattenable.h" #include "include/core/SkRefCnt.h" -#include "include/private/SkColorData.h" -#include "include/private/base/SkTPin.h" #include "modules/skcms/skcms.h" +#include "src/core/SkColorData.h" #include "src/core/SkColorFilterPriv.h" #include "src/core/SkColorSpaceXformSteps.h" #include "src/effects/colorfilters/SkColorFilterBase.h" @@ -48,10 +47,8 @@ SkColor4f SkColorFilter::filterColor4f(const SkColor4f& origSrcColor, SkColorSpa SkColorSpaceXformSteps(srcCS, kUnpremul_SkAlphaType, dstCS, kPremul_SkAlphaType).apply(color.vec()); - SkPMColor4f filteredColor = as_CFB(this)->onFilterColor4f(color, dstCS); // SkColor4f will assert if we allow alpha outside [0,1]. (SkSL color filters might do this). - filteredColor.fA = SkTPin(filteredColor.fA, 0.0f, 1.0f); - return filteredColor.unpremul(); + return as_CFB(this)->onFilterColor4f(color, dstCS).pinAlpha().unpremul(); } sk_sp SkColorFilter::makeWithWorkingColorSpace( diff --git a/gfx/skia/skia/include/core/SkColorPriv.h b/gfx/skia/skia/src/core/SkColorPriv.h similarity index 88% rename from gfx/skia/skia/include/core/SkColorPriv.h rename to gfx/skia/skia/src/core/SkColorPriv.h index 1ecbd221981b..6b9eea0cccbe 100644 --- a/gfx/skia/skia/include/core/SkColorPriv.h +++ b/gfx/skia/skia/src/core/SkColorPriv.h @@ -9,11 +9,15 @@ #define SkColorPriv_DEFINED #include "include/core/SkColor.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" +#include "include/private/base/SkCPUTypes.h" #include "include/private/base/SkMath.h" #include "include/private/base/SkTPin.h" #include "include/private/base/SkTo.h" #include +#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 @@ -112,14 +116,6 @@ static inline SkPMColor SkPackARGB32(U8CPU a, U8CPU r, U8CPU g, U8CPU b) { (g << SK_G32_SHIFT) | (b << SK_B32_SHIFT); } -/** - * Legacy "NoCheck" version of SkPackARGB32. Remove this once all callers are updated. - */ -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); @@ -138,22 +134,22 @@ SkPMColor SkPremultiplyARGBInline(U8CPU a, U8CPU r, U8CPU g, U8CPU 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; + static constexpr uint32_t kMask = 0x00FF00FF; - uint32_t rb = ((c & mask) * scale) >> 8; - uint32_t ag = ((c >> 8) & mask) * scale; - return (rb & mask) | (ag & ~mask); + uint32_t rb = ((c & kMask) * scale) >> 8; + uint32_t ag = ((c >> 8) & kMask) * scale; + return (rb & kMask) | (ag & ~kMask); } 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; + static constexpr uint32_t kMask = 0x00FF00FF; + uint32_t rb = (((dst & kMask) * scale) >> 8) & kMask; + uint32_t ag = (((dst >> 8) & kMask) * scale) & ~kMask; - rb += (src & mask); - ag += (src & ~mask); + rb += (src & kMask); + ag += (src & ~kMask); // Color channels (but not alpha) can overflow, so we have to saturate to 0xFF in each lane. return std::min(rb & 0x000001FF, 0x000000FFU) | diff --git a/gfx/skia/skia/src/core/SkColorSpace.cpp b/gfx/skia/skia/src/core/SkColorSpace.cpp index fc62b5183023..a2c39f45cb97 100644 --- a/gfx/skia/skia/src/core/SkColorSpace.cpp +++ b/gfx/skia/skia/src/core/SkColorSpace.cpp @@ -15,6 +15,106 @@ #include #include +namespace SkNamedPrimaries { + +bool GetCicp(CicpId primaries, SkColorSpacePrimaries& sk_primaries) { + // Rec. ITU-T H.273, Table 2. + switch (primaries) { + case CicpId::kRec709: + sk_primaries = kRec709; + return true; + case CicpId::kRec470SystemM: + sk_primaries = kRec470SystemM; + return true; + case CicpId::kRec470SystemBG: + sk_primaries = kRec470SystemBG; + return true; + case CicpId::kRec601: + sk_primaries = kRec601; + return true; + case CicpId::kSMPTE_ST_240: + sk_primaries = kSMPTE_ST_240; + return true; + case CicpId::kGenericFilm: + sk_primaries = kGenericFilm; + return true; + case CicpId::kRec2020: + sk_primaries = kRec2020; + return true; + case CicpId::kSMPTE_ST_428_1: + sk_primaries = kSMPTE_ST_428_1; + return true; + case CicpId::kSMPTE_RP_431_2: + sk_primaries = kSMPTE_RP_431_2; + return true; + case CicpId::kSMPTE_EG_432_1: + sk_primaries = kSMPTE_EG_432_1; + return true; + case CicpId::kITU_T_H273_Value22: + sk_primaries = kITU_T_H273_Value22; + return true; + default: + // Reserved or unimplemented. + break; + } + return false; +} + +} // namespace SkNamedPrimaries + +namespace SkNamedTransferFn { + +bool GetCicp(SkNamedTransferFn::CicpId transfer_characteristics, skcms_TransferFunction& trfn) { + // Rec. ITU-T H.273, Table 3. + switch (transfer_characteristics) { + case SkNamedTransferFn::CicpId::kRec709: + trfn = kRec709; + return true; + case SkNamedTransferFn::CicpId::kRec470SystemM: + trfn = kRec470SystemM; + return true; + case SkNamedTransferFn::CicpId::kRec470SystemBG: + trfn = kRec470SystemBG; + return true; + case SkNamedTransferFn::CicpId::kRec601: + trfn = kRec601; + return true; + case SkNamedTransferFn::CicpId::kSMPTE_ST_240: + trfn = kSMPTE_ST_240; + return true; + case SkNamedTransferFn::CicpId::kLinear: + trfn = SkNamedTransferFn::kLinear; + return true; + case SkNamedTransferFn::CicpId::kIEC61966_2_4: + trfn = kIEC61966_2_4; + break; + case SkNamedTransferFn::CicpId::kIEC61966_2_1: + trfn = SkNamedTransferFn::kIEC61966_2_1; + return true; + case SkNamedTransferFn::CicpId::kRec2020_10bit: + trfn = kRec2020_10bit; + return true; + case SkNamedTransferFn::CicpId::kRec2020_12bit: + trfn = kRec2020_12bit; + return true; + case SkNamedTransferFn::CicpId::kPQ: + trfn = SkNamedTransferFn::kPQ; + return true; + case SkNamedTransferFn::CicpId::kSMPTE_ST_428_1: + trfn = kSMPTE_ST_428_1; + return true; + case SkNamedTransferFn::CicpId::kHLG: + trfn = SkNamedTransferFn::kHLG; + return true; + default: + // Reserved or unimplemented. + break; + } + return false; +} + +} // namespace SkNamedTransferFn + bool SkColorSpacePrimaries::toXYZD50(skcms_Matrix3x3* toXYZ_D50) const { return skcms_PrimariesToXYZD50(fRX, fRY, fGX, fGY, fBX, fBY, fWX, fWY, toXYZ_D50); } @@ -64,6 +164,26 @@ sk_sp SkColorSpace::MakeRGB(const skcms_TransferFunction& transfer return sk_sp(new SkColorSpace(*tf, toXYZ)); } +sk_sp SkColorSpace::MakeCICP(SkNamedPrimaries::CicpId color_primaries, + SkNamedTransferFn::CicpId transfer_characteristics) { + skcms_TransferFunction trfn; + if (!SkNamedTransferFn::GetCicp(transfer_characteristics, trfn)) { + return nullptr; + } + + SkColorSpacePrimaries primaries; + if (!SkNamedPrimaries::GetCicp(color_primaries, primaries)) { + return nullptr; + } + + skcms_Matrix3x3 primaries_matrix; + if (!primaries.toXYZD50(&primaries_matrix)) { + return nullptr; + } + + return SkColorSpace::MakeRGB(trfn, primaries_matrix); +} + class SkColorSpaceSingletonFactory { public: static SkColorSpace* Make(const skcms_TransferFunction& transferFn, diff --git a/gfx/skia/skia/src/core/SkColorSpaceXformSteps.cpp b/gfx/skia/skia/src/core/SkColorSpaceXformSteps.cpp index 3ee1efaf0610..7bf156c0b276 100644 --- a/gfx/skia/skia/src/core/SkColorSpaceXformSteps.cpp +++ b/gfx/skia/skia/src/core/SkColorSpaceXformSteps.cpp @@ -18,7 +18,7 @@ #include -// See skia.org/user/color (== site/user/color.md). +// See skia.org/docs/user/color (== site/docs/user/color.md). SkColorSpaceXformSteps::SkColorSpaceXformSteps(const SkColorSpace* src, SkAlphaType srcAT, const SkColorSpace* dst, SkAlphaType dstAT) { @@ -38,27 +38,27 @@ SkColorSpaceXformSteps::SkColorSpaceXformSteps(const SkColorSpace* src, SkAlphaT return; } - this->flags.unpremul = srcAT == kPremul_SkAlphaType; - this->flags.linearize = !src->gammaIsLinear(); - this->flags.gamut_transform = src->toXYZD50Hash() != dst->toXYZD50Hash(); - this->flags.encode = !dst->gammaIsLinear(); - this->flags.premul = srcAT != kOpaque_SkAlphaType && dstAT == kPremul_SkAlphaType; + this->fFlags.unpremul = srcAT == kPremul_SkAlphaType; + this->fFlags.linearize = !src->gammaIsLinear(); + this->fFlags.gamut_transform = src->toXYZD50Hash() != dst->toXYZD50Hash(); + this->fFlags.encode = !dst->gammaIsLinear(); + this->fFlags.premul = srcAT != kOpaque_SkAlphaType && dstAT == kPremul_SkAlphaType; - if (this->flags.gamut_transform) { - skcms_Matrix3x3 src_to_dst; // TODO: switch src_to_dst_matrix to row-major + if (this->fFlags.gamut_transform) { + skcms_Matrix3x3 src_to_dst; // TODO: switch fSrcToDstMatrix to row-major src->gamutTransformTo(dst, &src_to_dst); - this->src_to_dst_matrix[0] = src_to_dst.vals[0][0]; - this->src_to_dst_matrix[1] = src_to_dst.vals[1][0]; - this->src_to_dst_matrix[2] = src_to_dst.vals[2][0]; + this->fSrcToDstMatrix[0] = src_to_dst.vals[0][0]; + this->fSrcToDstMatrix[1] = src_to_dst.vals[1][0]; + this->fSrcToDstMatrix[2] = src_to_dst.vals[2][0]; - this->src_to_dst_matrix[3] = src_to_dst.vals[0][1]; - this->src_to_dst_matrix[4] = src_to_dst.vals[1][1]; - this->src_to_dst_matrix[5] = src_to_dst.vals[2][1]; + this->fSrcToDstMatrix[3] = src_to_dst.vals[0][1]; + this->fSrcToDstMatrix[4] = src_to_dst.vals[1][1]; + this->fSrcToDstMatrix[5] = src_to_dst.vals[2][1]; - this->src_to_dst_matrix[6] = src_to_dst.vals[0][2]; - this->src_to_dst_matrix[7] = src_to_dst.vals[1][2]; - this->src_to_dst_matrix[8] = src_to_dst.vals[2][2]; + this->fSrcToDstMatrix[6] = src_to_dst.vals[0][2]; + this->fSrcToDstMatrix[7] = src_to_dst.vals[1][2]; + this->fSrcToDstMatrix[8] = src_to_dst.vals[2][2]; } else { #ifdef SK_DEBUG skcms_Matrix3x3 srcM, dstM; @@ -69,39 +69,39 @@ SkColorSpaceXformSteps::SkColorSpaceXformSteps(const SkColorSpace* src, SkAlphaT } // Fill out all the transfer functions we'll use. - src-> transferFn(&this->srcTF ); - dst->invTransferFn(&this->dstTFInv); + src-> transferFn(&this->fSrcTF ); + dst->invTransferFn(&this->fDstTFInv); // If we linearize then immediately reencode with the same transfer function, skip both. - if ( this->flags.linearize && - !this->flags.gamut_transform && - this->flags.encode && + if ( this->fFlags.linearize && + !this->fFlags.gamut_transform && + this->fFlags.encode && src->transferFnHash() == dst->transferFnHash()) { #ifdef SK_DEBUG skcms_TransferFunction dstTF; dst->transferFn(&dstTF); for (int i = 0; i < 7; i++) { - SkASSERT( (&srcTF.g)[i] == (&dstTF.g)[i] && "Hash collision" ); + SkASSERT( (&fSrcTF.g)[i] == (&dstTF.g)[i] && "Hash collision" ); } #endif - this->flags.linearize = false; - this->flags.encode = false; + this->fFlags.linearize = false; + this->fFlags.encode = false; } // Skip unpremul...premul if there are no non-linear operations between. - if ( this->flags.unpremul && - !this->flags.linearize && - !this->flags.encode && - this->flags.premul) + if ( this->fFlags.unpremul && + !this->fFlags.linearize && + !this->fFlags.encode && + this->fFlags.premul) { - this->flags.unpremul = false; - this->flags.premul = false; + this->fFlags.unpremul = false; + this->fFlags.premul = false; } } void SkColorSpaceXformSteps::apply(float* rgba) const { - if (flags.unpremul) { + if (this->fFlags.unpremul) { // I don't know why isfinite(x) stopped working on the Chromecast bots... auto is_finite = [](float x) { return x*0 == 0; }; @@ -111,25 +111,25 @@ void SkColorSpaceXformSteps::apply(float* rgba) const { rgba[1] *= invA; rgba[2] *= invA; } - if (flags.linearize) { - rgba[0] = skcms_TransferFunction_eval(&srcTF, rgba[0]); - rgba[1] = skcms_TransferFunction_eval(&srcTF, rgba[1]); - rgba[2] = skcms_TransferFunction_eval(&srcTF, rgba[2]); + if (this->fFlags.linearize) { + rgba[0] = skcms_TransferFunction_eval(&fSrcTF, rgba[0]); + rgba[1] = skcms_TransferFunction_eval(&fSrcTF, rgba[1]); + rgba[2] = skcms_TransferFunction_eval(&fSrcTF, rgba[2]); } - if (flags.gamut_transform) { + if (this->fFlags.gamut_transform) { float temp[3] = { rgba[0], rgba[1], rgba[2] }; for (int i = 0; i < 3; ++i) { - rgba[i] = src_to_dst_matrix[ i] * temp[0] + - src_to_dst_matrix[3 + i] * temp[1] + - src_to_dst_matrix[6 + i] * temp[2]; + rgba[i] = fSrcToDstMatrix[ i] * temp[0] + + fSrcToDstMatrix[3 + i] * temp[1] + + fSrcToDstMatrix[6 + i] * temp[2]; } } - if (flags.encode) { - rgba[0] = skcms_TransferFunction_eval(&dstTFInv, rgba[0]); - rgba[1] = skcms_TransferFunction_eval(&dstTFInv, rgba[1]); - rgba[2] = skcms_TransferFunction_eval(&dstTFInv, rgba[2]); + if (this->fFlags.encode) { + rgba[0] = skcms_TransferFunction_eval(&fDstTFInv, rgba[0]); + rgba[1] = skcms_TransferFunction_eval(&fDstTFInv, rgba[1]); + rgba[2] = skcms_TransferFunction_eval(&fDstTFInv, rgba[2]); } - if (flags.premul) { + if (this->fFlags.premul) { rgba[0] *= rgba[3]; rgba[1] *= rgba[3]; rgba[2] *= rgba[3]; @@ -137,9 +137,9 @@ void SkColorSpaceXformSteps::apply(float* rgba) const { } void SkColorSpaceXformSteps::apply(SkRasterPipeline* p) const { - if (flags.unpremul) { p->append(SkRasterPipelineOp::unpremul); } - if (flags.linearize) { p->appendTransferFunction(srcTF); } - if (flags.gamut_transform) { p->append(SkRasterPipelineOp::matrix_3x3, &src_to_dst_matrix); } - if (flags.encode) { p->appendTransferFunction(dstTFInv); } - if (flags.premul) { p->append(SkRasterPipelineOp::premul); } + if (this->fFlags.unpremul) { p->append(SkRasterPipelineOp::unpremul); } + if (this->fFlags.linearize) { p->appendTransferFunction(fSrcTF); } + if (this->fFlags.gamut_transform) { p->append(SkRasterPipelineOp::matrix_3x3, &fSrcToDstMatrix); } + if (this->fFlags.encode) { p->appendTransferFunction(fDstTFInv); } + if (this->fFlags.premul) { p->append(SkRasterPipelineOp::premul); } } diff --git a/gfx/skia/skia/src/core/SkColorSpaceXformSteps.h b/gfx/skia/skia/src/core/SkColorSpaceXformSteps.h index 7ee8061b3b3c..2b7f747f5699 100644 --- a/gfx/skia/skia/src/core/SkColorSpaceXformSteps.h +++ b/gfx/skia/skia/src/core/SkColorSpaceXformSteps.h @@ -46,11 +46,11 @@ struct SkColorSpaceXformSteps { void apply(float rgba[4]) const; void apply(SkRasterPipeline*) const; - Flags flags; + Flags fFlags; - skcms_TransferFunction srcTF, // Apply for linearize. - dstTFInv; // Apply for encode. - float src_to_dst_matrix[9]; // Apply this 3x3 column-major matrix for gamut_transform. + skcms_TransferFunction fSrcTF, // Apply for linearize. + fDstTFInv; // Apply for encode. + float fSrcToDstMatrix[9]; // Apply this 3x3 *column*-major matrix for gamut_transform. }; #endif//SkColorSpaceXformSteps_DEFINED diff --git a/gfx/skia/skia/src/core/SkCompressedDataUtils.cpp b/gfx/skia/skia/src/core/SkCompressedDataUtils.cpp index 184dadd1fb73..a131536f4c98 100644 --- a/gfx/skia/skia/src/core/SkCompressedDataUtils.cpp +++ b/gfx/skia/skia/src/core/SkCompressedDataUtils.cpp @@ -9,14 +9,14 @@ #include "include/core/SkBitmap.h" #include "include/core/SkColor.h" -#include "include/core/SkColorPriv.h" #include "include/core/SkData.h" #include "include/core/SkScalar.h" #include "include/core/SkSize.h" -#include "include/private/SkColorData.h" #include "include/private/base/SkTPin.h" #include "include/private/base/SkTo.h" #include "src/base/SkMathPriv.h" +#include "src/core/SkColorData.h" +#include "src/core/SkColorPriv.h" #include "src/core/SkMipmap.h" #include diff --git a/gfx/skia/skia/src/core/SkConvertPixels.cpp b/gfx/skia/skia/src/core/SkConvertPixels.cpp index 69f4b997a471..78ffaa1d89e6 100644 --- a/gfx/skia/skia/src/core/SkConvertPixels.cpp +++ b/gfx/skia/skia/src/core/SkConvertPixels.cpp @@ -9,12 +9,12 @@ #include "include/core/SkColorType.h" #include "include/core/SkImageInfo.h" #include "include/core/SkSize.h" -#include "include/private/SkColorData.h" #include "include/private/base/SkAssert.h" #include "include/private/base/SkTPin.h" #include "include/private/base/SkTemplates.h" #include "src/base/SkHalf.h" #include "src/base/SkRectMemcpy.h" +#include "src/core/SkColorData.h" #include "src/core/SkColorSpaceXformSteps.h" #include "src/core/SkImageInfoPriv.h" #include "src/core/SkRasterPipeline.h" @@ -33,7 +33,7 @@ static bool rect_memcpy(const SkImageInfo& dstInfo, void* dstPixels, size_ return false; } if (dstInfo.colorType() != kAlpha_8_SkColorType - && steps.flags.mask() != 0b00000) { + && steps.fFlags.mask() != 0b00000) { return false; } @@ -50,12 +50,12 @@ static bool swizzle_or_premul(const SkImageInfo& dstInfo, void* dstPixels, }; if (!is_8888(dstInfo.colorType()) || !is_8888(srcInfo.colorType()) || - steps.flags.linearize || - steps.flags.gamut_transform || + steps.fFlags.linearize || + steps.fFlags.gamut_transform || #if !defined(SK_ARM_HAS_NEON) - steps.flags.unpremul || + steps.fFlags.unpremul || #endif - steps.flags.encode) { + steps.fFlags.encode) { return false; } @@ -63,10 +63,10 @@ static bool swizzle_or_premul(const SkImageInfo& dstInfo, void* dstPixels, void (*fn)(uint32_t*, const uint32_t*, int) = nullptr; - if (steps.flags.premul) { + if (steps.fFlags.premul) { fn = swapRB ? SkOpts::RGBA_to_bgrA : SkOpts::RGBA_to_rgbA; - } else if (steps.flags.unpremul) { + } else if (steps.fFlags.unpremul) { fn = swapRB ? SkOpts::rgbA_to_BGRA : SkOpts::rgbA_to_RGBA; } else { @@ -247,8 +247,8 @@ static bool convert_to_alpha8(const SkImageInfo& dstInfo, void* vdst, size static void convert_with_pipeline(const SkImageInfo& dstInfo, void* dstRow, int dstStride, const SkImageInfo& srcInfo, const void* srcRow, int srcStride, const SkColorSpaceXformSteps& steps) { - SkRasterPipeline_MemoryCtx src = { const_cast(srcRow), srcStride }, - dst = { dstRow, dstStride }; + SkRasterPipelineContexts::MemoryCtx src = {const_cast(srcRow), srcStride}, + dst = {dstRow, dstStride}; SkRasterPipeline_<256> pipeline; pipeline.appendLoad(srcInfo.colorType(), &src); diff --git a/gfx/skia/skia/src/core/SkCoreBlitters.h b/gfx/skia/skia/src/core/SkCoreBlitters.h index f00e76eceea5..5fdb79be1031 100644 --- a/gfx/skia/skia/src/core/SkCoreBlitters.h +++ b/gfx/skia/skia/src/core/SkCoreBlitters.h @@ -34,9 +34,6 @@ public: protected: const SkPixmap fDevice; - -private: - using INHERITED = SkBlitter; }; class SkShaderBlitter : public SkRasterBlitter { @@ -52,13 +49,7 @@ public: protected: sk_sp fShader; - SkShaderBase::Context* fShaderContext; - -private: - // illegal - SkShaderBlitter& operator=(const SkShaderBlitter&); - - using INHERITED = SkRasterBlitter; + SkShaderBase::Context* fShaderContext; }; /////////////////////////////////////////////////////////////////////////////// @@ -75,40 +66,32 @@ public: void blitAntiV2(int x, int y, U8CPU a0, U8CPU a1) override; protected: - SkColor fColor; - SkPMColor fPMColor; - -private: - unsigned fSrcA, fSrcR, fSrcG, fSrcB; - - // illegal - SkARGB32_Blitter& operator=(const SkARGB32_Blitter&); - - using INHERITED = SkRasterBlitter; + SkColor fColor; + SkPMColor fPMColor; + SkAlpha fSrcA; }; class SkARGB32_Opaque_Blitter : public SkARGB32_Blitter { public: SkARGB32_Opaque_Blitter(const SkPixmap& device, const SkPaint& paint) - : INHERITED(device, paint) { SkASSERT(paint.getAlpha() == 0xFF); } + : SkARGB32_Blitter(device, paint) { + SkASSERT(paint.getAlpha() == 0xFF); + } void blitMask(const SkMask&, const SkIRect&) override; + void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) override; void blitAntiH2(int x, int y, U8CPU a0, U8CPU a1) override; void blitAntiV2(int x, int y, U8CPU a0, U8CPU a1) override; - -private: - using INHERITED = SkARGB32_Blitter; }; class SkARGB32_Black_Blitter : public SkARGB32_Opaque_Blitter { public: SkARGB32_Black_Blitter(const SkPixmap& device, const SkPaint& paint) - : INHERITED(device, paint) {} + : SkARGB32_Opaque_Blitter(device, paint) { + SkASSERT(paint.getColor() == SK_ColorBLACK); + } void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) override; void blitAntiH2(int x, int y, U8CPU a0, U8CPU a1) override; void blitAntiV2(int x, int y, U8CPU a0, U8CPU a1) override; - -private: - using INHERITED = SkARGB32_Opaque_Blitter; }; class SkARGB32_Shader_Blitter : public SkShaderBlitter { @@ -126,12 +109,7 @@ private: SkPMColor* fBuffer; SkBlitRow::Proc32 fProc32; SkBlitRow::Proc32 fProc32Blend; - bool fShadeDirectlyIntoDevice; - - // illegal - SkARGB32_Shader_Blitter& operator=(const SkARGB32_Shader_Blitter&); - - using INHERITED = SkShaderBlitter; + bool fShadeDirectlyIntoDevice; }; /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/gfx/skia/skia/src/core/SkDebugUtils.h b/gfx/skia/skia/src/core/SkDebugUtils.h index 9333e837d12d..a32b2e48bfbb 100644 --- a/gfx/skia/skia/src/core/SkDebugUtils.h +++ b/gfx/skia/skia/src/core/SkDebugUtils.h @@ -9,6 +9,11 @@ #define SkDebugUtils_DEFINED #include "include/core/SkTileMode.h" +#include "include/private/base/SkAssert.h" +#include "include/private/base/SkDebug.h" + +#include +#include static constexpr const char* SkTileModeToStr(SkTileMode tm) { switch (tm) { @@ -20,4 +25,31 @@ static constexpr const char* SkTileModeToStr(SkTileMode tm) { SkUNREACHABLE; } +#if defined(SK_DEBUG) +inline void SkDumpBuffer(uint8_t const* const buffer, int w, int h, int rowBytes, + bool dumpActualValues = false) { + SkASSERT(buffer); + + static constexpr char shades[] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { + uint8_t pixelValue = buffer[y * rowBytes + x]; + if (dumpActualValues) { + SkDebugf("%u\t", pixelValue); + } else { + int idx = static_cast(pixelValue * std::size(shades) / 256); + SkASSERT(idx >= 0 && idx < (int)std::size(shades)); + SkDebugf("%c", shades[idx]); + } + } + SkDebugf("\n"); + } +} +#else +inline void SkDumpBuffer(uint8_t*, int, int, int) {} +#endif + + #endif // SkDebugUtils_DEFINED diff --git a/gfx/skia/skia/src/core/SkDevice.cpp b/gfx/skia/skia/src/core/SkDevice.cpp index 0148dbda9bc2..56727c545a5f 100644 --- a/gfx/skia/skia/src/core/SkDevice.cpp +++ b/gfx/skia/skia/src/core/SkDevice.cpp @@ -8,7 +8,6 @@ #include "src/core/SkDevice.h" #include "include/core/SkAlphaType.h" -#include "include/core/SkColorPriv.h" #include "include/core/SkColorSpace.h" #include "include/core/SkColorType.h" #include "include/core/SkDrawable.h" @@ -27,6 +26,7 @@ #include "include/core/SkVertices.h" #include "include/private/base/SkFloatingPoint.h" #include "include/private/chromium/Slug.h" // IWYU pragma: keep +#include "src/core/SkColorPriv.h" #include "src/core/SkEnumerate.h" #include "src/core/SkImageFilterTypes.h" #include "src/core/SkImageFilter_Base.h" @@ -100,10 +100,10 @@ SkIPoint SkDevice::getOrigin() const { SkScalarFloorToInt(fDeviceToGlobal.rc(1, 3))); } -SkMatrix SkDevice::getRelativeTransform(const SkDevice& dstDevice) const { +SkM44 SkDevice::getRelativeTransform(const SkDevice& dstDevice) const { // To get the transform from this space to the other device's, transform from our space to // global and then from global to the other device. - return (dstDevice.fGlobalToDevice * fDeviceToGlobal).asM33(); + return dstDevice.fGlobalToDevice * fDeviceToGlobal; } static inline bool is_int(float x) { @@ -135,7 +135,7 @@ void SkDevice::drawArc(const SkArc& arc, const SkPaint& paint) { SkPath path; bool isFillNoPathEffect = SkPaint::kFill_Style == paint.getStyle() && !paint.getPathEffect(); SkPathPriv::CreateDrawArcPath(&path, arc, isFillNoPathEffect); - this->drawPath(path, paint); + this->drawPath(path, paint, true); } void SkDevice::drawDRRect(const SkRRect& outer, @@ -244,7 +244,7 @@ void SkDevice::drawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], SkCanvas:: // Draw the clip directly as a quad since it's a filled color with no local coords SkPath clipPath; clipPath.addPoly(clip, 4, true); - this->drawPath(clipPath, paint); + this->drawPath(clipPath, paint, true); } else { this->drawRect(r, paint); } @@ -310,8 +310,6 @@ void SkDevice::drawCoverageMask(const SkSpecialImage*, const SkMatrix& maskToDev SK_ABORT("Must override if useDrawCoverageMaskForMaskFilters() is true"); } -sk_sp SkDevice::makeSpecial(const SkBitmap&) { return nullptr; } -sk_sp SkDevice::makeSpecial(const SkImage*) { return nullptr; } sk_sp SkDevice::snapSpecial(const SkIRect&, bool forceCopy) { return nullptr; } sk_sp SkDevice::snapSpecialScaled(const SkIRect& subset, const SkISize& dstDims) { @@ -333,7 +331,7 @@ void SkDevice::drawDevice(SkDevice* device, if (deviceImage) { // SkCanvas only calls drawDevice() when there are no filters (so the transform is pixel // aligned). As such it can be drawn without clamping. - SkMatrix relativeTransform = device->getRelativeTransform(*this); + SkMatrix relativeTransform = device->getRelativeTransform(*this).asM33(); const bool strict = sampling != SkFilterMode::kNearest || !relativeTransform.isTranslate() || !SkScalarIsInt(relativeTransform.getTranslateX()) || @@ -372,7 +370,7 @@ void SkDevice::drawFilteredImage(const skif::Mapping& mapping, sk_sp result = as_IFB(filter)->filterImage(ctx).imageAndOffset(ctx, &offset); stats.reportStats(); if (result) { - SkMatrix deviceMatrixWithOffset = mapping.layerToDevice(); + SkMatrix deviceMatrixWithOffset = mapping.layerToDevice().asM33(); deviceMatrixWithOffset.preTranslate(offset.fX, offset.fY); this->drawSpecial(result.get(), deviceMatrixWithOffset, sampling, paint); } diff --git a/gfx/skia/skia/src/core/SkDevice.h b/gfx/skia/skia/src/core/SkDevice.h index 9f0ba6d25eab..cb5356e0f887 100644 --- a/gfx/skia/skia/src/core/SkDevice.h +++ b/gfx/skia/skia/src/core/SkDevice.h @@ -34,7 +34,6 @@ #include struct SkArc; -class SkBitmap; class SkColorSpace; class SkMesh; struct SkDrawShadowRec; @@ -206,7 +205,7 @@ public: * that device is drawn to the root device, the net effect will be that this device's contents * have been transformed by the global CTM. */ - SkMatrix getRelativeTransform(const SkDevice&) const; + SkM44 getRelativeTransform(const SkDevice&) const; void setLocalToDevice(const SkM44& localToDevice) { fLocalToDevice = localToDevice; @@ -358,7 +357,7 @@ public: */ virtual void drawPath(const SkPath& path, const SkPaint& paint, - bool pathIsMutable = false) = 0; + bool pathIsMutable) = 0; virtual void drawImageRect(const SkImage*, const SkRect* src, const SkRect& dst, const SkSamplingOptions&, const SkPaint&, @@ -485,11 +484,6 @@ public: const SkImageFilter*, const SkSamplingOptions&, const SkPaint&); protected: - // DEPRECATED: Can be deleted once SkCanvas::onDrawImage() uses skif::FilterResult so don't - // bother re-arranging. - virtual sk_sp makeSpecial(const SkBitmap&); - virtual sk_sp makeSpecial(const SkImage*); - // Configure the device's coordinate spaces, specifying both how its device image maps back to // the global space (via 'deviceToGlobal') and the initial CTM of the device (via // 'localToDevice', i.e. what geometry drawn into this device will be transformed with). @@ -643,11 +637,11 @@ private: class SkAutoDeviceTransformRestore : SkNoncopyable { public: - SkAutoDeviceTransformRestore(SkDevice* device, const SkMatrix& localToDevice) + SkAutoDeviceTransformRestore(SkDevice* device, const SkM44& localToDevice) : fDevice(device) , fPrevLocalToDevice(device->localToDevice()) { - fDevice->setLocalToDevice(SkM44(localToDevice)); + fDevice->setLocalToDevice(localToDevice); } ~SkAutoDeviceTransformRestore() { fDevice->setLocalToDevice(fPrevLocalToDevice); diff --git a/gfx/skia/skia/src/core/SkDraw.cpp b/gfx/skia/skia/src/core/SkDraw.cpp index 78ba942eed04..fb6f5b52a6f8 100644 --- a/gfx/skia/skia/src/core/SkDraw.cpp +++ b/gfx/skia/skia/src/core/SkDraw.cpp @@ -5,6 +5,8 @@ * found in the LICENSE file. */ +#include "src/core/SkDraw.h" + #include "include/core/SkBitmap.h" #include "include/core/SkColorType.h" #include "include/core/SkMatrix.h" @@ -25,7 +27,7 @@ #include "src/base/SkTLazy.h" #include "src/core/SkAutoBlitterChoose.h" #include "src/core/SkBlitter.h" -#include "src/core/SkDraw.h" +#include "src/core/SkDrawTypes.h" #include "src/core/SkImageInfoPriv.h" #include "src/core/SkImagePriv.h" #include "src/core/SkMatrixUtils.h" diff --git a/gfx/skia/skia/src/core/SkDrawBase.cpp b/gfx/skia/skia/src/core/SkDrawBase.cpp index 4dce30d6166c..e674536316e8 100644 --- a/gfx/skia/skia/src/core/SkDrawBase.cpp +++ b/gfx/skia/skia/src/core/SkDrawBase.cpp @@ -37,6 +37,7 @@ #include "src/core/SkRasterClip.h" #include "src/core/SkRectPriv.h" #include "src/core/SkScan.h" + #include #include #include @@ -322,8 +323,11 @@ DRAW_PATH: this->drawPath(path, paint, nullptr, true); } -void SkDrawBase::drawDevPath(const SkPath& devPath, const SkPaint& paint, bool drawCoverage, - SkBlitter* customBlitter, bool doFill) const { +void SkDrawBase::drawDevPath(const SkPath& devPath, + const SkPaint& paint, + SkDrawCoverage drawCoverage, + SkBlitter* customBlitter, + bool doFill) const { if (SkPathPriv::TooBigForMath(devPath)) { return; } @@ -381,9 +385,12 @@ void SkDrawBase::drawDevPath(const SkPath& devPath, const SkPaint& paint, bool d proc(devPath, *fRC, blitter); } -void SkDrawBase::drawPath(const SkPath& origSrcPath, const SkPaint& origPaint, - const SkMatrix* prePathMatrix, bool pathIsMutable, - bool drawCoverage, SkBlitter* customBlitter) const { +void SkDrawBase::drawPath(const SkPath& origSrcPath, + const SkPaint& origPaint, + const SkMatrix* prePathMatrix, + bool pathIsMutable, + SkDrawCoverage drawCoverage, + SkBlitter* customBlitter) const { SkDEBUGCODE(this->validate();) // nothing to draw @@ -491,21 +498,19 @@ void SkDrawBase::validate() const { //////////////////////////////////////////////////////////////////////////////////////////////// -bool SkDrawBase::ComputeMaskBounds(const SkRect& devPathBounds, const SkIRect& clipBounds, - const SkMaskFilter* filter, const SkMatrix* filterMatrix, - SkIRect* bounds) { +static bool compute_mask_bounds(const SkRect& devPathBounds, const SkIRect& clipBounds, + const SkMaskFilter* filter, const SkMatrix* filterMatrix, + SkIRect* bounds) { + SkASSERT(filter); + SkASSERT(filterMatrix); // init our bounds from the path *bounds = devPathBounds.makeOutset(SK_ScalarHalf, SK_ScalarHalf).roundOut(); - SkIPoint margin = SkIPoint::Make(0, 0); - if (filter) { - SkASSERT(filterMatrix); - - SkMask srcM(nullptr, *bounds, 0, SkMask::kA8_Format); - SkMaskBuilder dstM; - if (!as_MFB(filter)->filterMask(&dstM, srcM, *filterMatrix, &margin)) { - return false; - } + SkIVector margin = SkIPoint::Make(0, 0); + SkMask srcM(nullptr, *bounds, 0, SkMask::kA8_Format); + SkMaskBuilder dstM; + if (!as_MFB(filter)->filterMask(&dstM, srcM, *filterMatrix, &margin)) { + return false; } // trim the bounds to reflect the clip (plus whatever slop the filter needs) @@ -553,27 +558,29 @@ static void draw_into_mask(const SkMask& mask, const SkPath& devPath, break; } - draw.drawPath(devPath, paint); + draw.drawPath(devPath, paint, nullptr, false); } bool SkDrawBase::DrawToMask(const SkPath& devPath, const SkIRect& clipBounds, const SkMaskFilter* filter, const SkMatrix* filterMatrix, SkMaskBuilder* dst, SkMaskBuilder::CreateMode mode, SkStrokeRec::InitStyle style) { + SkASSERT(filter); if (devPath.isEmpty()) { return false; } if (SkMaskBuilder::kJustRenderImage_CreateMode != mode) { - // By using infinite bounds for inverse fills, ComputeMaskBounds is able to clip it to + // By using infinite bounds for inverse fills, compute_mask_bounds is able to clip it to // 'clipBounds' outset by whatever extra margin the mask filter requires. static const SkRect kInverseBounds = { SK_ScalarNegativeInfinity, SK_ScalarNegativeInfinity, SK_ScalarInfinity, SK_ScalarInfinity}; SkRect pathBounds = devPath.isInverseFillType() ? kInverseBounds : devPath.getBounds(); - if (!ComputeMaskBounds(pathBounds, clipBounds, filter, - filterMatrix, &dst->bounds())) + if (!compute_mask_bounds(pathBounds, clipBounds, filter, + filterMatrix, &dst->bounds())) { return false; + } } if (SkMaskBuilder::kComputeBoundsAndRenderImage_CreateMode == mode) { @@ -590,7 +597,6 @@ bool SkDrawBase::DrawToMask(const SkPath& devPath, const SkIRect& clipBounds, if (SkMaskBuilder::kJustComputeBounds_CreateMode != mode) { draw_into_mask(*dst, devPath, style); } - return true; } @@ -682,17 +688,17 @@ void SkDrawBase::drawDevicePoints(SkCanvas::PointMode mode, size_t count, if (!pointData.fFirst.isEmpty()) { if (device) { - device->drawPath(pointData.fFirst, newP); + device->drawPath(pointData.fFirst, newP, true); } else { - this->drawPath(pointData.fFirst, newP); + this->drawPath(pointData.fFirst, newP, nullptr, true); } } if (!pointData.fLast.isEmpty()) { if (device) { - device->drawPath(pointData.fLast, newP); + device->drawPath(pointData.fLast, newP, true); } else { - this->drawPath(pointData.fLast, newP); + this->drawPath(pointData.fLast, newP, nullptr, true); } } diff --git a/gfx/skia/skia/src/core/SkDrawBase.h b/gfx/skia/skia/src/core/SkDrawBase.h index 69b7f900b049..f616f8c2f364 100644 --- a/gfx/skia/skia/src/core/SkDrawBase.h +++ b/gfx/skia/skia/src/core/SkDrawBase.h @@ -16,6 +16,7 @@ #include "include/core/SkStrokeRec.h" #include "include/private/base/SkDebug.h" #include "src/base/SkZip.h" +#include "src/core/SkDrawTypes.h" #include "src/core/SkGlyphRunPainter.h" #include "src/core/SkMask.h" @@ -41,13 +42,13 @@ class SkDrawBase : public SkGlyphRunListPainterCPU::BitmapDevicePainter { public: SkDrawBase(); - void drawPaint(const SkPaint&) const; - void drawRect(const SkRect& prePaintRect, const SkPaint&, const SkMatrix* paintMatrix, + void drawPaint(const SkPaint&) const; + void drawRect(const SkRect& prePaintRect, const SkPaint&, const SkMatrix* paintMatrix, const SkRect* postPaintRect) const; - void drawRect(const SkRect& rect, const SkPaint& paint) const { + void drawRect(const SkRect& rect, const SkPaint& paint) const { this->drawRect(rect, paint, nullptr, nullptr); } - void drawRRect(const SkRRect&, const SkPaint&) const; + void drawRRect(const SkRRect&, const SkPaint&) const; /** * To save on mallocs, we allow a flag that tells us that srcPath is * mutable, so that we don't have to make copies of it as we transform it. @@ -57,9 +58,9 @@ public: * affect the geometry/rasterization, then the pre matrix can just be * pre-concated with the current matrix. */ - void drawPath(const SkPath& path, const SkPaint& paint, - const SkMatrix* prePathMatrix = nullptr, bool pathIsMutable = false) const { - this->drawPath(path, paint, prePathMatrix, pathIsMutable, false); + void drawPath(const SkPath& path, const SkPaint& paint, + const SkMatrix* prePathMatrix, bool pathIsMutable) const { + this->drawPath(path, paint, prePathMatrix, pathIsMutable, SkDrawCoverage::kNo); } /** @@ -72,17 +73,18 @@ public: SkBlitter* customBlitter = nullptr) const { bool isHairline = paint.getStyle() == SkPaint::kStroke_Style && paint.getStrokeWidth() == 0; - this->drawPath(src, paint, nullptr, false, !isHairline, customBlitter); + this->drawPath(src, + paint, + nullptr, + false, + isHairline ? SkDrawCoverage::kNo : SkDrawCoverage::kYes, + customBlitter); } void drawDevicePoints(SkCanvas::PointMode, size_t count, const SkPoint[], const SkPaint&, SkDevice*) const; - static bool ComputeMaskBounds(const SkRect& devPathBounds, const SkIRect& clipBounds, - const SkMaskFilter* filter, const SkMatrix* filterMatrix, - SkIRect* bounds); - - /** Helper function that creates a mask from a path and an optional maskfilter. + /** Helper function that creates a mask from a path and a required maskfilter. Note however, that the resulting mask will not have been actually filtered, that must be done afterwards (by calling filterMask). The maskfilter is provided solely to assist in computing the mask's bounds (if the mode requests that). @@ -110,14 +112,13 @@ public: static RectType ComputeRectType(const SkRect&, const SkPaint&, const SkMatrix&, SkPoint* strokeSize); - using BlitterChooser = SkBlitter* (const SkPixmap& dst, - const SkMatrix& ctm, - const SkPaint&, - SkArenaAlloc*, - bool drawCoverage, - sk_sp clipShader, - const SkSurfaceProps&); - + using BlitterChooser = SkBlitter*(const SkPixmap& dst, + const SkMatrix& ctm, + const SkPaint&, + SkArenaAlloc*, + SkDrawCoverage drawCoverage, + sk_sp clipShader, + const SkSurfaceProps&); private: // not supported @@ -129,14 +130,14 @@ private: const SkPaint&, const SkMatrix* preMatrix, bool pathIsMutable, - bool drawCoverage, + SkDrawCoverage drawCoverage, SkBlitter* customBlitter = nullptr) const; void drawLine(const SkPoint[2], const SkPaint&) const; void drawDevPath(const SkPath& devPath, const SkPaint& paint, - bool drawCoverage, + SkDrawCoverage drawCoverage, SkBlitter* customBlitter, bool doFill) const; /** diff --git a/gfx/skia/skia/src/core/SkDrawTypes.h b/gfx/skia/skia/src/core/SkDrawTypes.h new file mode 100644 index 000000000000..90d8f49197c0 --- /dev/null +++ b/gfx/skia/skia/src/core/SkDrawTypes.h @@ -0,0 +1,21 @@ +/* + * Copyright 2025 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkDrawTypes_DEFINED +#define SkDrawTypes_DEFINED + +#include + +enum class SkDrawCoverage : bool { + kNo = false, + kYes = true, +}; + +// A good size for creating shader contexts on the stack. +constexpr size_t kSkBlitterContextSize = 3332; + +#endif diff --git a/gfx/skia/skia/src/core/SkDraw_atlas.cpp b/gfx/skia/skia/src/core/SkDraw_atlas.cpp index ea0afb5d48c3..55aed310fe1f 100644 --- a/gfx/skia/skia/src/core/SkDraw_atlas.cpp +++ b/gfx/skia/skia/src/core/SkDraw_atlas.cpp @@ -60,7 +60,7 @@ static void fill_rect(const SkMatrix& ctm, const SkRasterClip& rc, } } -static void load_color(SkRasterPipeline_UniformColorCtx* ctx, const float rgba[]) { +static void load_color(SkRasterPipelineContexts::UniformColorCtx* ctx, const float rgba[]) { // only need one of these. can I query the pipeline to know if its lowp or highp? ctx->rgba[0] = SkScalarRoundToInt(rgba[0]*255); ctx->r = rgba[0]; ctx->rgba[1] = SkScalarRoundToInt(rgba[1]*255); ctx->g = rgba[1]; @@ -102,12 +102,12 @@ void SkDraw::drawAtlas(const SkRSXform xform[], return; } - SkRasterPipeline_UniformColorCtx* uniformCtx = nullptr; + SkRasterPipelineContexts::UniformColorCtx* uniformCtx = nullptr; SkColorSpaceXformSteps steps(sk_srgb_singleton(), kUnpremul_SkAlphaType, rec.fDstCS, kUnpremul_SkAlphaType); if (colors) { // we will late-bind the values in ctx, once for each color in the loop - uniformCtx = alloc.make(); + uniformCtx = alloc.make(); rec.fPipeline->append(SkRasterPipelineOp::uniform_color_dst, uniformCtx); std::optional bm = as_BB(blender)->asBlendMode(); if (!bm.has_value()) { diff --git a/gfx/skia/skia/src/core/SkDraw_text.cpp b/gfx/skia/skia/src/core/SkDraw_text.cpp index 563b9165c415..cbd3fdad5911 100644 --- a/gfx/skia/skia/src/core/SkDraw_text.cpp +++ b/gfx/skia/skia/src/core/SkDraw_text.cpp @@ -17,6 +17,7 @@ #include "src/core/SkAAClip.h" #include "src/core/SkBlitter.h" #include "src/core/SkDraw.h" +#include "src/core/SkDrawTypes.h" #include "src/core/SkGlyph.h" #include "src/core/SkGlyphRunPainter.h" #include "src/core/SkMask.h" @@ -50,13 +51,12 @@ static bool check_glyph_position(SkPoint position) { } void SkDraw::paintMasks(SkZip accepted, const SkPaint& paint) const { - // The size used for a typical blitter. - SkSTArenaAlloc<3308> alloc; + SkSTArenaAlloc alloc; SkBlitter* blitter = SkBlitter::Choose(fDst, *fCTM, paint, &alloc, - false, + SkDrawCoverage::kNo, fRC->clipShader(), SkSurfacePropsCopyOrDefault(fProps)); diff --git a/gfx/skia/skia/src/core/SkDraw_vertices.cpp b/gfx/skia/skia/src/core/SkDraw_vertices.cpp index 132988fc92ce..e7bc78f3c7e5 100644 --- a/gfx/skia/skia/src/core/SkDraw_vertices.cpp +++ b/gfx/skia/skia/src/core/SkDraw_vertices.cpp @@ -24,11 +24,11 @@ #include "include/core/SkSurfaceProps.h" #include "include/core/SkTypes.h" #include "include/core/SkVertices.h" -#include "include/private/SkColorData.h" #include "include/private/base/SkFloatingPoint.h" #include "include/private/base/SkTo.h" #include "src/base/SkArenaAlloc.h" #include "src/core/SkBlenderBase.h" +#include "src/core/SkColorData.h" #include "src/core/SkConvertPixels.h" #include "src/core/SkCoreBlitters.h" #include "src/core/SkDraw.h" diff --git a/gfx/skia/skia/src/core/SkEdge.cpp b/gfx/skia/skia/src/core/SkEdge.cpp index 2bf31cc5dbb0..8fbe92086384 100644 --- a/gfx/skia/skia/src/core/SkEdge.cpp +++ b/gfx/skia/skia/src/core/SkEdge.cpp @@ -8,6 +8,7 @@ #include "src/core/SkEdge.h" #include "include/private/base/SkDebug.h" +#include "include/private/base/SkSafe32.h" #include "include/private/base/SkTo.h" #include "src/base/SkMathPriv.h" #include "src/core/SkFDot6.h" @@ -50,7 +51,7 @@ void SkEdge::dump() const { SkFixedToFloat(realLastY), SkFixedToFloat(fX), SkFixedToFloat(fDX), - fWinding); + static_cast(fWinding)); } #endif @@ -72,13 +73,12 @@ int SkEdge::setLine(const SkPoint& p0, const SkPoint& p1, const SkIRect* clip, i #endif } - int winding = 1; - + Winding winding = Winding::kCW; if (y0 > y1) { using std::swap; swap(x0, x1); swap(y0, y1); - winding = -1; + winding = Winding::kCCW; } int top = SkFDot6Round(y0); @@ -100,9 +100,9 @@ int SkEdge::setLine(const SkPoint& p0, const SkPoint& p1, const SkIRect* clip, i fDX = slope; fFirstY = top; fLastY = bot - 1; - fEdgeType = kLine_Type; + fEdgeType = Type::kLine; fCurveCount = 0; - fWinding = SkToS8(winding); + fWinding = winding; fCurveShift = 0; if (clip) { @@ -114,7 +114,7 @@ int SkEdge::setLine(const SkPoint& p0, const SkPoint& p1, const SkIRect* clip, i // called from a curve subclass int SkEdge::updateLine(SkFixed x0, SkFixed y0, SkFixed x1, SkFixed y1) { - SkASSERT(fWinding == 1 || fWinding == -1); + SkASSERT(fWinding == Winding::kCW || fWinding == Winding::kCCW); SkASSERT(fCurveCount != 0); // SkASSERT(fCurveShift != 0); @@ -221,13 +221,13 @@ bool SkQuadraticEdge::setQuadraticWithoutUpdate(const SkPoint pts[3], int shift) #endif } - int winding = 1; + Winding winding = Winding::kCW; if (y0 > y2) { using std::swap; swap(x0, x2); swap(y0, y2); - winding = -1; + winding = Winding::kCCW; } SkASSERT(y0 <= y1 && y1 <= y2); @@ -255,9 +255,9 @@ bool SkQuadraticEdge::setQuadraticWithoutUpdate(const SkPoint pts[3], int shift) shift = MAX_COEFF_SHIFT; } - fWinding = SkToS8(winding); + fWinding = winding; //fCubicDShift only set for cubics - fEdgeType = kQuad_Type; + fEdgeType = Type::kQuad; fCurveCount = SkToS8(1 << shift); /* @@ -396,7 +396,7 @@ bool SkCubicEdge::setCubicWithoutUpdate(const SkPoint pts[4], int shift, bool so #endif } - int winding = 1; + Winding winding = Winding::kCW; if (sortY && y0 > y3) { using std::swap; @@ -404,7 +404,7 @@ bool SkCubicEdge::setCubicWithoutUpdate(const SkPoint pts[4], int shift, bool so swap(x1, x2); swap(y0, y3); swap(y1, y2); - winding = -1; + winding = Winding::kCCW; } int top = SkFDot6Round(y0); @@ -441,8 +441,8 @@ bool SkCubicEdge::setCubicWithoutUpdate(const SkPoint pts[4], int shift, bool so upShift = 10 - shift; } - fWinding = SkToS8(winding); - fEdgeType = kCubic_Type; + fWinding = winding; + fEdgeType = Type::kCubic; fCurveCount = SkToS8(SkLeftShift(-1, shift)); fCurveShift = SkToU8(shift); fCubicDShift = SkToU8(downShift); diff --git a/gfx/skia/skia/src/core/SkEdge.h b/gfx/skia/skia/src/core/SkEdge.h index 64fc3f743f2f..7aa810339b86 100644 --- a/gfx/skia/skia/src/core/SkEdge.h +++ b/gfx/skia/skia/src/core/SkEdge.h @@ -14,8 +14,6 @@ #include "include/private/base/SkDebug.h" #include "include/private/base/SkFixed.h" #include "include/private/base/SkMath.h" -#include "include/private/base/SkSafe32.h" -#include "include/private/base/SkTo.h" #include "src/core/SkFDot6.h" #include @@ -25,10 +23,14 @@ #define SkEdge_Compute_DY(top, y0) (SkLeftShift(top, 6) + 32 - (y0)) struct SkEdge { - enum Type { - kLine_Type, - kQuad_Type, - kCubic_Type + enum class Type : int8_t { + kLine, + kQuad, + kCubic, + }; + enum class Winding : int8_t { + kCW = 1, // clockwise + kCCW = -1, // counter clockwise }; SkEdge* fNext; @@ -42,7 +44,7 @@ struct SkEdge { int8_t fCurveCount; // only used by kQuad(+) and kCubic(-) uint8_t fCurveShift; // appled to all Dx/DDx/DDDx except for fCubicDShift exception uint8_t fCubicDShift; // applied to fCDx and fCDy only in cubic - int8_t fWinding; // 1 or -1 + Winding fWinding; int setLine(const SkPoint& p0, const SkPoint& p1, const SkIRect* clip, int shiftUp); // call this version if you know you don't have a clip @@ -63,7 +65,7 @@ struct SkEdge { SkASSERT(fNext->fPrev == this); SkASSERT(fFirstY <= fLastY); - SkASSERT(SkAbs32(fWinding) == 1); + SkASSERT(fWinding == Winding::kCW || fWinding == Winding::kCCW); } #endif }; @@ -109,13 +111,13 @@ int SkEdge::setLine(const SkPoint& p0, const SkPoint& p1, int shift) { #endif } - int winding = 1; + Winding winding = Winding::kCW; if (y0 > y1) { using std::swap; swap(x0, x1); swap(y0, y1); - winding = -1; + winding = Winding::kCCW; } int top = SkFDot6Round(y0); @@ -133,9 +135,9 @@ int SkEdge::setLine(const SkPoint& p0, const SkPoint& p1, int shift) { fDX = slope; fFirstY = top; fLastY = bot - 1; - fEdgeType = kLine_Type; + fEdgeType = Type::kLine; fCurveCount = 0; - fWinding = SkToS8(winding); + fWinding = winding; fCurveShift = 0; return 1; } diff --git a/gfx/skia/skia/src/core/SkEdgeBuilder.cpp b/gfx/skia/skia/src/core/SkEdgeBuilder.cpp index 0be077b88a64..bab4eb456065 100644 --- a/gfx/skia/skia/src/core/SkEdgeBuilder.cpp +++ b/gfx/skia/skia/src/core/SkEdgeBuilder.cpp @@ -27,7 +27,7 @@ SkEdgeBuilder::Combine SkBasicEdgeBuilder::combineVertical(const SkEdge* edge, SkEdge* last) { // We only consider edges that were originally lines to be vertical to avoid numerical issues // (crbug.com/1154864). - if (last->fEdgeType != SkEdge::kLine_Type || last->fDX || edge->fX != last->fX) { + if (last->fEdgeType != SkEdge::Type::kLine || last->fDX || edge->fX != last->fX) { return kNo_Combine; } if (edge->fWinding == last->fWinding) { @@ -75,7 +75,7 @@ SkEdgeBuilder::Combine SkAnalyticEdgeBuilder::combineVertical(const SkAnalyticEd // We only consider edges that were originally lines to be vertical to avoid numerical issues // (crbug.com/1154864). - if (last->fEdgeType != SkAnalyticEdge::kLine_Type || last->fDX || edge->fX != last->fX) { + if (last->fEdgeType != SkAnalyticEdge::Type::kLine || last->fDX || edge->fX != last->fX) { return kNo_Combine; } if (edge->fWinding == last->fWinding) { @@ -124,7 +124,7 @@ static bool is_vertical(const Edge* edge) { // We only consider edges that were originally lines to be vertical to avoid numerical issues // (crbug.com/1154864). return edge->fDX == 0 - && edge->fEdgeType == Edge::kLine_Type; + && edge->fEdgeType == Edge::Type::kLine; } // TODO: we can deallocate the edge if edge->setFoo() fails @@ -301,10 +301,6 @@ int SkEdgeBuilder::buildPoly(const SkPath& path, const SkIRect* iclip, bool canC } int SkEdgeBuilder::build(const SkPath& path, const SkIRect* iclip, bool canCullToTheRight) { - SkAutoConicToQuads quadder; - const SkScalar conicTol = SK_Scalar1 / 4; - bool is_finite = true; - SkPathEdgeIter iter(path); if (iclip) { SkRect clip = this->recoverClip(*iclip); @@ -333,45 +329,49 @@ int SkEdgeBuilder::build(const SkPath& path, const SkIRect* iclip, bool canCullT } } }, &rec); - is_finite = rec.fIsFinite; - } else { - auto handle_quad = [this](const SkPoint pts[3]) { - SkPoint monoX[5]; - int n = SkChopQuadAtYExtrema(pts, monoX); - for (int i = 0; i <= n; i++) { - this->addQuad(&monoX[i * 2]); + fEdgeList = fList.begin(); + return rec.fIsFinite ? fList.size() : 0; + } + + SkAutoConicToQuads quadder; + constexpr float kConicTol = 0.25f; + SkPoint monoY[10]; + SkPoint monoX[5]; + auto handle_quad = [this, &monoX](const SkPoint pts[3]) { + int n = SkChopQuadAtYExtrema(pts, monoX); + for (int i = 0; i <= n; i++) { + this->addQuad(&monoX[i * 2]); + } + }; + + while (auto e = iter.next()) { + switch (e.fEdge) { + case SkPathEdgeIter::Edge::kLine: + this->addLine(e.fPts); + break; + case SkPathEdgeIter::Edge::kQuad: { + handle_quad(e.fPts); + break; } - }; - while (auto e = iter.next()) { - switch (e.fEdge) { - case SkPathEdgeIter::Edge::kLine: - this->addLine(e.fPts); - break; - case SkPathEdgeIter::Edge::kQuad: { - handle_quad(e.fPts); - break; + case SkPathEdgeIter::Edge::kConic: { + const SkPoint* quadPts = + quadder.computeQuads(e.fPts, iter.conicWeight(), kConicTol); + for (int i = 0; i < quadder.countQuads(); ++i) { + handle_quad(quadPts); + quadPts += 2; } - case SkPathEdgeIter::Edge::kConic: { - const SkPoint* quadPts = quadder.computeQuads( - e.fPts, iter.conicWeight(), conicTol); - for (int i = 0; i < quadder.countQuads(); ++i) { - handle_quad(quadPts); - quadPts += 2; - } - } break; - case SkPathEdgeIter::Edge::kCubic: { - SkPoint monoY[10]; - int n = SkChopCubicAtYExtrema(e.fPts, monoY); - for (int i = 0; i <= n; i++) { - this->addCubic(&monoY[i * 3]); - } - break; + } break; + case SkPathEdgeIter::Edge::kCubic: { + int n = SkChopCubicAtYExtrema(e.fPts, monoY); + for (int i = 0; i <= n; i++) { + this->addCubic(&monoY[i * 3]); } + break; } } } fEdgeList = fList.begin(); - return is_finite ? fList.size() : 0; + return fList.size(); } int SkEdgeBuilder::buildEdges(const SkPath& path, diff --git a/gfx/skia/skia/src/core/SkFontPriv.h b/gfx/skia/skia/src/core/SkFontPriv.h index 49b6e94f6bfa..c07fef8d2402 100644 --- a/gfx/skia/skia/src/core/SkFontPriv.h +++ b/gfx/skia/skia/src/core/SkFontPriv.h @@ -82,7 +82,7 @@ public: // Returns the number of elements (characters or glyphs) in the array. static int CountTextElements(const void* text, size_t byteLength, SkTextEncoding); - static void GlyphsToUnichars(const SkFont&, const uint16_t glyphs[], int count, SkUnichar[]); + static void GlyphsToUnichars(const SkFont&, const SkGlyphID glyphs[], int count, SkUnichar[]); static void Flatten(const SkFont&, SkWriteBuffer& buffer); static bool Unflatten(SkFont*, SkReadBuffer& buffer); @@ -108,11 +108,11 @@ public: } int count() const { return fCount; } - const uint16_t* glyphs() const { return fGlyphs; } + const SkGlyphID* glyphs() const { return fGlyphs; } private: - skia_private::AutoSTArray<32, uint16_t> fStorage; - const uint16_t* fGlyphs; + skia_private::AutoSTArray<32, SkGlyphID> fStorage; + const SkGlyphID* fGlyphs; int fCount; }; diff --git a/gfx/skia/skia/src/core/SkFontScanner.h b/gfx/skia/skia/src/core/SkFontScanner.h deleted file mode 100644 index 1a1c6a94a827..000000000000 --- a/gfx/skia/skia/src/core/SkFontScanner.h +++ /dev/null @@ -1,43 +0,0 @@ -/* -* 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 SKFONTSCANNER_H_ -#define SKFONTSCANNER_H_ - -#include "include/core/SkFontArguments.h" -#include "include/core/SkTypes.h" -#include "include/private/base/SkFixed.h" -#include "include/private/base/SkNoncopyable.h" -#include "include/private/base/SkTArray.h" -class SkFontStyle; -class SkStreamAsset; -class SkString; - -class SkFontScanner : public SkNoncopyable { -public: - virtual ~SkFontScanner() = default; - struct AxisDefinition { - SkFourByteTag fTag; - SkScalar fMinimum; - SkScalar fDefault; - SkScalar fMaximum; - }; - typedef skia_private::STArray<4, AxisDefinition, true> AxisDefinitions; - - virtual bool scanFile(SkStreamAsset* stream, int* numFaces) const = 0; - virtual bool scanFace(SkStreamAsset* stream, int faceIndex, int* numInstances) const = 0; - /* instanceIndex 0 is the default instance, 1 to numInstances are the named instances. */ - virtual bool scanInstance(SkStreamAsset* stream, - int faceIndex, - int instanceIndex, - SkString* name, - SkFontStyle* style, - bool* isFixedPitch, - AxisDefinitions* axes) const = 0; -}; - -#endif // SKFONTSCANNER_H_ diff --git a/gfx/skia/skia/src/core/SkGlyph.cpp b/gfx/skia/skia/src/core/SkGlyph.cpp index 7a49ddf25b4c..667f1829f253 100644 --- a/gfx/skia/skia/src/core/SkGlyph.cpp +++ b/gfx/skia/skia/src/core/SkGlyph.cpp @@ -116,12 +116,6 @@ std::optional SkGlyph::MakeFromBuffer(SkReadBuffer& buffer) { return glyph; } -SkGlyph::SkGlyph(const SkGlyph&) = default; -SkGlyph& SkGlyph::operator=(const SkGlyph&) = default; -SkGlyph::SkGlyph(SkGlyph&&) = default; -SkGlyph& SkGlyph::operator=(SkGlyph&&) = default; -SkGlyph::~SkGlyph() = default; - SkMask SkGlyph::mask() const { SkIRect bounds = SkIRect::MakeXYWH(fLeft, fTop, fWidth, fHeight); return SkMask(static_cast(fImage), bounds, this->rowBytes(), fMaskFormat); diff --git a/gfx/skia/skia/src/core/SkGlyph.h b/gfx/skia/skia/src/core/SkGlyph.h index 8dcb4efd1214..07ccf26043e0 100644 --- a/gfx/skia/skia/src/core/SkGlyph.h +++ b/gfx/skia/skia/src/core/SkGlyph.h @@ -372,6 +372,15 @@ public: static uint32_t Hash(SkPackedGlyphID packedID) { return packedID.hash(); } + static bool ShouldGrow(int count, int capacity) { + // Having the 50% load factor results in performance improvements and significantly reduces + // the average number of probes on the Speedometer3 Editor-TipTap benchmark. + return 2 * count >= capacity; + } + static bool ShouldShrink(int count, int capacity) { + // Use 1/6 as the minimal load. + return 6 * count <= capacity; + } private: void setAction(skglyph::ActionType actionType, skglyph::GlyphAction action) { @@ -415,11 +424,11 @@ public: static std::optional MakeFromBuffer(SkReadBuffer&); // SkGlyph() is used for testing. constexpr SkGlyph() : SkGlyph{SkPackedGlyphID()} { } - SkGlyph(const SkGlyph&); - SkGlyph& operator=(const SkGlyph&); - SkGlyph(SkGlyph&&); - SkGlyph& operator=(SkGlyph&&); - ~SkGlyph(); + SkGlyph(const SkGlyph&) = default; + SkGlyph& operator=(const SkGlyph&) = default; + SkGlyph(SkGlyph&&) = default; + SkGlyph& operator=(SkGlyph&&) = default; + ~SkGlyph() = default; constexpr explicit SkGlyph(SkPackedGlyphID id) : fID{id} { } SkVector advanceVector() const { return SkVector{fAdvanceX, fAdvanceY}; } diff --git a/gfx/skia/skia/src/core/SkGlyphRunPainter.cpp b/gfx/skia/skia/src/core/SkGlyphRunPainter.cpp index 5175600b135d..27cda231a131 100644 --- a/gfx/skia/skia/src/core/SkGlyphRunPainter.cpp +++ b/gfx/skia/skia/src/core/SkGlyphRunPainter.cpp @@ -34,7 +34,6 @@ #include #include -#include using namespace skia_private; @@ -159,6 +158,50 @@ prepare_for_direct_mask_drawing(SkStrike* strike, return {acceptedBuffer.first(acceptedSize), rejectedBuffer.first(rejectedSize)}; } + +// Same as prepare_for_direct_mask_drawing but accepted points are unmapped source points. +std::tuple, SkZip> +prepare_for_direct_bitmap_drawing(SkStrike* strike, + const SkMatrix& creationMatrix, + SkZip source, + SkZip acceptedBuffer, + SkZip rejectedBuffer) { + const SkIPoint mask = strike->roundingSpec().ignorePositionFieldMask; + const SkPoint halfSampleFreq = strike->roundingSpec().halfAxisSampleFreq; + + // Build up the mapping from source space to device space. Add the rounding constant + // halfSampleFreq, so we just need to floor to get the device result. + SkMatrix positionMatrixWithRounding = creationMatrix; + positionMatrixWithRounding.postTranslate(halfSampleFreq.x(), halfSampleFreq.y()); + + int acceptedSize = 0; + int rejectedSize = 0; + strike->lock(); + for (auto [glyphID, pos] : source) { + if (!SkIsFinite(pos.x(), pos.y())) { + continue; + } + + const SkPoint mappedPos = positionMatrixWithRounding.mapPoint(pos); + const SkPackedGlyphID packedGlyphID = SkPackedGlyphID{glyphID, mappedPos, mask}; + switch (SkGlyphDigest digest = strike->digestFor(kDirectMaskCPU, packedGlyphID); + digest.actionFor(kDirectMaskCPU)) { + case GlyphAction::kAccept: { + acceptedBuffer[acceptedSize++] = + std::make_tuple(strike->glyph(digest), pos); + break; + } + case GlyphAction::kReject: + rejectedBuffer[rejectedSize++] = std::make_tuple(glyphID, pos); + break; + default: + break; + } + } + strike->unlock(); + + return {acceptedBuffer.first(acceptedSize), rejectedBuffer.first(rejectedSize)}; +} } // namespace // -- SkGlyphRunListPainterCPU --------------------------------------------------------------------- @@ -289,8 +332,6 @@ void SkGlyphRunListPainterCPU::drawForBitmapDevice(SkCanvas* canvas, bitmapDevice->paintMasks(accepted, paint); } if (!source.empty()) { - std::vector sourcePositions; - // Create a strike is source space to calculate scale information. SkStrikeSpec scaleStrikeSpec = SkStrikeSpec::MakeMask( runFont, paint, props, fScalerContextFlags, SkMatrix::I()); @@ -307,11 +348,8 @@ void SkGlyphRunListPainterCPU::drawForBitmapDevice(SkCanvas* canvas, continue; } SkPoint corners[4]; - SkPoint srcPos = pos + drawOrigin; - // Store off the positions in device space to position the glyphs during drawing. - sourcePositions.push_back(srcPos); SkRect rect = glyph->rect(); - rect.makeOffset(srcPos); + rect.makeOffset(drawOrigin + pos); positionMatrix.mapRectToQuad(corners, rect); // left top -> right top SkScalar scale = (corners[1] - corners[0]).length() / rect.width(); @@ -341,13 +379,13 @@ void SkGlyphRunListPainterCPU::drawForBitmapDevice(SkCanvas* canvas, auto strike = strikeSpec.findOrCreateStrike(); - auto [accepted, rejected] = prepare_for_direct_mask_drawing(strike.get(), - positionMatrix, - source, - acceptedBuffer, - rejectedBuffer); + auto [accepted, rejected] = prepare_for_direct_bitmap_drawing(strike.get(), + positionMatrix, + source, + acceptedBuffer, + rejectedBuffer); const SkScalar invMaxScale = 1.0f/maxScale; - for (auto [glyph, srcPos] : SkMakeZip(accepted.get<0>(), sourcePositions)) { + for (auto [glyph, srcPos] : accepted) { SkMask mask = glyph->mask(); // TODO: is this needed will A8 and BW just work? if (mask.fFormat != SkMask::kARGB32_Format) { @@ -361,12 +399,12 @@ void SkGlyphRunListPainterCPU::drawForBitmapDevice(SkCanvas* canvas, // Since the glyph in the cache is scaled by maxScale, its top left vector is too // long. Reduce it to find proper positions on the device. - SkPoint realPos = - srcPos + SkPoint::Make(mask.fBounds.left(), mask.fBounds.top())*invMaxScale; + SkPoint pos = drawOrigin + srcPos + + SkPoint::Make(mask.fBounds.left(), mask.fBounds.top())*invMaxScale; // Calculate the preConcat matrix for drawBitmap to get the rectangle from the // glyph cache (which is multiplied by maxScale) to land in the right place. - SkMatrix translate = SkMatrix::Translate(realPos); + SkMatrix translate = SkMatrix::Translate(pos); translate.preScale(invMaxScale, invMaxScale); // Draw the bitmap using the rect from the scaled cache, and not the source diff --git a/gfx/skia/skia/src/core/SkImageFilter.cpp b/gfx/skia/skia/src/core/SkImageFilter.cpp index 858e8b484460..4074461516a5 100644 --- a/gfx/skia/skia/src/core/SkImageFilter.cpp +++ b/gfx/skia/skia/src/core/SkImageFilter.cpp @@ -10,7 +10,7 @@ #include "include/core/SkColorFilter.h" #include "include/core/SkImage.h" #include "include/core/SkImageInfo.h" -#include "include/core/SkMatrix.h" +#include "include/core/SkM44.h" #include "include/core/SkPoint.h" #include "include/core/SkRect.h" #include "include/core/SkTypes.h" @@ -63,7 +63,7 @@ SkIRect SkImageFilter::filterBounds(const SkIRect& src, const SkMatrix& ctm, // The old filterBounds() function uses SkIRects that are defined in layer space so, while // we still are supporting it, bypass SkIF_B's new public filter bounds functions and go right // to the internal layer-space calculations. - skif::Mapping mapping{ctm}; + skif::Mapping mapping{SkM44(ctm)}; if (kReverse_MapDirection == direction) { skif::LayerSpace targetOutput(src); std::optional> content; @@ -245,7 +245,7 @@ skif::FilterResult SkImageFilter_Base::filterImage(const skif::Context& context) const SkIRect srcSubset = srcInKey ? context.source().image()->subset() : SkIRect::MakeWH(0, 0); SkImageFilterCacheKey key(fUniqueID, - context.mapping().layerMatrix(), + context.mapping().layerMatrix().asM33(), SkIRect(context.desiredOutput()), srcGenID, srcSubset); if (context.backend()->cache() && context.backend()->cache()->get(key, &result)) { @@ -279,7 +279,7 @@ sk_sp SkImageFilter_Base::makeImageWithFilter(sk_sp back skif::Stats stats; const skif::Context context{std::move(backend), - skif::Mapping(SkMatrix::I()), + skif::Mapping(SkM44()), skif::LayerSpace(clipBounds), skif::FilterResult(std::move(srcSpecialImage), skif::LayerSpace(subset.topLeft())), diff --git a/gfx/skia/skia/src/core/SkImageFilterTypes.cpp b/gfx/skia/skia/src/core/SkImageFilterTypes.cpp index 5280fc922281..33bc7f0c2e9b 100644 --- a/gfx/skia/skia/src/core/SkImageFilterTypes.cpp +++ b/gfx/skia/skia/src/core/SkImageFilterTypes.cpp @@ -99,7 +99,7 @@ void decompose_transform(const SkMatrix& transform, SkPoint representativePoint, } else { // Perspective, which has a non-uniform scaling effect on the filter. Pick a single scale // factor that best matches where the filter will be evaluated. - SkScalar approxScale = SkMatrixPriv::DifferentialAreaScale(transform, representativePoint); + float approxScale = SkMatrixPriv::DifferentialAreaScale(transform, representativePoint); if (SkIsFinite(approxScale) && !SkScalarNearlyZero(approxScale)) { // Now take the sqrt to go from an area scale factor to a scaling per X and Y approxScale = SkScalarSqrt(approxScale); @@ -107,8 +107,11 @@ void decompose_transform(const SkMatrix& transform, SkPoint representativePoint, // The point was behind the W = 0 plane, so don't factor out any scale. approxScale = 1.f; } - *postScaling = transform; - postScaling->preScale(SkScalarInvert(approxScale), SkScalarInvert(approxScale)); + if (postScaling) { + *postScaling = transform; + float invScale = SkScalarInvert(approxScale); + postScaling->preScale(invScale, invScale); + } *scaling = SkMatrix::Scale(approxScale, approxScale); } } @@ -266,25 +269,33 @@ SkIRect RoundOut(SkRect r) { return r.makeInset(kRoundEpsilon, kRoundEpsilon).ro SkIRect RoundIn(SkRect r) { return r.makeOutset(kRoundEpsilon, kRoundEpsilon).roundIn(); } -bool Mapping::decomposeCTM(const SkMatrix& ctm, MatrixCapability capability, +bool Mapping::decomposeCTM(const SkM44& ctm, MatrixCapability capability, const skif::ParameterSpace& representativePt) { - SkMatrix remainder, layer; + SkM44 remainder{SkM44::kUninitialized_Constructor}; + SkM44 layer{SkM44::kUninitialized_Constructor}; if (capability == MatrixCapability::kTranslate) { // Apply the entire CTM post-filtering remainder = ctm; - layer = SkMatrix::I(); - } else if (ctm.isScaleTranslate() || capability == MatrixCapability::kComplex) { + layer = SkM44(); + } else if (SkMatrixPriv::IsScaleTranslateAsM33(ctm) || + capability == MatrixCapability::kComplex) { // Either layer space can be anything (kComplex) - or - it can be scale+translate, and the // ctm is. In both cases, the layer space can be equivalent to device space. - remainder = SkMatrix::I(); + remainder = SkM44(); layer = ctm; } else { // This case implies some amount of sampling post-filtering, either due to skew or rotation // in the original matrix. As such, keep the layer matrix as simple as possible. - decompose_transform(ctm, SkPoint(representativePt), &remainder, &layer); + SkMatrix layer33; + decompose_transform(ctm.asM33(), SkPoint(representativePt), + /*postScaling=*/nullptr, &layer33); + layer = SkM44(layer33); + // Reconstruct full 4x4 remainder matrix so the mapping doesn't lose the 3rd row/column. + remainder = ctm; + remainder.preScale(1.f / layer.rc(0,0), 1.f / layer.rc(1,1)); } - SkMatrix invRemainder; + SkM44 invRemainder; if (!remainder.invert(&invRemainder)) { // Under floating point arithmetic, it's possible to decompose an invertible matrix into // a scaling matrix and a remainder and have the remainder be non-invertible. Generally @@ -299,7 +310,7 @@ bool Mapping::decomposeCTM(const SkMatrix& ctm, MatrixCapability capability, } } -bool Mapping::decomposeCTM(const SkMatrix& ctm, +bool Mapping::decomposeCTM(const SkM44& ctm, const SkImageFilter* filter, const skif::ParameterSpace& representativePt) { return this->decomposeCTM( @@ -308,8 +319,8 @@ bool Mapping::decomposeCTM(const SkMatrix& ctm, representativePt); } -bool Mapping::adjustLayerSpace(const SkMatrix& layer) { - SkMatrix invLayer; +bool Mapping::adjustLayerSpace(const SkM44& layer) { + SkM44 invLayer; if (!layer.invert(&invLayer)) { return false; } @@ -592,7 +603,7 @@ public: } if (renderInParameterSpace) { - fCanvas->concat(SkMatrix(ctx.mapping().layerMatrix())); + fCanvas->concat(ctx.mapping().layerMatrix()); } } @@ -1065,7 +1076,7 @@ static bool compatible_sampling(const SkSamplingOptions& currentSampling, } FilterResult FilterResult::applyTransform(const Context& ctx, - const LayerSpace &transform, + const LayerSpace& transform, const SkSamplingOptions &sampling) const { if (!fImage || ctx.desiredOutput().isEmpty()) { // Transformed transparent black remains transparent black. @@ -1073,6 +1084,10 @@ FilterResult FilterResult::applyTransform(const Context& ctx, return {}; } + if (!transform.invert(nullptr)) { + return {}; + } + // Extract the sampling options that matter based on the current and next transforms. // We make sure the new sampling is bilerp (default) if the new transform doesn't matter // (and assert that the current is bilerp if its transform didn't matter). Bilerp can be @@ -1974,9 +1989,8 @@ FilterResult FilterResult::MakeFromImage(const Context& ctx, // client could be doing their own external approximate-fit texturing. skif::FilterResult subset{std::move(specialImage), skif::LayerSpace(srcSubset.topLeft())}; - SkMatrix transform = SkMatrix::Concat(ctx.mapping().layerMatrix(), - SkMatrix::RectToRect(srcRect, SkRect(dstRect))); - return subset.applyTransform(ctx, skif::LayerSpace(transform), sampling); + SkM44 transform = ctx.mapping().layerMatrix() * SkM44::RectToRect(srcRect, SkRect(dstRect)); + return subset.applyTransform(ctx, skif::LayerSpace(transform.asM33()), sampling); } // For now, draw the src->dst subset of image into a new image. @@ -2012,7 +2026,7 @@ SkSpan> FilterResult::Builder::createInputShaders( // into is being sampled in parameter space. Add the inverse of the layerMatrix() (i.e. // layer to parameter space) as a local matrix to convert from the parameter-space coords // of the outer shader to the layer-space coords of the FilterResult). - SkAssertResult(fContext.mapping().layerMatrix().invert(&layerToParam)); + SkAssertResult(fContext.mapping().layerMatrix().asM33().invert(&layerToParam)); // Automatically add nonTrivial sampling if the layer-to-parameter space mapping isn't // also pixel aligned. if (!is_nearly_integer_translation(LayerSpace(layerToParam))) { @@ -2165,14 +2179,13 @@ FilterResult FilterResult::Builder::blur(const LayerSpace& sigma) { !algorithm->supportsOnlyDecalTiling()); // Map 'sigma' into the low-res image's pixel space to determine the low-res blur params to pass - // into the blur engine. - PixelSpace layerToLowRes; - SkAssertResult(lowResImage.fTransform.invert(&layerToLowRes)); - PixelSpace lowResSigma = layerToLowRes.mapSize(sigma); - // The layerToLowRes mapped size should be <= maxSigma, but clamp it just in case floating point - // error made it slightly higher. - lowResSigma = PixelSpace{{std::min(algorithm->maxSigma(), lowResSigma.width()), - std::min(algorithm->maxSigma(), lowResSigma.height())}}; + // into the blur engine. This relies on rescale() producing an image with a scale+translate + // transform, so it's possible to derive the inverse scale factors directly. We also clamp to + // be <= maxSigma just in case floating point error made it slightly higher. + const float invScaleX = sk_ieee_float_divide(1.f, lowResImage.fTransform.rc(0,0)); + const float invScaleY = sk_ieee_float_divide(1.f, lowResImage.fTransform.rc(1,1)); + PixelSpace lowResSigma{{std::min(sigma.width() * invScaleX, algorithm->maxSigma()), + std::min(sigma.height()* invScaleY, algorithm->maxSigma())}}; PixelSpace lowResMaxOutput{SkISize{lowResImage.fImage->width(), lowResImage.fImage->height()}}; @@ -2185,7 +2198,7 @@ FilterResult FilterResult::Builder::blur(const LayerSpace& sigma) { } else { // For decal and clamp tiling, the blurred image stops being interesting outside the radii // outset, so redo the max output analysis with the 'outputBounds' mapped into pixel space. - srcRelativeOutput = layerToLowRes.mapRect(outputBounds); + SkAssertResult(lowResImage.fTransform.inverseMapRect(outputBounds, &srcRelativeOutput)); // NOTE: Since 'lowResMaxOutput' is based on the actual image and deferred tiling, this can // be smaller than the pessimistic filling for a clamp-tiled blur. @@ -2193,9 +2206,15 @@ FilterResult FilterResult::Builder::blur(const LayerSpace& sigma) { 3.f * lowResSigma.height()}).ceil()); srcRelativeOutput = lowResMaxOutput.relevantSubset(srcRelativeOutput, lowResImage.tileMode()); + // Clamp won't return empty from relevantSubset() and a non-intersecting decal should have // been caught earlier. - SkASSERT(!srcRelativeOutput.isEmpty()); + // TODO(40042624): However, with some pathological inputs and the current mix of float vs. + // int representations, the definition of emptiness can change. Once everything is floating + // point, this check can be removed. + if (srcRelativeOutput.isEmpty()) { + return {}; + } // Include 1px of blur output so that it can be sampled during the upscale, which is needed // to correctly seam large blurs across crop/raster tiles (crbug.com/1500021). diff --git a/gfx/skia/skia/src/core/SkImageFilterTypes.h b/gfx/skia/skia/src/core/SkImageFilterTypes.h index 5f8292ec2b45..46bdc7c88334 100644 --- a/gfx/skia/skia/src/core/SkImageFilterTypes.h +++ b/gfx/skia/skia/src/core/SkImageFilterTypes.h @@ -10,6 +10,7 @@ #include "include/core/SkColorFilter.h" #include "include/core/SkColorSpace.h" +#include "include/core/SkM44.h" #include "include/core/SkMatrix.h" #include "include/core/SkPoint.h" #include "include/core/SkRect.h" @@ -523,7 +524,7 @@ public: } bool invert(LayerSpace* inverse) const { - return fData.invert(&inverse->fData); + return fData.invert(inverse ? &inverse->fData : nullptr); } // Transforms 'r' by the inverse of this matrix if it is invertible and stores it in 'out'. @@ -559,14 +560,14 @@ public: Mapping() = default; // Helper constructor that equates device and layer space to the same coordinate space. - explicit Mapping(const SkMatrix& paramToLayer) - : fLayerToDevMatrix(SkMatrix::I()) + explicit Mapping(const SkM44& paramToLayer) + : fLayerToDevMatrix(SkM44()) , fParamToLayerMatrix(paramToLayer) - , fDevToLayerMatrix(SkMatrix::I()) {} + , fDevToLayerMatrix(SkM44()) {} // This constructor allows the decomposition to be explicitly provided, assumes that // 'layerToDev's inverse has already been calculated in 'devToLayer' - Mapping(const SkMatrix& layerToDev, const SkMatrix& devToLayer, const SkMatrix& paramToLayer) + Mapping(const SkM44& layerToDev, const SkM44& devToLayer, const SkM44& paramToLayer) : fLayerToDevMatrix(layerToDev) , fParamToLayerMatrix(paramToLayer) , fDevToLayerMatrix(devToLayer) {} @@ -574,10 +575,10 @@ public: // Sets this Mapping to the default decomposition of the canvas's total transform, given the // requirements of the 'filter'. Returns false if the decomposition failed or would produce an // invalid device matrix. Assumes 'ctm' is invertible. - [[nodiscard]] bool decomposeCTM(const SkMatrix& ctm, + [[nodiscard]] bool decomposeCTM(const SkM44& ctm, const SkImageFilter* filter, const skif::ParameterSpace& representativePt); - [[nodiscard]] bool decomposeCTM(const SkMatrix& ctm, + [[nodiscard]] bool decomposeCTM(const SkM44& ctm, MatrixCapability, const skif::ParameterSpace& representativePt); @@ -593,34 +594,45 @@ public: // coordinate systems are changed, but skif::LayerSpace is adjusted. // // Returns false if the layer matrix cannot be inverted, and this mapping is left unmodified. - bool adjustLayerSpace(const SkMatrix& layer); + bool adjustLayerSpace(const SkM44& layer); // Update the mapping's layer space so that the point 'origin' in the current layer coordinate // space maps to (0, 0) in the adjusted coordinate space. void applyOrigin(const LayerSpace& origin) { - SkAssertResult(this->adjustLayerSpace(SkMatrix::Translate(-origin.x(), -origin.y()))); + SkAssertResult(this->adjustLayerSpace(SkM44::Translate(-origin.x(), -origin.y()))); } - const SkMatrix& layerToDevice() const { return fLayerToDevMatrix; } - const SkMatrix& deviceToLayer() const { return fDevToLayerMatrix; } - const SkMatrix& layerMatrix() const { return fParamToLayerMatrix; } - SkMatrix totalMatrix() const { - return SkMatrix::Concat(fLayerToDevMatrix, fParamToLayerMatrix); + const SkM44& layerToDevice() const { return fLayerToDevMatrix; } + const SkM44& deviceToLayer() const { return fDevToLayerMatrix; } + const SkM44& layerMatrix() const { return fParamToLayerMatrix; } + SkM44 totalMatrix() const { + return fLayerToDevMatrix * fParamToLayerMatrix; } template LayerSpace paramToLayer(const ParameterSpace& paramGeometry) const { - return LayerSpace(map(static_cast(paramGeometry), fParamToLayerMatrix)); + return LayerSpace(map(static_cast(paramGeometry), + fParamToLayerMatrix.asM33())); } template LayerSpace deviceToLayer(const DeviceSpace& devGeometry) const { - return LayerSpace(map(static_cast(devGeometry), fDevToLayerMatrix)); + // For inverse mapping back to layer space, we may be undoing perspective projection. + // Using fDevToLayerMatrix for this would require knowing the device-space Z values, + // which are discarded. fDevToLayerMatrix.asM33() would operate as if all those + // Z values were 0 (this is true for local 2D geometry, not device space). Instead, + // derive the 3x3 inverse of the flattened layer-to-device matrix, returning empty + // if numerical stability meant its 4x4 was invertible but somehow the 3x3 wasn't. + SkMatrix devToLayer33; + if (!fLayerToDevMatrix.asM33().invert(&devToLayer33)) { + return LayerSpace::Empty(); + } + return LayerSpace(map(static_cast(devGeometry), devToLayer33)); } template DeviceSpace layerToDevice(const LayerSpace& layerGeometry) const { - return DeviceSpace(map(static_cast(layerGeometry), fLayerToDevMatrix)); + return DeviceSpace(map(static_cast(layerGeometry), fLayerToDevMatrix.asM33())); } private: @@ -631,14 +643,16 @@ private: // param-to-layer matrix to define the layer-space coordinate system. Depending on how it's // decomposed, either the layer matrix or the device matrix could be the identity matrix (but // sometimes neither). - SkMatrix fLayerToDevMatrix; - SkMatrix fParamToLayerMatrix; + SkM44 fLayerToDevMatrix; + SkM44 fParamToLayerMatrix; - // Cached inverse of fLayerToDevMatrix - SkMatrix fDevToLayerMatrix; + // Cached inverse of fLayerToDevMatrix. We keep this as 4x4 so that conversion between different + // SkDevice coordinate spaces and coord space reconstruction is lossless. + SkM44 fDevToLayerMatrix; // Actual geometric mapping operations that work on coordinates and matrices w/o the type // safety of the coordinate space wrappers (hence these are private). + // TODO(b/40042800): Finish moving skif::Mapping operations to use the SkM44 directly. template static T map(const T& geom, const SkMatrix& matrix); }; diff --git a/gfx/skia/skia/src/core/SkImageInfoPriv.h b/gfx/skia/skia/src/core/SkImageInfoPriv.h index 8afc51d4954c..1e0076403d0b 100644 --- a/gfx/skia/skia/src/core/SkImageInfoPriv.h +++ b/gfx/skia/skia/src/core/SkImageInfoPriv.h @@ -45,6 +45,23 @@ static inline uint32_t SkColorTypeChannelFlags(SkColorType ct) { SkUNREACHABLE; } +static inline int SkColorTypeNumChannels(SkColorType ct) { + switch (SkColorTypeChannelFlags(ct)) { + case kRed_SkColorChannelFlag : return 1; + case kAlpha_SkColorChannelFlag : return 1; + case kGray_SkColorChannelFlag : return 1; + case kGrayAlpha_SkColorChannelFlags : return 2; + case kRG_SkColorChannelFlags : return 2; + case kRGB_SkColorChannelFlags : return 3; + case kRGBA_SkColorChannelFlags : return 4; + case 0 : return 0; + default: + SkDEBUGFAIL("unexpected color channel flags"); + return 0; + } + SkUNREACHABLE; +} + static inline bool SkColorTypeIsAlphaOnly(SkColorType ct) { return SkColorTypeChannelFlags(ct) == kAlpha_SkColorChannelFlag; } diff --git a/gfx/skia/skia/src/core/SkKnownRuntimeEffects.cpp b/gfx/skia/skia/src/core/SkKnownRuntimeEffects.cpp index 1597da304f1a..6df402955775 100644 --- a/gfx/skia/skia/src/core/SkKnownRuntimeEffects.cpp +++ b/gfx/skia/skia/src/core/SkKnownRuntimeEffects.cpp @@ -9,6 +9,7 @@ #include "include/core/SkString.h" #include "include/effects/SkRuntimeEffect.h" +#include "include/private/base/SkAssert.h" #include "src/core/SkRuntimeEffectPriv.h" #include "src/effects/imagefilters/SkMatrixConvolutionImageFilter.h" @@ -16,10 +17,19 @@ namespace SkKnownRuntimeEffects { namespace { +SkRuntimeEffect::Options get_options(StableKey stableKey) { + SkRuntimeEffect::Options options; + SkRuntimeEffectPriv::SetStableKeyOnOptions(&options, static_cast(stableKey)); + SkRuntimeEffectPriv::AllowPrivateAccess(&options); + return options; +} + // This must be kept in sync w/ the version in BlurUtils.h static constexpr int kMaxBlurSamples = 28; -SkRuntimeEffect* make_blur_1D_effect(int kernelWidth, const SkRuntimeEffect::Options& options) { +SkRuntimeEffect* make_blur_1D_shader(int kernelWidth, StableKey stableKey) { + SkRuntimeEffect::Options options = get_options(stableKey); + SkASSERT(kernelWidth <= kMaxBlurSamples); // The SkSL structure performs two kernel taps; if the kernel has an odd width the last // sample will be skipped with the current loop limit calculation. @@ -51,7 +61,9 @@ SkRuntimeEffect* make_blur_1D_effect(int kernelWidth, const SkRuntimeEffect::Opt options); } -SkRuntimeEffect* make_blur_2D_effect(int maxKernelSize, const SkRuntimeEffect::Options& options) { +SkRuntimeEffect* make_blur_2D_shader(int maxKernelSize, StableKey stableKey) { + SkRuntimeEffect::Options options = get_options(stableKey); + SkASSERT(maxKernelSize % 4 == 0); return SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader, SkStringPrintf( @@ -88,6 +100,38 @@ SkRuntimeEffect* make_blur_2D_effect(int maxKernelSize, const SkRuntimeEffect::O options); } +SkRuntimeEffect* make_blend_shader() { + SkRuntimeEffect::Options options = get_options(StableKey::kBlend); + + static constexpr char kBlendShaderCode[] = + "uniform shader s, d;" + "uniform blender b;" + "half4 main(float2 xy) {" + "return b.eval(s.eval(xy), d.eval(xy));" + "}"; + + return SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader, + kBlendShaderCode, + options); +} + +SkRuntimeEffect* make_lerp_shader() { + SkRuntimeEffect::Options options = get_options(StableKey::kLerp); + + static constexpr char kLerpFilterCode[] = + "uniform colorFilter cf0;" + "uniform colorFilter cf1;" + "uniform half weight;" + + "half4 main(half4 color) {" + "return mix(cf0.eval(color), cf1.eval(color), weight);" + "}"; + + return SkMakeRuntimeEffect(SkRuntimeEffect::MakeForColorFilter, + kLerpFilterCode, + options); +} + enum class MatrixConvolutionImpl { kUniformBased, kTextureBasedSm, @@ -98,8 +142,9 @@ enum class MatrixConvolutionImpl { // a smaller kernel version that stores the matrix in uniforms and iterates in 1D // a larger kernel version that stores the matrix in a 1D texture. The texture version has small // and large variants w/ the actual kernel size uploaded as a uniform. -SkRuntimeEffect* make_matrix_conv_effect(MatrixConvolutionImpl impl, - const SkRuntimeEffect::Options& options) { +SkRuntimeEffect* make_matrix_conv_shader(MatrixConvolutionImpl impl, StableKey stableKey) { + SkRuntimeEffect::Options options = get_options(stableKey); + // While the uniforms and kernel access are different, pieces of the algorithm are common and // defined statically for re-use in the two shaders: static const char* kHeaderAndBeginLoopSkSL = @@ -199,308 +244,356 @@ SkRuntimeEffect* make_matrix_conv_effect(MatrixConvolutionImpl impl, SkUNREACHABLE; } +SkRuntimeEffect* make_decal_shader() { + SkRuntimeEffect::Options options = get_options(StableKey::kDecal); + + static constexpr char kDecalShaderCode[] = + "uniform shader image;" + "uniform float4 decalBounds;" + + "half4 main(float2 coord) {" + "return sk_decal(image, coord, decalBounds);" + "}"; + + return SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader, + kDecalShaderCode, + options); +} + +SkRuntimeEffect* make_displacement_shader() { + SkRuntimeEffect::Options options = get_options(StableKey::kDisplacement); + + // NOTE: This uses dot product selection to work on all GLES2 hardware (enforced by + // public runtime effect restrictions). Otherwise, this would use a "uniform ivec2" + // and component indexing to convert the displacement color into a vector. + static constexpr char kDisplacementShaderCode[] = + "uniform shader displMap;" + "uniform shader colorMap;" + "uniform half2 scale;" + "uniform half4 xSelect;" // Only one of RGBA will be 1, the rest are 0 + "uniform half4 ySelect;" + + "half4 main(float2 coord) {" + "return sk_displacement(displMap, colorMap, coord, scale, xSelect, ySelect);" + "}"; + + return SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader, + kDisplacementShaderCode, + options); +} + +SkRuntimeEffect* make_lighting_shader() { + SkRuntimeEffect::Options options = get_options(StableKey::kLighting); + + static constexpr char kLightingShaderCode[] = + "uniform shader normalMap;" + + // Packs surface depth, shininess, material type (0 == diffuse) and light type + // (< 0 = distant, 0 = point, > 0 = spot) + "uniform half4 materialAndLightType;" + + "uniform half4 lightPosAndSpotFalloff;" // (x,y,z) are lightPos, w is spot falloff + // exponent + "uniform half4 lightDirAndSpotCutoff;" // (x,y,z) are lightDir, + // w is spot cos(cutoffAngle) + "uniform half3 lightColor;" // Material's k has already been multiplied in + + "half4 main(float2 coord) {" + "return sk_lighting(normalMap, coord," + /*depth=*/"materialAndLightType.x," + /*shininess=*/"materialAndLightType.y," + /*materialType=*/"materialAndLightType.z," + /*lightType=*/"materialAndLightType.w," + /*lightPos=*/"lightPosAndSpotFalloff.xyz," + /*spotFalloff=*/"lightPosAndSpotFalloff.w," + /*lightDir=*/"lightDirAndSpotCutoff.xyz," + /*cosCutoffAngle=*/"lightDirAndSpotCutoff.w," + "lightColor);" + "}"; + + return SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader, + kLightingShaderCode, + options); +} + +SkRuntimeEffect* make_linear_morphology_shader() { + SkRuntimeEffect::Options options = get_options(StableKey::kLinearMorphology); + + static constexpr char kLinearMorphologyShaderCode[] = + "uniform shader child;" + "uniform half2 offset;" + "uniform half flip;" // -1 converts the max() calls to min() + "uniform int radius;" + + "half4 main(float2 coord) {" + "return sk_linear_morphology(child, coord, offset, flip, radius);" + "}"; + + return SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader, + kLinearMorphologyShaderCode, + options); +} + +SkRuntimeEffect* make_magnifier_shader() { + SkRuntimeEffect::Options options = get_options(StableKey::kMagnifier); + + static constexpr char kMagnifierShaderCode[] = + "uniform shader src;" + "uniform float4 lensBounds;" + "uniform float4 zoomXform;" + "uniform float2 invInset;" + + "half4 main(float2 coord) {" + "return sk_magnifier(src, coord, lensBounds, zoomXform, invInset);" + "}"; + + return SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader, + kMagnifierShaderCode, + options); +} + +SkRuntimeEffect* make_normal_shader() { + SkRuntimeEffect::Options options = get_options(StableKey::kNormal); + + static constexpr char kNormalShaderCode[] = + "uniform shader alphaMap;" + "uniform float4 edgeBounds;" + "uniform half negSurfaceDepth;" + + "half4 main(float2 coord) {" + "return sk_normal(alphaMap, coord, edgeBounds, negSurfaceDepth);" + "}"; + + return SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader, + kNormalShaderCode, + options); +} + +SkRuntimeEffect* make_sparse_morphology_shader() { + SkRuntimeEffect::Options options = get_options(StableKey::kSparseMorphology); + + static constexpr char kSparseMorphologyShaderCode[] = + "uniform shader child;" + "uniform half2 offset;" + "uniform half flip;" + + "half4 main(float2 coord) {" + "return sk_sparse_morphology(child, coord, offset, flip);" + "}"; + + return SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader, + kSparseMorphologyShaderCode, + options); +} + +SkRuntimeEffect* make_arithmetic_blender() { + SkRuntimeEffect::Options options = get_options(StableKey::kArithmetic); + + static constexpr char kArithmeticBlenderCode[] = + "uniform half4 k;" + "uniform half pmClamp;" + + "half4 main(half4 src, half4 dst) {" + "return sk_arithmetic_blend(src, dst, k, pmClamp);" + "}"; + + return SkMakeRuntimeEffect(SkRuntimeEffect::MakeForBlender, + kArithmeticBlenderCode, + options); +} + +SkRuntimeEffect* make_high_contrast_color_filter() { + SkRuntimeEffect::Options options = get_options(StableKey::kHighContrast); + + static constexpr char kHighContrastFilterCode[] = + "uniform half grayscale, invertStyle, contrast;" + "half4 main(half4 color) {" + "return half4(sk_high_contrast(color.rgb, grayscale, invertStyle, contrast), color.a);" + "}"; + + return SkMakeRuntimeEffect(SkRuntimeEffect::MakeForColorFilter, + kHighContrastFilterCode, + options); +} + +SkRuntimeEffect* make_luma_color_filter() { + SkRuntimeEffect::Options options = get_options(StableKey::kLuma); + + static constexpr char kLumaFilterCode[] = + "half4 main(half4 color) {" + "return sk_luma(color.rgb);" + "}"; + + return SkMakeRuntimeEffect(SkRuntimeEffect::MakeForColorFilter, + kLumaFilterCode, + options); +} + +SkRuntimeEffect* make_overdraw_color_filter() { + SkRuntimeEffect::Options options = get_options(StableKey::kOverdraw); + + static constexpr char kOverdrawFilterCode[] = + "uniform half4 color0, color1, color2, color3, color4, color5;" + + "half4 main(half4 color) {" + "return sk_overdraw(color.a, color0, color1, color2, color3, color4, color5);" + "}"; + + return SkMakeRuntimeEffect(SkRuntimeEffect::MakeForColorFilter, + kOverdrawFilterCode, + options); +} + } // anonymous namespace -const SkRuntimeEffect* GetKnownRuntimeEffect(StableKey stableKey) { - SkRuntimeEffect::Options options; - SkRuntimeEffectPriv::SetStableKey(&options, static_cast(stableKey)); - SkRuntimeEffectPriv::AllowPrivateAccess(&options); +bool IsSkiaKnownRuntimeEffect(int candidate) { + return (candidate >= static_cast(StableKey::kStart) && + candidate <= static_cast(StableKey::kLast)); +} +bool IsUserDefinedRuntimeEffect(int candidate) { + return candidate >= kUnknownRuntimeEffectIDStart; +} + +bool IsViableUserDefinedKnownRuntimeEffect(int candidate) { + return candidate >= kUserDefinedKnownRuntimeEffectsStart && + candidate < kUserDefinedKnownRuntimeEffectsEnd; +} + +sk_sp MaybeGetKnownRuntimeEffect(uint32_t candidate) { + if (IsSkiaKnownRuntimeEffect(candidate)) { + SkKnownRuntimeEffects::StableKey stableKey = + static_cast(candidate); + + return sk_ref_sp(GetKnownRuntimeEffect(stableKey)); + } + + return nullptr; +} + +const SkRuntimeEffect* GetKnownRuntimeEffect(StableKey stableKey) { switch (stableKey) { case StableKey::kInvalid: return nullptr; // Shaders case StableKey::k1DBlur4: { - static SkRuntimeEffect* s1DBlurEffect = make_blur_1D_effect(4, options); + static SkRuntimeEffect* s1DBlurEffect = make_blur_1D_shader(4, stableKey); return s1DBlurEffect; } case StableKey::k1DBlur8: { - static SkRuntimeEffect* s1DBlurEffect = make_blur_1D_effect(8, options); + static SkRuntimeEffect* s1DBlurEffect = make_blur_1D_shader(8, stableKey); return s1DBlurEffect; } case StableKey::k1DBlur12: { - static SkRuntimeEffect* s1DBlurEffect = make_blur_1D_effect(12, options); + static SkRuntimeEffect* s1DBlurEffect = make_blur_1D_shader(12, stableKey); return s1DBlurEffect; } case StableKey::k1DBlur16: { - static SkRuntimeEffect* s1DBlurEffect = make_blur_1D_effect(16, options); + static SkRuntimeEffect* s1DBlurEffect = make_blur_1D_shader(16, stableKey); return s1DBlurEffect; } case StableKey::k1DBlur20: { - static SkRuntimeEffect* s1DBlurEffect = make_blur_1D_effect(20, options); + static SkRuntimeEffect* s1DBlurEffect = make_blur_1D_shader(20, stableKey); return s1DBlurEffect; } case StableKey::k1DBlur28: { - static SkRuntimeEffect* s1DBlurEffect = make_blur_1D_effect(28, options); + static SkRuntimeEffect* s1DBlurEffect = make_blur_1D_shader(28, stableKey); return s1DBlurEffect; } case StableKey::k2DBlur4: { - static SkRuntimeEffect* s2DBlurEffect = make_blur_2D_effect(4, options); + static SkRuntimeEffect* s2DBlurEffect = make_blur_2D_shader(4, stableKey); return s2DBlurEffect; } case StableKey::k2DBlur8: { - static SkRuntimeEffect* s2DBlurEffect = make_blur_2D_effect(8, options); + static SkRuntimeEffect* s2DBlurEffect = make_blur_2D_shader(8, stableKey); return s2DBlurEffect; } case StableKey::k2DBlur12: { - static SkRuntimeEffect* s2DBlurEffect = make_blur_2D_effect(12, options); + static SkRuntimeEffect* s2DBlurEffect = make_blur_2D_shader(12, stableKey); return s2DBlurEffect; } case StableKey::k2DBlur16: { - static SkRuntimeEffect* s2DBlurEffect = make_blur_2D_effect(16, options); + static SkRuntimeEffect* s2DBlurEffect = make_blur_2D_shader(16, stableKey); return s2DBlurEffect; } case StableKey::k2DBlur20: { - static SkRuntimeEffect* s2DBlurEffect = make_blur_2D_effect(20, options); + static SkRuntimeEffect* s2DBlurEffect = make_blur_2D_shader(20, stableKey); return s2DBlurEffect; } case StableKey::k2DBlur28: { - static SkRuntimeEffect* s2DBlurEffect = make_blur_2D_effect(28, options); + static SkRuntimeEffect* s2DBlurEffect = make_blur_2D_shader(28, stableKey); return s2DBlurEffect; } case StableKey::kBlend: { - static constexpr char kBlendShaderCode[] = - "uniform shader s, d;" - "uniform blender b;" - "half4 main(float2 xy) {" - "return b.eval(s.eval(xy), d.eval(xy));" - "}"; - - static const SkRuntimeEffect* sBlendEffect = - SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader, - kBlendShaderCode, - options); + static const SkRuntimeEffect* sBlendEffect = make_blend_shader(); return sBlendEffect; } case StableKey::kLerp: { - static constexpr char kLerpFilterCode[] = - "uniform colorFilter cf0;" - "uniform colorFilter cf1;" - "uniform half weight;" - - "half4 main(half4 color) {" - "return mix(cf0.eval(color), cf1.eval(color), weight);" - "}"; - - static const SkRuntimeEffect* sLerpEffect = - SkMakeRuntimeEffect(SkRuntimeEffect::MakeForColorFilter, - kLerpFilterCode, - options); + static const SkRuntimeEffect* sLerpEffect = make_lerp_shader(); return sLerpEffect; } case StableKey::kMatrixConvUniforms: { static const SkRuntimeEffect* sMatrixConvUniformsEffect = - make_matrix_conv_effect(MatrixConvolutionImpl::kUniformBased, options); + make_matrix_conv_shader(MatrixConvolutionImpl::kUniformBased, stableKey); return sMatrixConvUniformsEffect; } - case StableKey::kMatrixConvTexSm: { static const SkRuntimeEffect* sMatrixConvTexSmEffect = - make_matrix_conv_effect(MatrixConvolutionImpl::kTextureBasedSm, options); + make_matrix_conv_shader(MatrixConvolutionImpl::kTextureBasedSm, stableKey); return sMatrixConvTexSmEffect; } - case StableKey::kMatrixConvTexLg: { static const SkRuntimeEffect* sMatrixConvTexMaxEffect = - make_matrix_conv_effect(MatrixConvolutionImpl::kTextureBasedLg, options); + make_matrix_conv_shader(MatrixConvolutionImpl::kTextureBasedLg, stableKey); return sMatrixConvTexMaxEffect; } case StableKey::kDecal: { - static constexpr char kDecalShaderCode[] = - "uniform shader image;" - "uniform float4 decalBounds;" - - "half4 main(float2 coord) {" - "return sk_decal(image, coord, decalBounds);" - "}"; - - static const SkRuntimeEffect* sDecalEffect = - SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader, - kDecalShaderCode, - options); + static const SkRuntimeEffect* sDecalEffect = make_decal_shader(); return sDecalEffect; } case StableKey::kDisplacement: { - // NOTE: This uses dot product selection to work on all GLES2 hardware (enforced by - // public runtime effect restrictions). Otherwise, this would use a "uniform ivec2" - // and component indexing to convert the displacement color into a vector. - static constexpr char kDisplacementShaderCode[] = - "uniform shader displMap;" - "uniform shader colorMap;" - "uniform half2 scale;" - "uniform half4 xSelect;" // Only one of RGBA will be 1, the rest are 0 - "uniform half4 ySelect;" - - "half4 main(float2 coord) {" - "return sk_displacement(displMap, colorMap, coord, scale, xSelect, ySelect);" - "}"; - - static const SkRuntimeEffect* sDisplacementEffect = - SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader, - kDisplacementShaderCode, - options); + static const SkRuntimeEffect* sDisplacementEffect = make_displacement_shader(); return sDisplacementEffect; } case StableKey::kLighting: { - static constexpr char kLightingShaderCode[] = - "uniform shader normalMap;" - - // Packs surface depth, shininess, material type (0 == diffuse) and light type - // (< 0 = distant, 0 = point, > 0 = spot) - "uniform half4 materialAndLightType;" - - "uniform half4 lightPosAndSpotFalloff;" // (x,y,z) are lightPos, w is spot falloff - // exponent - "uniform half4 lightDirAndSpotCutoff;" // (x,y,z) are lightDir, - // w is spot cos(cutoffAngle) - "uniform half3 lightColor;" // Material's k has already been multiplied in - - "half4 main(float2 coord) {" - "return sk_lighting(normalMap, coord," - /*depth=*/"materialAndLightType.x," - /*shininess=*/"materialAndLightType.y," - /*materialType=*/"materialAndLightType.z," - /*lightType=*/"materialAndLightType.w," - /*lightPos=*/"lightPosAndSpotFalloff.xyz," - /*spotFalloff=*/"lightPosAndSpotFalloff.w," - /*lightDir=*/"lightDirAndSpotCutoff.xyz," - /*cosCutoffAngle=*/"lightDirAndSpotCutoff.w," - "lightColor);" - "}"; - - static const SkRuntimeEffect* sLightingEffect = - SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader, - kLightingShaderCode, - options); + static const SkRuntimeEffect* sLightingEffect = make_lighting_shader(); return sLightingEffect; } case StableKey::kLinearMorphology: { - static constexpr char kLinearMorphologyShaderCode[] = - "uniform shader child;" - "uniform half2 offset;" - "uniform half flip;" // -1 converts the max() calls to min() - "uniform int radius;" - - "half4 main(float2 coord) {" - "return sk_linear_morphology(child, coord, offset, flip, radius);" - "}"; - - static const SkRuntimeEffect* sLinearMorphologyEffect = - SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader, - kLinearMorphologyShaderCode, - options); + static const SkRuntimeEffect* sLinearMorphologyEffect = make_linear_morphology_shader(); return sLinearMorphologyEffect; } - case StableKey::kMagnifier: { - static constexpr char kMagnifierShaderCode[] = - "uniform shader src;" - "uniform float4 lensBounds;" - "uniform float4 zoomXform;" - "uniform float2 invInset;" - - "half4 main(float2 coord) {" - "return sk_magnifier(src, coord, lensBounds, zoomXform, invInset);" - "}"; - - static const SkRuntimeEffect* sMagnifierEffect = - SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader, - kMagnifierShaderCode, - options); + static const SkRuntimeEffect* sMagnifierEffect = make_magnifier_shader(); return sMagnifierEffect; } case StableKey::kNormal: { - static constexpr char kNormalShaderCode[] = - "uniform shader alphaMap;" - "uniform float4 edgeBounds;" - "uniform half negSurfaceDepth;" - - "half4 main(float2 coord) {" - "return sk_normal(alphaMap, coord, edgeBounds, negSurfaceDepth);" - "}"; - - static const SkRuntimeEffect* sNormalEffect = - SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader, - kNormalShaderCode, - options); + static const SkRuntimeEffect* sNormalEffect = make_normal_shader(); return sNormalEffect; } case StableKey::kSparseMorphology: { - static constexpr char kSparseMorphologyShaderCode[] = - "uniform shader child;" - "uniform half2 offset;" - "uniform half flip;" - - "half4 main(float2 coord) {" - "return sk_sparse_morphology(child, coord, offset, flip);" - "}"; - - static const SkRuntimeEffect* sSparseMorphologyEffect = - SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader, - kSparseMorphologyShaderCode, - options); + static const SkRuntimeEffect* sSparseMorphologyEffect = make_sparse_morphology_shader(); return sSparseMorphologyEffect; } // Blenders case StableKey::kArithmetic: { - static constexpr char kArithmeticBlenderCode[] = - "uniform half4 k;" - "uniform half pmClamp;" - - "half4 main(half4 src, half4 dst) {" - "return sk_arithmetic_blend(src, dst, k, pmClamp);" - "}"; - - static const SkRuntimeEffect* sArithmeticEffect = - SkMakeRuntimeEffect(SkRuntimeEffect::MakeForBlender, - kArithmeticBlenderCode, - options); + static const SkRuntimeEffect* sArithmeticEffect = make_arithmetic_blender(); return sArithmeticEffect; } // Color Filters case StableKey::kHighContrast: { - static constexpr char kHighContrastFilterCode[] = - "uniform half grayscale, invertStyle, contrast;" - "half4 main(half4 color) {" - "return half4(sk_high_contrast(color.rgb, grayscale, invertStyle, contrast)," - "color.a);" - "}"; - - static const SkRuntimeEffect* sHighContrastEffect = - SkMakeRuntimeEffect(SkRuntimeEffect::MakeForColorFilter, - kHighContrastFilterCode, - options); + static const SkRuntimeEffect* sHighContrastEffect = make_high_contrast_color_filter(); return sHighContrastEffect; } - case StableKey::kLuma: { - static constexpr char kLumaFilterCode[] = - "half4 main(half4 color) {" - "return sk_luma(color.rgb);" - "}"; - - static const SkRuntimeEffect* sLumaEffect = - SkMakeRuntimeEffect(SkRuntimeEffect::MakeForColorFilter, - kLumaFilterCode, - options); + static const SkRuntimeEffect* sLumaEffect = make_luma_color_filter(); return sLumaEffect; } - case StableKey::kOverdraw: { - static constexpr char kOverdrawFilterCode[] = - "uniform half4 color0, color1, color2, color3, color4, color5;" - - "half4 main(half4 color) {" - "return sk_overdraw(color.a, color0, color1, color2, color3, color4, color5);" - "}"; - - static const SkRuntimeEffect* sOverdrawEffect = - SkMakeRuntimeEffect(SkRuntimeEffect::MakeForColorFilter, - kOverdrawFilterCode, - options); + static const SkRuntimeEffect* sOverdrawEffect = make_overdraw_color_filter(); return sOverdrawEffect; } } diff --git a/gfx/skia/skia/src/core/SkKnownRuntimeEffects.h b/gfx/skia/skia/src/core/SkKnownRuntimeEffects.h index 706576c96173..f83dbc58ba8e 100644 --- a/gfx/skia/skia/src/core/SkKnownRuntimeEffects.h +++ b/gfx/skia/skia/src/core/SkKnownRuntimeEffects.h @@ -8,7 +8,7 @@ #ifndef SkKnownRuntimeEffects_DEFINED #define SkKnownRuntimeEffects_DEFINED -#include "include/core/SkTypes.h" +#include "include/core/SkRefCnt.h" #include class SkRuntimeEffect; @@ -18,8 +18,7 @@ namespace SkKnownRuntimeEffects { // We allocate the keys in blocks in the order: // Skia builtins // Skia known runtime effects -// Android known runtime effects -// Chrome known runtime effects +// First party client (i.e., Chrome and Android) known runtime effects // unknown runtime effects (on a first come, first served basis -> unstable) // // WARNING: If any of these values are changed, UniqueKeys that have stably-keyed effects @@ -27,22 +26,17 @@ namespace SkKnownRuntimeEffects { // TODO(b/238759147): add a revision number that can drive the invalidation. static constexpr int kSkiaBuiltInReservedCnt = 500; static constexpr int kSkiaKnownRuntimeEffectsReservedCnt = 500; -static constexpr int kAndroidKnownRuntimeEffectsReservedCnt = 100; -static constexpr int kChromeKnownRuntimeEffectsReservedCnt = 100; +static constexpr int kUserDefinedKnownRuntimeEffectsReservedCnt = 100; static constexpr int kSkiaKnownRuntimeEffectsStart = kSkiaBuiltInReservedCnt; static constexpr int kSkiaKnownRuntimeEffectsEnd = kSkiaKnownRuntimeEffectsStart + kSkiaKnownRuntimeEffectsReservedCnt; -static constexpr int kAndroidKnownRuntimeEffectsStart = kSkiaKnownRuntimeEffectsEnd; -static constexpr int kAndroidKnownRuntimeEffectsEnd = kAndroidKnownRuntimeEffectsStart + - kAndroidKnownRuntimeEffectsReservedCnt; +static constexpr int kUserDefinedKnownRuntimeEffectsStart = kSkiaKnownRuntimeEffectsEnd; +static constexpr int kUserDefinedKnownRuntimeEffectsEnd = + kUserDefinedKnownRuntimeEffectsStart + kUserDefinedKnownRuntimeEffectsReservedCnt; -static constexpr int kChromeKnownRuntimeEffectsStart = kAndroidKnownRuntimeEffectsEnd; -static constexpr int kChromeKnownRuntimeEffectsEnd = kChromeKnownRuntimeEffectsStart + - kChromeKnownRuntimeEffectsReservedCnt; - -static constexpr int kUnknownRuntimeEffectIDStart = kChromeKnownRuntimeEffectsEnd; +static constexpr int kUnknownRuntimeEffectIDStart = kUserDefinedKnownRuntimeEffectsEnd; // All six 1DBlur* stable keys must be consecutive after 1DBlurBase and // there is no 1DBlur24 bc for large kernels we bin by a multiple of eight. @@ -105,8 +99,25 @@ enum class StableKey : uint32_t { static const int kStableKeyCnt = static_cast(StableKey::kLast) - static_cast(StableKey::kStart) + 1; +// kStart cannot be allowed to be zero since that is used as the not-a-stable-key value in the +// serialized runtime effects (c.f., SkRuntimeShader::flatten) +static_assert(static_cast(StableKey::kStart) != 0); + static_assert(static_cast(StableKey::kLast) < kSkiaKnownRuntimeEffectsEnd); +// Is 'candidate' a viable StableKey value. Note that this includes the kInvalid value. +bool IsSkiaKnownRuntimeEffect(int candidate); + +bool IsUserDefinedRuntimeEffect(int candidate); + +// This call provides a rough check on 'candidate's viability as a user-defined known +// run-time effect stable key. Every use of this method should be followed up with a call to +// ShaderCodeDictionary::isUserDefinedKnownRuntimeEffect - which provides tighter bounds. +bool IsViableUserDefinedKnownRuntimeEffect(int candidate); + +// Validate 'candidate' before calling GetKnownRuntimeEffect +sk_sp MaybeGetKnownRuntimeEffect(uint32_t candidate); + const SkRuntimeEffect* GetKnownRuntimeEffect(StableKey); static_assert(static_cast(StableKey::kInvalid) == static_cast(StableKey::kStart)); diff --git a/gfx/skia/skia/src/core/SkLRUCache.h b/gfx/skia/skia/src/core/SkLRUCache.h index 2b6921a288f2..93c205a729c8 100644 --- a/gfx/skia/skia/src/core/SkLRUCache.h +++ b/gfx/skia/skia/src/core/SkLRUCache.h @@ -12,16 +12,21 @@ #include "src/core/SkChecksum.h" #include "src/core/SkTHash.h" +struct SkNoOpPurge { + template + void operator()(void* /* context */, const K& /* k */, const V* /* v */) const {} +}; + /** * A generic LRU cache. */ -template +template class SkLRUCache { private: struct Entry { Entry(const K& key, V&& value) - : fKey(key) - , fValue(std::move(value)) {} + : fKey(key) + , fValue(std::move(value)) {} K fKey; V fValue; @@ -30,7 +35,9 @@ private: }; public: - explicit SkLRUCache(int maxCount) : fMaxCount(maxCount) {} + explicit SkLRUCache(int maxCount, void* context = nullptr) + : fMaxCount(maxCount) + , fContext(context) {} SkLRUCache() = delete; ~SkLRUCache() { @@ -101,6 +108,17 @@ public: } } + void remove(const K& key) { + Entry** value = fMap.find(key); + SkASSERT(value); + Entry* entry = *value; + SkASSERT(key == entry->fKey); + PurgeCB()(fContext, key, &entry->fValue); + fMap.remove(key); + fLRU.remove(entry); + delete entry; + } + private: struct Traits { static const K& GetKey(Entry* e) { @@ -112,19 +130,10 @@ private: } }; - void remove(const K& key) { - Entry** value = fMap.find(key); - SkASSERT(value); - Entry* entry = *value; - SkASSERT(key == entry->fKey); - fMap.remove(key); - fLRU.remove(entry); - delete entry; - } - int fMaxCount; skia_private::THashTable fMap; SkTInternalLList fLRU; + void* fContext; }; #endif diff --git a/gfx/skia/skia/src/core/SkMD5.cpp b/gfx/skia/skia/src/core/SkMD5.cpp index a3e046962792..365023517392 100644 --- a/gfx/skia/skia/src/core/SkMD5.cpp +++ b/gfx/skia/skia/src/core/SkMD5.cpp @@ -26,7 +26,7 @@ static void transform(uint32_t state[4], const uint8_t block[64]); static void encode(uint8_t output[16], const uint32_t input[4]); /** Encodes input into output (little endian 64 bit value). */ -static void encode(uint8_t output[8], const uint64_t input); +static void encode(uint8_t output[8], uint64_t input); /** Decodes input (4 little endian 32 bit values) into storage, if required. */ static const uint32_t* decode(uint32_t storage[16], const uint8_t input[64]); @@ -246,7 +246,7 @@ static void encode(uint8_t output[16], const uint32_t input[4]) { } } -static void encode(uint8_t output[8], const uint64_t input) { +static void encode(uint8_t output[8], uint64_t input) { output[0] = (uint8_t) (input & 0xff); output[1] = (uint8_t)((input >> 8) & 0xff); output[2] = (uint8_t)((input >> 16) & 0xff); diff --git a/gfx/skia/skia/src/core/SkMask.h b/gfx/skia/skia/src/core/SkMask.h index d611c698db2b..9e8e95d7e9bb 100644 --- a/gfx/skia/skia/src/core/SkMask.h +++ b/gfx/skia/skia/src/core/SkMask.h @@ -8,11 +8,11 @@ #ifndef SkMask_DEFINED #define SkMask_DEFINED -#include "include/core/SkColorPriv.h" #include "include/core/SkRect.h" -#include "include/private/SkColorData.h" #include "include/private/base/SkAssert.h" #include "include/private/base/SkTemplates.h" +#include "src/core/SkColorData.h" +#include "src/core/SkColorPriv.h" #include #include diff --git a/gfx/skia/skia/src/core/SkMaskBlurFilter.cpp b/gfx/skia/skia/src/core/SkMaskBlurFilter.cpp index 5af114c7e2a8..3401cafea7ed 100644 --- a/gfx/skia/skia/src/core/SkMaskBlurFilter.cpp +++ b/gfx/skia/skia/src/core/SkMaskBlurFilter.cpp @@ -7,25 +7,25 @@ #include "src/core/SkMaskBlurFilter.h" -#include "include/core/SkColorPriv.h" +#include "include/private/base/SkFloatingPoint.h" #include "include/private/base/SkMalloc.h" #include "include/private/base/SkTPin.h" #include "include/private/base/SkTemplates.h" #include "include/private/base/SkTo.h" #include "src/base/SkArenaAlloc.h" #include "src/base/SkVx.h" +#include "src/core/SkColorPriv.h" #include "src/core/SkGaussFilter.h" #include #include namespace { -static const double kPi = 3.14159265358979323846264338327950288; class PlanGauss final { public: explicit PlanGauss(double sigma) { - auto possibleWindow = static_cast(floor(sigma * 3 * sqrt(2 * kPi) / 4 + 0.5)); + auto possibleWindow = static_cast(floor(sigma * 3 * sqrt(2 * SK_DoublePI) / 4 + 0.5)); auto window = std::max(1, possibleWindow); fPass0Size = window - 1; @@ -235,7 +235,7 @@ public: int fPass2Size; }; -} // namespace +} // namespace // NB 135 is the largest sigma that will not cause a buffer full of 255 mask values to overflow // using the Gauss filter. It also limits the size of buffers used hold intermediate values. The @@ -260,7 +260,17 @@ SkMaskBlurFilter::SkMaskBlurFilter(double sigmaW, double sigmaH) } bool SkMaskBlurFilter::hasNoBlur() const { - return (3 * fSigmaW <= 1) && (3 * fSigmaH <= 1); + // If the sigma value is less than a certain amount, the window will be 0 which means + // there is effectively no blur. Using Wolfram alpha to solve the equation used for + // possibleWindow above shows that the threshold is (2 * sqrt(2/pi))/3 +#if defined(SK_USE_LARGER_NO_BLUR_THRESHOLD) + constexpr double kNoWindowSigma = 0.531923; +#else + // However, historically we used 1/3 as the cutoff. Clients who might have pixel tests + // that depend on this can be updated one at a time. + constexpr double kNoWindowSigma = 1./3.; +#endif + return fSigmaW < kNoWindowSigma && fSigmaH <= kNoWindowSigma; } // We favor A8 masks, and if we need to work with another format, we'll convert to A8 first. diff --git a/gfx/skia/skia/src/core/SkMaskCache.cpp b/gfx/skia/skia/src/core/SkMaskCache.cpp index 82f3e0ce1a65..91dc7755cf0f 100644 --- a/gfx/skia/skia/src/core/SkMaskCache.cpp +++ b/gfx/skia/skia/src/core/SkMaskCache.cpp @@ -113,15 +113,13 @@ static unsigned gRectsBlurKeyNamespaceLabel; struct RectsBlurKey : public SkResourceCache::Key { public: - RectsBlurKey(SkScalar sigma, SkBlurStyle style, const SkRect rects[], int count) - : fSigma(sigma) - , fStyle(style) - { - SkASSERT(1 == count || 2 == count); + RectsBlurKey(SkScalar sigma, SkBlurStyle style, SkSpan rects) + : fSigma(sigma), fStyle(style) { + SkASSERT(rects.size() == 1 || rects.size() == 2); SkIRect ir; rects[0].roundOut(&ir); fSizes[0] = SkSize{rects[0].width(), rects[0].height()}; - if (2 == count) { + if (rects.size() == 2) { fSizes[1] = SkSize{rects[1].width(), rects[1].height()}; fSizes[2] = SkSize{rects[0].x() - rects[1].x(), rects[0].y() - rects[1].y()}; } else { @@ -175,11 +173,13 @@ struct RectsBlurRec : public SkResourceCache::Rec { }; } // namespace -SkCachedData* SkMaskCache::FindAndRef(SkScalar sigma, SkBlurStyle style, - const SkRect rects[], int count, SkTLazy* mask, +SkCachedData* SkMaskCache::FindAndRef(SkScalar sigma, + SkBlurStyle style, + SkSpan rects, + SkTLazy* mask, SkResourceCache* localCache) { SkTLazy result; - RectsBlurKey key(sigma, style, rects, count); + RectsBlurKey key(sigma, style, rects); if (!CHECK_LOCAL(localCache, find, Find, key, RectsBlurRec::Visitor, &result)) { return nullptr; } @@ -189,9 +189,12 @@ SkCachedData* SkMaskCache::FindAndRef(SkScalar sigma, SkBlurStyle style, return result->fData; } -void SkMaskCache::Add(SkScalar sigma, SkBlurStyle style, - const SkRect rects[], int count, const SkMask& mask, SkCachedData* data, +void SkMaskCache::Add(SkScalar sigma, + SkBlurStyle style, + SkSpan rects, + const SkMask& mask, + SkCachedData* data, SkResourceCache* localCache) { - RectsBlurKey key(sigma, style, rects, count); + RectsBlurKey key(sigma, style, rects); return CHECK_LOCAL(localCache, add, Add, new RectsBlurRec(key, mask, data)); } diff --git a/gfx/skia/skia/src/core/SkMaskCache.h b/gfx/skia/skia/src/core/SkMaskCache.h index dcb8548290d8..5d56244e967b 100644 --- a/gfx/skia/skia/src/core/SkMaskCache.h +++ b/gfx/skia/skia/src/core/SkMaskCache.h @@ -9,6 +9,7 @@ #define SkMaskCache_DEFINED #include "include/core/SkScalar.h" +#include "include/core/SkSpan.h" class SkCachedData; class SkRRect; @@ -29,8 +30,10 @@ public: static SkCachedData* FindAndRef(SkScalar sigma, SkBlurStyle style, const SkRRect& rrect, SkTLazy* mask, SkResourceCache* localCache = nullptr); - static SkCachedData* FindAndRef(SkScalar sigma, SkBlurStyle style, - const SkRect rects[], int count, SkTLazy* mask, + static SkCachedData* FindAndRef(SkScalar sigma, + SkBlurStyle style, + SkSpan rects, + SkTLazy* mask, SkResourceCache* localCache = nullptr); /** @@ -39,8 +42,11 @@ public: static void Add(SkScalar sigma, SkBlurStyle style, const SkRRect& rrect, const SkMask& mask, SkCachedData* data, SkResourceCache* localCache = nullptr); - static void Add(SkScalar sigma, SkBlurStyle style, - const SkRect rects[], int count, const SkMask& mask, SkCachedData* data, + static void Add(SkScalar sigma, + SkBlurStyle style, + SkSpan rects, + const SkMask& mask, + SkCachedData* data, SkResourceCache* localCache = nullptr); }; diff --git a/gfx/skia/skia/src/core/SkMaskFilter.cpp b/gfx/skia/skia/src/core/SkMaskFilter.cpp index d56f7ae4b9f5..e75d29868018 100644 --- a/gfx/skia/skia/src/core/SkMaskFilter.cpp +++ b/gfx/skia/skia/src/core/SkMaskFilter.cpp @@ -7,319 +7,11 @@ #include "include/core/SkMaskFilter.h" #include "include/core/SkFlattenable.h" -#include "include/core/SkImageFilter.h" -#include "include/core/SkMatrix.h" -#include "include/core/SkPath.h" -#include "include/core/SkPoint.h" -#include "include/core/SkRect.h" #include "include/core/SkRefCnt.h" -#include "include/core/SkRegion.h" -#include "include/core/SkStrokeRec.h" #include "include/core/SkTypes.h" -#include "include/private/base/SkTemplates.h" -#include "src/base/SkAutoMalloc.h" -#include "src/base/SkTLazy.h" -#include "src/core/SkBlitter.h" -#include "src/core/SkCachedData.h" -#include "src/core/SkDraw.h" -#include "src/core/SkMask.h" #include "src/core/SkMaskFilterBase.h" -#include "src/core/SkPathPriv.h" -#include "src/core/SkRasterClip.h" - -#include -#include - -class SkRRect; struct SkDeserialProcs; -SkMaskFilterBase::NinePatch::~NinePatch() { - if (fCache) { - SkASSERT((const void*)fMask.fImage == fCache->data()); - fCache->unref(); - } else { - // fMask is about to be destroyed and "owns" its fImage. - SkMaskBuilder::FreeImage(const_cast(fMask.fImage)); - } -} - -bool SkMaskFilterBase::asABlur(BlurRec*) const { - return false; -} - -sk_sp SkMaskFilterBase::asImageFilter(const SkMatrix& ctm) const { - return nullptr; -} - -static SkMask extractMaskSubset(const SkMask& src, SkIRect bounds, int32_t newX, int32_t newY) { - SkASSERT(src.fBounds.contains(bounds)); - - const int dx = bounds.left() - src.fBounds.left(); - const int dy = bounds.top() - src.fBounds.top(); - bounds.offsetTo(newX, newY); - return SkMask(src.fImage + dy * src.fRowBytes + dx, - bounds, - src.fRowBytes, - src.fFormat); -} - -static void blitClippedMask(SkBlitter* blitter, const SkMask& mask, - const SkIRect& bounds, const SkIRect& clipR) { - SkIRect r; - if (r.intersect(bounds, clipR)) { - blitter->blitMask(mask, r); - } -} - -static void blitClippedRect(SkBlitter* blitter, const SkIRect& rect, const SkIRect& clipR) { - SkIRect r; - if (r.intersect(rect, clipR)) { - blitter->blitRect(r.left(), r.top(), r.width(), r.height()); - } -} - -static void draw_nine_clipped(const SkMask& mask, const SkIRect& outerR, - const SkIPoint& center, bool fillCenter, - const SkIRect& clipR, SkBlitter* blitter) { - int cx = center.x(); - int cy = center.y(); - SkIRect bounds; - - // top-left - bounds = mask.fBounds; - bounds.fRight = cx; - bounds.fBottom = cy; - if (bounds.width() > 0 && bounds.height() > 0) { - SkMask m = extractMaskSubset(mask, bounds, outerR.left(), outerR.top()); - blitClippedMask(blitter, m, m.fBounds, clipR); - } - - // top-right - bounds = mask.fBounds; - bounds.fLeft = cx + 1; - bounds.fBottom = cy; - if (bounds.width() > 0 && bounds.height() > 0) { - SkMask m = extractMaskSubset(mask, bounds, outerR.right() - bounds.width(), outerR.top()); - blitClippedMask(blitter, m, m.fBounds, clipR); - } - - // bottom-left - bounds = mask.fBounds; - bounds.fRight = cx; - bounds.fTop = cy + 1; - if (bounds.width() > 0 && bounds.height() > 0) { - SkMask m = extractMaskSubset(mask, bounds, outerR.left(), outerR.bottom() - bounds.height()); - blitClippedMask(blitter, m, m.fBounds, clipR); - } - - // bottom-right - bounds = mask.fBounds; - bounds.fLeft = cx + 1; - bounds.fTop = cy + 1; - if (bounds.width() > 0 && bounds.height() > 0) { - SkMask m = extractMaskSubset(mask, bounds, outerR.right() - bounds.width(), - outerR.bottom() - bounds.height()); - blitClippedMask(blitter, m, m.fBounds, clipR); - } - - SkIRect innerR; - innerR.setLTRB(outerR.left() + cx - mask.fBounds.left(), - outerR.top() + cy - mask.fBounds.top(), - outerR.right() + (cx + 1 - mask.fBounds.right()), - outerR.bottom() + (cy + 1 - mask.fBounds.bottom())); - if (fillCenter) { - blitClippedRect(blitter, innerR, clipR); - } - - const int innerW = innerR.width(); - size_t storageSize = (innerW + 1) * (sizeof(int16_t) + sizeof(uint8_t)); - SkAutoSMalloc<4*1024> storage(storageSize); - int16_t* runs = (int16_t*)storage.get(); - uint8_t* alpha = (uint8_t*)(runs + innerW + 1); - - SkIRect r; - // top - r.setLTRB(innerR.left(), outerR.top(), innerR.right(), innerR.top()); - if (r.intersect(clipR)) { - int startY = std::max(0, r.top() - outerR.top()); - int stopY = startY + r.height(); - int width = r.width(); - for (int y = startY; y < stopY; ++y) { - runs[0] = width; - runs[width] = 0; - alpha[0] = *mask.getAddr8(cx, mask.fBounds.top() + y); - blitter->blitAntiH(r.left(), outerR.top() + y, alpha, runs); - } - } - // bottom - r.setLTRB(innerR.left(), innerR.bottom(), innerR.right(), outerR.bottom()); - if (r.intersect(clipR)) { - int startY = outerR.bottom() - r.bottom(); - int stopY = startY + r.height(); - int width = r.width(); - for (int y = startY; y < stopY; ++y) { - runs[0] = width; - runs[width] = 0; - alpha[0] = *mask.getAddr8(cx, mask.fBounds.bottom() - y - 1); - blitter->blitAntiH(r.left(), outerR.bottom() - y - 1, alpha, runs); - } - } - // left - r.setLTRB(outerR.left(), innerR.top(), innerR.left(), innerR.bottom()); - if (r.intersect(clipR)) { - SkMask leftMask(mask.getAddr8(mask.fBounds.left() + r.left() - outerR.left(), - mask.fBounds.top() + cy), - r, - 0, // so we repeat the scanline for our height - SkMask::kA8_Format); - blitter->blitMask(leftMask, r); - } - // right - r.setLTRB(innerR.right(), innerR.top(), outerR.right(), innerR.bottom()); - if (r.intersect(clipR)) { - SkMask rightMask(mask.getAddr8(mask.fBounds.right() - outerR.right() + r.left(), - mask.fBounds.top() + cy), - r, - 0, // so we repeat the scanline for our height - SkMask::kA8_Format); - blitter->blitMask(rightMask, r); - } -} - -static void draw_nine(const SkMask& mask, const SkIRect& outerR, const SkIPoint& center, - bool fillCenter, const SkRasterClip& clip, SkBlitter* blitter) { - // if we get here, we need to (possibly) resolve the clip and blitter - SkAAClipBlitterWrapper wrapper(clip, blitter); - blitter = wrapper.getBlitter(); - - SkRegion::Cliperator clipper(wrapper.getRgn(), outerR); - - if (!clipper.done()) { - const SkIRect& cr = clipper.rect(); - do { - draw_nine_clipped(mask, outerR, center, fillCenter, cr, blitter); - clipper.next(); - } while (!clipper.done()); - } -} - -static int countNestedRects(const SkPath& path, SkRect rects[2]) { - if (SkPathPriv::IsNestedFillRects(path, rects)) { - return 2; - } - return path.isRect(&rects[0]); -} - -bool SkMaskFilterBase::filterRRect(const SkRRect& devRRect, const SkMatrix& matrix, - const SkRasterClip& clip, SkBlitter* blitter) const { - // Attempt to speed up drawing by creating a nine patch. If a nine patch - // cannot be used, return false to allow our caller to recover and perform - // the drawing another way. - SkTLazy patch; - if (kTrue_FilterReturn != this->filterRRectToNine(devRRect, matrix, - clip.getBounds(), - &patch)) { - SkASSERT(!patch.isValid()); - return false; - } - draw_nine(patch->fMask, patch->fOuterRect, patch->fCenter, true, clip, blitter); - return true; -} - -bool SkMaskFilterBase::filterPath(const SkPath& devPath, const SkMatrix& matrix, - const SkRasterClip& clip, SkBlitter* blitter, - SkStrokeRec::InitStyle style) const { - SkRect rects[2]; - int rectCount = 0; - if (SkStrokeRec::kFill_InitStyle == style) { - rectCount = countNestedRects(devPath, rects); - } - if (rectCount > 0) { - SkTLazy patch; - - switch (this->filterRectsToNine(rects, rectCount, matrix, clip.getBounds(), &patch)) { - case kFalse_FilterReturn: - SkASSERT(!patch.isValid()); - return false; - - case kTrue_FilterReturn: - draw_nine(patch->fMask, patch->fOuterRect, patch->fCenter, 1 == rectCount, clip, - blitter); - return true; - - case kUnimplemented_FilterReturn: - SkASSERT(!patch.isValid()); - // fall out - break; - } - } - - SkMaskBuilder srcM, dstM; - -#if defined(SK_BUILD_FOR_FUZZER) - if (devPath.countVerbs() > 1000 || devPath.countPoints() > 1000) { - return false; - } -#endif - if (!SkDraw::DrawToMask(devPath, clip.getBounds(), this, &matrix, &srcM, - SkMaskBuilder::kComputeBoundsAndRenderImage_CreateMode, - style)) { - return false; - } - SkAutoMaskFreeImage autoSrc(srcM.image()); - - if (!this->filterMask(&dstM, srcM, matrix, nullptr)) { - return false; - } - SkAutoMaskFreeImage autoDst(dstM.image()); - - // if we get here, we need to (possibly) resolve the clip and blitter - SkAAClipBlitterWrapper wrapper(clip, blitter); - blitter = wrapper.getBlitter(); - - SkRegion::Cliperator clipper(wrapper.getRgn(), dstM.fBounds); - - if (!clipper.done()) { - const SkIRect& cr = clipper.rect(); - do { - blitter->blitMask(dstM, cr); - clipper.next(); - } while (!clipper.done()); - } - - return true; -} - -SkMaskFilterBase::FilterReturn -SkMaskFilterBase::filterRRectToNine(const SkRRect&, const SkMatrix&, - const SkIRect& clipBounds, SkTLazy*) const { - return kUnimplemented_FilterReturn; -} - -SkMaskFilterBase::FilterReturn -SkMaskFilterBase::filterRectsToNine(const SkRect[], int count, const SkMatrix&, - const SkIRect& clipBounds, SkTLazy*) const { - return kUnimplemented_FilterReturn; -} - -void SkMaskFilterBase::computeFastBounds(const SkRect& src, SkRect* dst) const { - SkMask srcM(nullptr, src.roundOut(), 0, SkMask::kA8_Format); - SkMaskBuilder dstM; - - SkIPoint margin; // ignored - if (this->filterMask(&dstM, srcM, SkMatrix::I(), &margin)) { - dst->set(dstM.fBounds); - } else { - dst->set(srcM.fBounds); - } -} - -SkRect SkMaskFilter::approximateFilteredBounds(const SkRect& src) const { - SkRect dst; - as_MFB(this)->computeFastBounds(src, &dst); - return dst; -} - void SkMaskFilter::RegisterFlattenables() { sk_register_blur_maskfilter_createproc(); } diff --git a/gfx/skia/skia/src/core/SkMaskFilterBase.cpp b/gfx/skia/skia/src/core/SkMaskFilterBase.cpp new file mode 100644 index 000000000000..cdaff0783acd --- /dev/null +++ b/gfx/skia/skia/src/core/SkMaskFilterBase.cpp @@ -0,0 +1,313 @@ +/* + * Copyright 2025 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "src/core/SkMaskFilterBase.h" + +#include "include/core/SkImageFilter.h" +#include "include/core/SkMatrix.h" +#include "include/core/SkPath.h" +#include "include/core/SkPoint.h" +#include "include/core/SkRect.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkRegion.h" +#include "include/core/SkStrokeRec.h" +#include "include/core/SkTypes.h" +#include "include/private/base/SkTemplates.h" +#include "src/base/SkAutoMalloc.h" +#include "src/core/SkBlitter.h" +#include "src/core/SkCachedData.h" +#include "src/core/SkDraw.h" +#include "src/core/SkMask.h" +#include "src/core/SkPathPriv.h" +#include "src/core/SkRasterClip.h" + +#include +#include +#include +#include + +class SkRRect; + +SkMaskFilterBase::NinePatch::~NinePatch() { + if (fCache) { + SkASSERT((const void*)fMask.fImage == fCache->data()); + fCache->unref(); + } else { + // fMask is about to be destroyed and "owns" its fImage. + SkMaskBuilder::FreeImage(const_cast(fMask.fImage)); + } +} + +bool SkMaskFilterBase::asABlur(BlurRec*) const { + return false; +} + +sk_sp SkMaskFilterBase::asImageFilter(const SkMatrix& ctm) const { + return nullptr; +} + +static SkMask extract_mask_subset(const SkMask& src, SkIRect bounds, int32_t newX, int32_t newY) { + SkASSERT(src.fBounds.contains(bounds)); + + const int dx = bounds.left() - src.fBounds.left(); + const int dy = bounds.top() - src.fBounds.top(); + bounds.offsetTo(newX, newY); + return SkMask(src.fImage + dy * src.fRowBytes + dx, + bounds, + src.fRowBytes, + src.fFormat); +} + +static void blit_clipped_mask(SkBlitter* blitter, const SkMask& mask, + const SkIRect& bounds, const SkIRect& clipR) { + SkIRect r; + if (r.intersect(bounds, clipR)) { + blitter->blitMask(mask, r); + } +} + +static void blit_clipped_rect(SkBlitter* blitter, const SkIRect& rect, const SkIRect& clipR) { + SkIRect r; + if (r.intersect(rect, clipR)) { + blitter->blitRect(r.left(), r.top(), r.width(), r.height()); + } +} + +static void draw_nine_clipped(const SkMask& mask, const SkIRect& outerR, + const SkIPoint& center, bool fillCenter, + const SkIRect& clipR, SkBlitter* blitter) { + const int cx = center.x(); + const int cy = center.y(); + SkIRect bounds; + + // top-left + bounds = mask.fBounds; + bounds.fRight = cx; + bounds.fBottom = cy; + if (bounds.width() > 0 && bounds.height() > 0) { + SkMask m = extract_mask_subset(mask, bounds, outerR.left(), outerR.top()); + blit_clipped_mask(blitter, m, m.fBounds, clipR); + } + + // top-right + bounds = mask.fBounds; + bounds.fLeft = cx + 1; + bounds.fBottom = cy; + if (bounds.width() > 0 && bounds.height() > 0) { + SkMask m = extract_mask_subset(mask, bounds, outerR.right() - bounds.width(), outerR.top()); + blit_clipped_mask(blitter, m, m.fBounds, clipR); + } + + // bottom-left + bounds = mask.fBounds; + bounds.fRight = cx; + bounds.fTop = cy + 1; + if (bounds.width() > 0 && bounds.height() > 0) { + SkMask m = extract_mask_subset(mask, bounds, outerR.left(), outerR.bottom() - bounds.height()); + blit_clipped_mask(blitter, m, m.fBounds, clipR); + } + + // bottom-right + bounds = mask.fBounds; + bounds.fLeft = cx + 1; + bounds.fTop = cy + 1; + if (bounds.width() > 0 && bounds.height() > 0) { + SkMask m = extract_mask_subset(mask, bounds, outerR.right() - bounds.width(), + outerR.bottom() - bounds.height()); + blit_clipped_mask(blitter, m, m.fBounds, clipR); + } + + SkIRect innerR; + innerR.setLTRB(outerR.left() + cx - mask.fBounds.left(), + outerR.top() + cy - mask.fBounds.top(), + outerR.right() + (cx + 1 - mask.fBounds.right()), + outerR.bottom() + (cy + 1 - mask.fBounds.bottom())); + if (fillCenter) { + blit_clipped_rect(blitter, innerR, clipR); + } + + const int innerW = innerR.width(); + size_t storageSize = (innerW + 1) * (sizeof(int16_t) + sizeof(uint8_t)); + SkAutoSMalloc<4*1024> storage(storageSize); + int16_t* runs = (int16_t*)storage.get(); + uint8_t* alpha = (uint8_t*)(runs + innerW + 1); + + SkIRect r; + // top + r.setLTRB(innerR.left(), outerR.top(), innerR.right(), innerR.top()); + if (r.intersect(clipR)) { + const int startY = std::max(0, r.top() - outerR.top()); + const int stopY = startY + r.height(); + const int width = r.width(); + runs[0] = width; + runs[width] = 0; + for (int y = startY; y < stopY; ++y) { + alpha[0] = *mask.getAddr8(cx, mask.fBounds.top() + y); + blitter->blitAntiH(r.left(), outerR.top() + y, alpha, runs); + } + } + // bottom + r.setLTRB(innerR.left(), innerR.bottom(), innerR.right(), outerR.bottom()); + if (r.intersect(clipR)) { + const int startY = outerR.bottom() - r.bottom(); + const int stopY = startY + r.height(); + const int width = r.width(); + runs[0] = width; + runs[width] = 0; + for (int y = startY; y < stopY; ++y) { + alpha[0] = *mask.getAddr8(cx, mask.fBounds.bottom() - y - 1); + blitter->blitAntiH(r.left(), outerR.bottom() - y - 1, alpha, runs); + } + } + // left + r.setLTRB(outerR.left(), innerR.top(), innerR.left(), innerR.bottom()); + if (r.intersect(clipR)) { + SkMask leftMask(mask.getAddr8(mask.fBounds.left() + r.left() - outerR.left(), + mask.fBounds.top() + cy), + r, + 0, // so we repeat the scanline for our height + SkMask::kA8_Format); + blitter->blitMask(leftMask, r); + } + // right + r.setLTRB(innerR.right(), innerR.top(), outerR.right(), innerR.bottom()); + if (r.intersect(clipR)) { + SkMask rightMask(mask.getAddr8(mask.fBounds.right() - outerR.right() + r.left(), + mask.fBounds.top() + cy), + r, + 0, // so we repeat the scanline for our height + SkMask::kA8_Format); + blitter->blitMask(rightMask, r); + } +} + +static void draw_nine(const SkMask& mask, const SkIRect& outerR, const SkIPoint& center, + bool fillCenter, const SkRasterClip& clip, SkBlitter* blitter) { + // if we get here, we need to (possibly) resolve the clip and blitter + SkAAClipBlitterWrapper wrapper(clip, blitter); + blitter = wrapper.getBlitter(); + + SkRegion::Cliperator clipper(wrapper.getRgn(), outerR); + + if (!clipper.done()) { + const SkIRect& cr = clipper.rect(); + do { + draw_nine_clipped(mask, outerR, center, fillCenter, cr, blitter); + clipper.next(); + } while (!clipper.done()); + } +} + +static int countNestedRects(const SkPath& path, SkRect rects[2]) { + if (SkPathPriv::IsNestedFillRects(path, rects)) { + return 2; + } + return path.isRect(&rects[0]); +} + +bool SkMaskFilterBase::filterRRect(const SkRRect& devRRect, const SkMatrix& matrix, + const SkRasterClip& clip, SkBlitter* blitter) const { + // Attempt to speed up drawing by creating a nine patch. If a nine patch + // cannot be used, return false to allow our caller to recover and perform + // the drawing another way. + std::optional patch = this->filterRRectToNine(devRRect, matrix, clip.getBounds()); + + if (!patch.has_value()) { + return false; + } + draw_nine(patch->fMask, patch->fOuterRect, patch->fCenter, true, clip, blitter); + return true; +} + +bool SkMaskFilterBase::filterPath(const SkPath& devPath, const SkMatrix& matrix, + const SkRasterClip& clip, SkBlitter* blitter, + SkStrokeRec::InitStyle style) const { + SkRect rects[2]; + int rectCount = 0; + if (SkStrokeRec::kFill_InitStyle == style) { + rectCount = countNestedRects(devPath, rects); + } + if (rectCount > 0) { + std::optional patch; + + switch (this->filterRectsToNine( + SkSpan(rects, rectCount), matrix, clip.getBounds(), &patch)) { + case FilterReturn::kFalse: + SkASSERT(!patch.has_value()); + return false; + + case FilterReturn::kTrue: + draw_nine(patch->fMask, patch->fOuterRect, patch->fCenter, 1 == rectCount, clip, + blitter); + return true; + + case FilterReturn::kUnimplemented: + SkASSERT(!patch.has_value()); + // fall out + break; + } + } + + SkMaskBuilder srcM, dstM; + +#if defined(SK_BUILD_FOR_FUZZER) + if (devPath.countVerbs() > 1000 || devPath.countPoints() > 1000) { + return false; + } +#endif + if (!SkDraw::DrawToMask(devPath, clip.getBounds(), this, &matrix, &srcM, + SkMaskBuilder::kComputeBoundsAndRenderImage_CreateMode, + style)) { + return false; + } + SkAutoMaskFreeImage autoSrc(srcM.image()); + + if (!this->filterMask(&dstM, srcM, matrix, nullptr)) { + return false; + } + SkAutoMaskFreeImage autoDst(dstM.image()); + + // if we get here, we need to (possibly) resolve the clip and blitter + SkAAClipBlitterWrapper wrapper(clip, blitter); + blitter = wrapper.getBlitter(); + + SkRegion::Cliperator clipper(wrapper.getRgn(), dstM.fBounds); + + if (!clipper.done()) { + const SkIRect& cr = clipper.rect(); + do { + blitter->blitMask(dstM, cr); + clipper.next(); + } while (!clipper.done()); + } + + return true; +} + +std::optional SkMaskFilterBase::filterRRectToNine( + const SkRRect&, const SkMatrix&, const SkIRect&) const { + return std::nullopt; +} + +SkMaskFilterBase::FilterReturn SkMaskFilterBase::filterRectsToNine( + SkSpan, + const SkMatrix&, + const SkIRect&, + std::optional*) const { + return FilterReturn::kUnimplemented; +} + +void SkMaskFilterBase::computeFastBounds(const SkRect& src, SkRect* dst) const { + SkMask srcM(nullptr, src.roundOut(), 0, SkMask::kA8_Format); + SkMaskBuilder dstM; + + SkIPoint margin; // ignored + if (this->filterMask(&dstM, srcM, SkMatrix::I(), &margin)) { + dst->set(dstM.fBounds); + } else { + dst->set(srcM.fBounds); + } +} diff --git a/gfx/skia/skia/src/core/SkMaskFilterBase.h b/gfx/skia/skia/src/core/SkMaskFilterBase.h index b87d507646df..938ee544d90e 100644 --- a/gfx/skia/skia/src/core/SkMaskFilterBase.h +++ b/gfx/skia/skia/src/core/SkMaskFilterBase.h @@ -8,39 +8,27 @@ #ifndef SkMaskFilterBase_DEFINED #define SkMaskFilterBase_DEFINED -#include "include/core/SkBlurTypes.h" #include "include/core/SkFlattenable.h" #include "include/core/SkMaskFilter.h" -#include "include/core/SkPaint.h" +#include "include/core/SkPoint.h" +#include "include/core/SkRect.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkScalar.h" +#include "include/core/SkSpan.h" #include "include/core/SkStrokeRec.h" #include "include/private/base/SkNoncopyable.h" -#include "src/base/SkTLazy.h" #include "src/core/SkMask.h" -class GrClip; -struct GrFPArgs; -class GrFragmentProcessor; -class GrPaint; -class GrRecordingContext; -class GrRenderTarget; -namespace skgpu { -namespace ganesh { -class SurfaceDrawContext; -} -} // namespace skgpu -class GrResourceProvider; -class GrStyledShape; -class GrSurfaceProxyView; -class GrTexture; -class GrTextureProxy; +#include -class SkBitmap; class SkBlitter; +class SkImageFilter; class SkCachedData; class SkMatrix; class SkPath; -class SkRasterClip; class SkRRect; +class SkRasterClip; +enum SkBlurStyle : int; class SkMaskFilterBase : public SkMaskFilter { public: @@ -116,16 +104,17 @@ public: protected: SkMaskFilterBase() {} - enum FilterReturn { - kFalse_FilterReturn, - kTrue_FilterReturn, - kUnimplemented_FilterReturn + enum class FilterReturn { + kFalse, + kTrue, + kUnimplemented, }; - class NinePatch : ::SkNoncopyable { + class NinePatch final : ::SkNoncopyable { public: NinePatch(const SkMask& mask, SkIRect outerRect, SkIPoint center, SkCachedData* cache) : fMask(mask), fOuterRect(outerRect), fCenter(center), fCache(cache) {} + NinePatch(NinePatch&&) = delete; // the transfer of fCache makes this not work ~NinePatch(); SkMask fMask; // fBounds must have [0,0] in its top-left @@ -135,11 +124,16 @@ protected: }; /** + * As an optimization, some filters can be applied to a smaller nine-patch + * instead of the full-sized rectangle. These nine-patches are not only smaller, + * but more re-usable/cacheable. Then, when drawing/blitting, the ninepatch + * can be expanded to the desired size. + * * Override if your subclass can filter a rect, and return the answer as * a ninepatch mask to be stretched over the returned outerRect. On success - * return kTrue_FilterReturn. On failure (e.g. out of memory) return - * kFalse_FilterReturn. If the normal filterMask() entry-point should be - * called (the default) return kUnimplemented_FilterReturn. + * return FilterReturn::kTrue. On failure (e.g. out of memory) return + * FilterReturn::kFalse. If the normal filterMask() entry-point should be + * called (the default) return FilterReturn::kUnimplemented. * * By convention, the caller will take the center rol/col from the returned * mask as the slice it can replicate horizontally and vertically as we @@ -149,16 +143,16 @@ protected: * the caller will call mask.fBounds.centerX() and centerY() to find the * strips that will be replicated. */ - virtual FilterReturn filterRectsToNine(const SkRect[], int count, + virtual FilterReturn filterRectsToNine(SkSpan, const SkMatrix&, const SkIRect& clipBounds, - SkTLazy*) const; + std::optional*) const; /** * Similar to filterRectsToNine, except it performs the work on a round rect. */ - virtual FilterReturn filterRRectToNine(const SkRRect&, const SkMatrix&, - const SkIRect& clipBounds, - SkTLazy*) const; + virtual std::optional filterRRectToNine(const SkRRect&, + const SkMatrix&, + const SkIRect& clipBounds) const; private: friend class SkDraw; @@ -176,10 +170,10 @@ private: mask and then call filterMask(). If this returns true, the specified blitter will be called to render that mask. Returns false if filterMask() returned false. */ - bool filterRRect(const SkRRect& devRRect, const SkMatrix& ctm, const SkRasterClip&, + bool filterRRect(const SkRRect& devRRect, + const SkMatrix& ctm, + const SkRasterClip&, SkBlitter*) const; - - using INHERITED = SkFlattenable; }; inline SkMaskFilterBase* as_MFB(SkMaskFilter* mf) { diff --git a/gfx/skia/skia/src/core/SkMaskGamma.h b/gfx/skia/skia/src/core/SkMaskGamma.h index 1c8e71e94ff8..b39b9804950f 100644 --- a/gfx/skia/skia/src/core/SkMaskGamma.h +++ b/gfx/skia/skia/src/core/SkMaskGamma.h @@ -12,10 +12,10 @@ #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" #include "include/core/SkTypes.h" -#include "include/private/SkColorData.h" #include "include/private/base/SkCPUTypes.h" #include "include/private/base/SkNoncopyable.h" #include "include/private/base/SkTo.h" +#include "src/core/SkColorData.h" #include #include diff --git a/gfx/skia/skia/src/core/SkMatrix.cpp b/gfx/skia/skia/src/core/SkMatrix.cpp index c8766f355fb5..bbb699e627b0 100644 --- a/gfx/skia/skia/src/core/SkMatrix.cpp +++ b/gfx/skia/skia/src/core/SkMatrix.cpp @@ -815,41 +815,53 @@ bool SkMatrix::invertNonIdentity(SkMatrix* inv) const { TypeMask mask = this->getType(); + // Optimized invert for only scale and/or translation matrices. if (0 == (mask & ~(kScale_Mask | kTranslate_Mask))) { - bool invertible = true; - if (inv) { - if (mask & kScale_Mask) { - SkScalar invX = sk_ieee_float_divide(1.f, fMat[kMScaleX]); - SkScalar invY = sk_ieee_float_divide(1.f, fMat[kMScaleY]); - // Denormalized (non-zero) scale factors will overflow when inverted, in which case - // the inverse matrix would not be finite, so return false. - if (!SkIsFinite(invX, invY)) { - return false; - } - - // Must be careful when writing to inv, since it may be the - // same memory as this. + if (mask & kScale_Mask) { + // Scale + (optional) Translate + SkScalar invSX = sk_ieee_float_divide(1.f, fMat[kMScaleX]); + SkScalar invSY = sk_ieee_float_divide(1.f, fMat[kMScaleY]); + // Denormalized (non-zero) scale factors will overflow when inverted, in which case + // the inverse matrix would not be finite, so return false. + if (!SkIsFinite(invSX, invSY)) { + return false; + } + SkScalar invTX = -fMat[kMTransX] * invSX; + SkScalar invTY = -fMat[kMTransY] * invSY; + // Make sure inverse translation didn't overflow/underflow after dividing by scale. + // Also catches cases where the original matrix's translation values are not finite. + if (!SkIsFinite(invTX, invTY)) { + return false; + } + // Must be careful when writing to inv, since it may be the + // same memory as this. + if (inv) { inv->fMat[kMSkewX] = inv->fMat[kMSkewY] = inv->fMat[kMPersp0] = inv->fMat[kMPersp1] = 0; - inv->fMat[kMScaleX] = invX; - inv->fMat[kMScaleY] = invY; + inv->fMat[kMScaleX] = invSX; + inv->fMat[kMScaleY] = invSY; inv->fMat[kMPersp2] = 1; - inv->fMat[kMTransX] = -fMat[kMTransX] * invX; - inv->fMat[kMTransY] = -fMat[kMTransY] * invY; + inv->fMat[kMTransX] = invTX; + inv->fMat[kMTransY] = invTY; inv->setTypeMask(mask | kRectStaysRect_Mask); - } else { - // translate only - inv->setTranslate(-fMat[kMTransX], -fMat[kMTransY]); - } - } else { // inv is nullptr, just check if we're invertible - if (!fMat[kMScaleX] || !fMat[kMScaleY]) { - invertible = false; } + + return true; } - return invertible; + + // Translate-only + if (!SkIsFinite(fMat[kMTransX], fMat[kMTransY])) { + // Translation components aren't finite, so inverse isn't possible + return false; + } + + if (inv) { + inv->setTranslate(-fMat[kMTransX], -fMat[kMTransY]); + } + return true; } int isPersp = mask & kPerspective_Mask; diff --git a/gfx/skia/skia/src/core/SkMipmapHQDownSampler.cpp b/gfx/skia/skia/src/core/SkMipmapHQDownSampler.cpp index 5c099c1f13bf..5e921b14c406 100644 --- a/gfx/skia/skia/src/core/SkMipmapHQDownSampler.cpp +++ b/gfx/skia/skia/src/core/SkMipmapHQDownSampler.cpp @@ -9,9 +9,10 @@ #ifndef SK_USE_DRAWING_MIPMAP_DOWNSAMPLER -#include "include/private/SkColorData.h" +#include "include/private/base/SkFloatingPoint.h" #include "src/base/SkHalf.h" #include "src/base/SkVx.h" +#include "src/core/SkColorData.h" #include "src/core/SkMipmap.h" namespace { diff --git a/gfx/skia/skia/src/core/SkOpts.cpp b/gfx/skia/skia/src/core/SkOpts.cpp index c09303e04a45..6b48eb6357ad 100644 --- a/gfx/skia/skia/src/core/SkOpts.cpp +++ b/gfx/skia/skia/src/core/SkOpts.cpp @@ -29,7 +29,7 @@ namespace SkOpts { StageFn ops_highp[] = { SK_RASTER_PIPELINE_OPS_ALL(M) }; StageFn just_return_highp = (StageFn)SK_OPTS_NS::just_return; void (*start_pipeline_highp)(size_t, size_t, size_t, size_t, SkRasterPipelineStage*, - SkSpan, + SkSpan, uint8_t*) = SK_OPTS_NS::start_pipeline; #undef M @@ -38,7 +38,7 @@ namespace SkOpts { StageFn ops_lowp[] = { SK_RASTER_PIPELINE_OPS_LOWP(M) }; StageFn just_return_lowp = (StageFn)SK_OPTS_NS::lowp::just_return; void (*start_pipeline_lowp)(size_t, size_t, size_t, size_t, SkRasterPipelineStage*, - SkSpan, + SkSpan, uint8_t*) = SK_OPTS_NS::lowp::start_pipeline; #undef M diff --git a/gfx/skia/skia/src/core/SkOpts.h b/gfx/skia/skia/src/core/SkOpts.h index afdbd168bccd..5ab2bbbaa664 100644 --- a/gfx/skia/skia/src/core/SkOpts.h +++ b/gfx/skia/skia/src/core/SkOpts.h @@ -14,7 +14,9 @@ #include #include -struct SkRasterPipeline_MemoryCtxPatch; +namespace SkRasterPipelineContexts { +struct MemoryCtxPatch; +} /** * SkOpts (short for SkOptimizations) is a mechanism where we can ship with multiple implementations @@ -75,10 +77,10 @@ namespace SkOpts { extern StageFn ops_lowp [kNumRasterPipelineLowpOps ], just_return_lowp; extern void (*start_pipeline_highp)(size_t,size_t,size_t,size_t, SkRasterPipelineStage*, - SkSpan, + SkSpan, uint8_t*); extern void (*start_pipeline_lowp )(size_t,size_t,size_t,size_t, SkRasterPipelineStage*, - SkSpan, + SkSpan, uint8_t*); extern size_t raster_pipeline_lowp_stride; diff --git a/gfx/skia/skia/src/core/SkPaint.cpp b/gfx/skia/skia/src/core/SkPaint.cpp index 3e9037b7a2b1..7f703d02bb8c 100644 --- a/gfx/skia/skia/src/core/SkPaint.cpp +++ b/gfx/skia/skia/src/core/SkPaint.cpp @@ -123,7 +123,7 @@ void SkPaint::setColor(SkColor color) { void SkPaint::setColor(const SkColor4f& color, SkColorSpace* colorSpace) { SkColorSpaceXformSteps steps{colorSpace, kUnpremul_SkAlphaType, sk_srgb_singleton(), kUnpremul_SkAlphaType}; - fColor4f = {color.fR, color.fG, color.fB, SkTPin(color.fA, 0.0f, 1.0f)}; + fColor4f = color.pinAlpha(); steps.apply(fColor4f.vec()); } diff --git a/gfx/skia/skia/src/core/SkPath.cpp b/gfx/skia/skia/src/core/SkPath.cpp index f30995fc08eb..f6f00972f1dd 100644 --- a/gfx/skia/skia/src/core/SkPath.cpp +++ b/gfx/skia/skia/src/core/SkPath.cpp @@ -3459,7 +3459,7 @@ bool SkPath::IsCubicDegenerate(const SkPoint& p1, const SkPoint& p2, ////////////////////////////////////////////////////////////////////////////////////////////////// -SkPathVerbAnalysis sk_path_analyze_verbs(const uint8_t vbs[], int verbCount) { +SkPathVerbAnalysis SkPathPriv::AnalyzeVerbs(const uint8_t vbs[], int verbCount) { SkPathVerbAnalysis info = {false, 0, 0, 0}; bool needMove = true; bool invalid = false; @@ -3521,7 +3521,7 @@ SkPath SkPath::Make(const SkPoint pts[], int pointCount, return SkPath(); } - const auto info = sk_path_analyze_verbs(vbs, verbCount); + const auto info = SkPathPriv::AnalyzeVerbs(vbs, verbCount); if (!info.valid || info.points > pointCount || info.weights > wCount) { SkDEBUGFAIL("invalid verbs and number of points/weights"); return SkPath(); diff --git a/gfx/skia/skia/src/core/SkPathPriv.h b/gfx/skia/skia/src/core/SkPathPriv.h index 7bdbe95a6c70..c8a03ed3c122 100644 --- a/gfx/skia/skia/src/core/SkPathPriv.h +++ b/gfx/skia/skia/src/core/SkPathPriv.h @@ -34,8 +34,17 @@ static_assert(1 == static_cast(SkPathFillType::kEvenOdd), "fill_type_mismat static_assert(2 == static_cast(SkPathFillType::kInverseWinding), "fill_type_mismatch"); static_assert(3 == static_cast(SkPathFillType::kInverseEvenOdd), "fill_type_mismatch"); +// These are computed from a stream of verbs +struct SkPathVerbAnalysis { + int points, weights; + unsigned segmentMask; + bool valid; +}; + class SkPathPriv { public: + static SkPathVerbAnalysis AnalyzeVerbs(const uint8_t verbs[], int count); + // skbug.com/9906: Not a perfect solution for W plane clipping, but 1/16384 is a // reasonable limit (roughly 5e-5) inline static constexpr SkScalar kW0PlaneDistance = 1.f / (1 << 14); @@ -143,7 +152,7 @@ public: Verbs(const SkPath& path) : fPathRef(path.fPathRef.get()) {} struct Iter { void operator++() { fVerb++; } - bool operator!=(const Iter& b) { return fVerb != b.fVerb; } + bool operator!=(const Iter& b) const { return fVerb != b.fVerb; } SkPath::Verb operator*() { return static_cast(*fVerb); } const uint8_t* fVerb; }; diff --git a/gfx/skia/skia/src/core/SkPathRef.cpp b/gfx/skia/skia/src/core/SkPathRef.cpp index ceacb83ea743..91e3ec8b1d2f 100644 --- a/gfx/skia/skia/src/core/SkPathRef.cpp +++ b/gfx/skia/skia/src/core/SkPathRef.cpp @@ -13,6 +13,7 @@ #include "include/private/base/SkFloatingPoint.h" #include "include/private/base/SkOnce.h" #include "src/base/SkVx.h" +#include "src/core/SkPathPriv.h" #include #include @@ -690,7 +691,7 @@ void SkPathRef::reset() { } bool SkPathRef::dataMatchesVerbs() const { - const auto info = sk_path_analyze_verbs(fVerbs.begin(), fVerbs.size()); + const auto info = SkPathPriv::AnalyzeVerbs(fVerbs.begin(), fVerbs.size()); return info.valid && info.segmentMask == fSegmentMask && info.points == fPoints.size() && diff --git a/gfx/skia/skia/src/core/SkPath_serial.cpp b/gfx/skia/skia/src/core/SkPath_serial.cpp index fccf8202af9d..dc13192602ba 100644 --- a/gfx/skia/skia/src/core/SkPath_serial.cpp +++ b/gfx/skia/skia/src/core/SkPath_serial.cpp @@ -249,7 +249,7 @@ size_t SkPath::readFromMemory(const void* storage, size_t length) { verbs = tmpVerbs; } - SkPathVerbAnalysis analysis = sk_path_analyze_verbs(verbs, counts.vbs); + SkPathVerbAnalysis analysis = SkPathPriv::AnalyzeVerbs(verbs, counts.vbs); if (!analysis.valid || analysis.points != counts.pts || analysis.weights != counts.cnx) { return 0; } diff --git a/gfx/skia/skia/src/core/SkPicturePriv.h b/gfx/skia/skia/src/core/SkPicturePriv.h index b277e22e5d35..763a6bacf5a1 100644 --- a/gfx/skia/skia/src/core/SkPicturePriv.h +++ b/gfx/skia/skia/src/core/SkPicturePriv.h @@ -123,6 +123,7 @@ public: // v104: SaveLayer supports multiple image filters // v105: Unclamped matrix color filter // v106: SaveLayer supports custom backdrop tile modes + // v107: Combine SkColorShader and SkColorShader4 enum Version { kPictureShaderFilterParam_Version = 82, @@ -150,6 +151,8 @@ public: kMultipleFiltersOnSaveLayer = 104, kUnclampedMatrixColorFilter = 105, kSaveLayerBackdropTileMode = 106, + kCombineColorShaders = 107, + kSerializeStableKeys = 108, // Only SKPs within the min/current picture version range (inclusive) can be read. // @@ -174,7 +177,7 @@ public: // // Contact the Infra Gardener if the above steps do not work for you. kMin_Version = kPictureShaderFilterParam_Version, - kCurrent_Version = kSaveLayerBackdropTileMode + kCurrent_Version = kSerializeStableKeys }; }; diff --git a/gfx/skia/skia/src/core/SkPixmap.cpp b/gfx/skia/skia/src/core/SkPixmap.cpp index ca25a5ffdaa9..d9a13e29ff60 100644 --- a/gfx/skia/skia/src/core/SkPixmap.cpp +++ b/gfx/skia/skia/src/core/SkPixmap.cpp @@ -8,15 +8,15 @@ #include "include/core/SkPixmap.h" #include "include/core/SkAlphaType.h" -#include "include/core/SkColorPriv.h" #include "include/core/SkColorSpace.h" #include "include/core/SkColorType.h" #include "include/core/SkUnPreMultiply.h" -#include "include/private/SkColorData.h" #include "include/private/base/SkFloatingPoint.h" #include "include/private/base/SkTPin.h" #include "src/base/SkHalf.h" #include "src/base/SkVx.h" +#include "src/core/SkColorData.h" +#include "src/core/SkColorPriv.h" #include "src/core/SkConvertPixels.h" #include "src/core/SkImageInfoPriv.h" #include "src/core/SkMask.h" diff --git a/gfx/skia/skia/src/core/SkRRect.cpp b/gfx/skia/skia/src/core/SkRRect.cpp index 226a8b19de3d..24a13dab0fd1 100644 --- a/gfx/skia/skia/src/core/SkRRect.cpp +++ b/gfx/skia/skia/src/core/SkRRect.cpp @@ -554,13 +554,13 @@ bool SkRRect::transform(const SkMatrix& matrix, SkRRect* dst) const { swap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerRight_Corner]); } + dst->scaleRadii(); + if (!AreRectAndRadiiValid(dst->fRect, dst->fRadii)) { return false; } - dst->scaleRadii(); - dst->isValid(); // TODO: is this meant to be SkASSERT(dst->isValid())? - + SkASSERT(dst->isValid()); return true; } diff --git a/gfx/skia/skia/src/core/SkRasterPipeline.cpp b/gfx/skia/skia/src/core/SkRasterPipeline.cpp index 93f8f7c18648..b24978eab9fb 100644 --- a/gfx/skia/skia/src/core/SkRasterPipeline.cpp +++ b/gfx/skia/skia/src/core/SkRasterPipeline.cpp @@ -99,6 +99,20 @@ void SkRasterPipeline::uncheckedAppend(SkRasterPipelineOp op, void* ctx) { #undef COLOR_TYPE_CASE + case Op::debug_r: + case Op::debug_g: + case Op::debug_b: + case Op::debug_a: + case Op::debug_r_255: + case Op::debug_g_255: + case Op::debug_b_255: + case Op::debug_a_255: + case Op::debug_x: + case Op::debug_y: { + ct = kRGBA_8888_SkColorType; + isStore = true; + break; + } // Odd stage that doesn't have a load variant (appendLoad uses load_a8 + alpha_to_red) case Op::store_r8: { ct = kR8_unorm_SkColorType; @@ -125,7 +139,8 @@ void SkRasterPipeline::uncheckedAppend(SkRasterPipelineOp op, void* ctx) { } case Op::emboss: { // Special-case, this op uses a context that holds *two* MemoryCtxs - SkRasterPipeline_EmbossCtx* embossCtx = (SkRasterPipeline_EmbossCtx*)ctx; + SkRasterPipelineContexts::EmbossCtx* embossCtx = + (SkRasterPipelineContexts::EmbossCtx*)ctx; this->addMemoryContext(&embossCtx->add, SkColorTypeBytesPerPixel(kAlpha_8_SkColorType), /*load=*/true, /*store=*/false); @@ -135,12 +150,12 @@ void SkRasterPipeline::uncheckedAppend(SkRasterPipelineOp op, void* ctx) { break; } case Op::init_lane_masks: { - auto* initCtx = (SkRasterPipeline_InitLaneMasksCtx*)ctx; + auto* initCtx = (SkRasterPipelineContexts::InitLaneMasksCtx*)ctx; initCtx->tail = this->tailPointer(); break; } case Op::branch_if_all_lanes_active: { - auto* branchCtx = (SkRasterPipeline_BranchIfAllLanesActiveCtx*)ctx; + auto* branchCtx = (SkRasterPipelineContexts::BranchIfAllLanesActiveCtx*)ctx; branchCtx->tail = this->tailPointer(); break; } @@ -153,8 +168,10 @@ void SkRasterPipeline::uncheckedAppend(SkRasterPipelineOp op, void* ctx) { if (isLoad || isStore) { SkASSERT(ct != kUnknown_SkColorType); - this->addMemoryContext( - (SkRasterPipeline_MemoryCtx*)ctx, SkColorTypeBytesPerPixel(ct), isLoad, isStore); + this->addMemoryContext((SkRasterPipelineContexts::MemoryCtx*)ctx, + SkColorTypeBytesPerPixel(ct), + isLoad, + isStore); } } @@ -172,7 +189,7 @@ void SkRasterPipeline::extend(const SkRasterPipeline& src) { // we need to keep it, since we already have rewind ops that reference it. Either way, we need // to rewrite all the rewind ops to point to _our_ rewind context; we only get that checkpoint. if (src.fRewindCtx && !fRewindCtx) { - fRewindCtx = fAlloc->make(); + fRewindCtx = fAlloc->make(); } auto stages = fAlloc->makeArrayDefault(src.fNumStages); @@ -189,12 +206,12 @@ void SkRasterPipeline::extend(const SkRasterPipeline& src) { break; } case Op::init_lane_masks: { - auto* ctx = (SkRasterPipeline_InitLaneMasksCtx*)stages[n].ctx; + auto* ctx = (SkRasterPipelineContexts::InitLaneMasksCtx*)stages[n].ctx; ctx->tail = this->tailPointer(); break; } case Op::branch_if_all_lanes_active: { - auto* ctx = (SkRasterPipeline_BranchIfAllLanesActiveCtx*)stages[n].ctx; + auto* ctx = (SkRasterPipelineContexts::BranchIfAllLanesActiveCtx*)stages[n].ctx; ctx->tail = this->tailPointer(); break; } @@ -209,7 +226,7 @@ void SkRasterPipeline::extend(const SkRasterPipeline& src) { fStages = &stages[src.fNumStages - 1]; fNumStages += src.fNumStages; - for (const SkRasterPipeline_MemoryCtxInfo& info : src.fMemoryCtxInfos) { + for (const SkRasterPipelineContexts::MemoryCtxInfo& info : src.fMemoryCtxInfos) { this->addMemoryContext(info.context, info.bytesPerPixel, info.load, info.store); } } @@ -263,7 +280,7 @@ void SkRasterPipeline::appendConstantColor(SkArenaAlloc* alloc, const float rgba } else if (rgba[0] == 1 && rgba[1] == 1 && rgba[2] == 1 && rgba[3] == 1) { this->append(Op::white_color); } else { - auto ctx = alloc->make(); + auto ctx = alloc->make(); skvx::float4 color = skvx::float4::Load(rgba); color.store(&ctx->r); @@ -316,7 +333,7 @@ void SkRasterPipeline::appendMatrix(SkArenaAlloc* alloc, const SkMatrix& matrix) } } -void SkRasterPipeline::appendLoad(SkColorType ct, const SkRasterPipeline_MemoryCtx* ctx) { +void SkRasterPipeline::appendLoad(SkColorType ct, const SkRasterPipelineContexts::MemoryCtx* ctx) { switch (ct) { case kUnknown_SkColorType: SkASSERT(false); break; @@ -384,7 +401,8 @@ void SkRasterPipeline::appendLoad(SkColorType ct, const SkRasterPipeline_MemoryC } } -void SkRasterPipeline::appendLoadDst(SkColorType ct, const SkRasterPipeline_MemoryCtx* ctx) { +void SkRasterPipeline::appendLoadDst(SkColorType ct, + const SkRasterPipelineContexts::MemoryCtx* ctx) { switch (ct) { case kUnknown_SkColorType: SkASSERT(false); break; @@ -455,7 +473,7 @@ void SkRasterPipeline::appendLoadDst(SkColorType ct, const SkRasterPipeline_Memo } } -void SkRasterPipeline::appendStore(SkColorType ct, const SkRasterPipeline_MemoryCtx* ctx) { +void SkRasterPipeline::appendStore(SkColorType ct, const SkRasterPipelineContexts::MemoryCtx* ctx) { switch (ct) { case kUnknown_SkColorType: SkASSERT(false); break; @@ -548,7 +566,7 @@ void SkRasterPipeline::appendClampIfNormalized(const SkImageInfo& info) { void SkRasterPipeline::appendStackRewind() { if (!fRewindCtx) { - fRewindCtx = fAlloc->make(); + fRewindCtx = fAlloc->make(); } this->uncheckedAppend(Op::stack_rewind, fRewindCtx); } @@ -627,7 +645,7 @@ void SkRasterPipeline::run(size_t x, size_t y, size_t w, size_t h) const { AutoSTMalloc<32, SkRasterPipelineStage> program(stagesNeeded); int numMemoryCtxs = fMemoryCtxInfos.size(); - AutoSTMalloc<2, SkRasterPipeline_MemoryCtxPatch> patches(numMemoryCtxs); + AutoSTMalloc<2, SkRasterPipelineContexts::MemoryCtxPatch> patches(numMemoryCtxs); for (int i = 0; i < numMemoryCtxs; ++i) { patches[i].info = fMemoryCtxInfos[i]; patches[i].backup = nullptr; @@ -650,8 +668,8 @@ std::function SkRasterPipeline::compile() SkRasterPipelineStage* program = fAlloc->makeArray(stagesNeeded); int numMemoryCtxs = fMemoryCtxInfos.size(); - SkRasterPipeline_MemoryCtxPatch* patches = - fAlloc->makeArray(numMemoryCtxs); + SkRasterPipelineContexts::MemoryCtxPatch* patches = + fAlloc->makeArray(numMemoryCtxs); for (int i = 0; i < numMemoryCtxs; ++i) { patches[i].info = fMemoryCtxInfos[i]; patches[i].backup = nullptr; @@ -667,18 +685,20 @@ std::function SkRasterPipeline::compile() }; } -void SkRasterPipeline::addMemoryContext(SkRasterPipeline_MemoryCtx* ctx, +void SkRasterPipeline::addMemoryContext(SkRasterPipelineContexts::MemoryCtx* ctx, int bytesPerPixel, bool load, bool store) { - SkRasterPipeline_MemoryCtxInfo* info = - std::find_if(fMemoryCtxInfos.begin(), fMemoryCtxInfos.end(), - [=](const SkRasterPipeline_MemoryCtxInfo& i) { return i.context == ctx; }); + SkRasterPipelineContexts::MemoryCtxInfo* info = std::find_if( + fMemoryCtxInfos.begin(), + fMemoryCtxInfos.end(), + [=](const SkRasterPipelineContexts::MemoryCtxInfo& i) { return i.context == ctx; }); if (info != fMemoryCtxInfos.end()) { SkASSERT(bytesPerPixel == info->bytesPerPixel); info->load = info->load || load; info->store = info->store || store; } else { - fMemoryCtxInfos.push_back(SkRasterPipeline_MemoryCtxInfo{ctx, bytesPerPixel, load, store}); + fMemoryCtxInfos.push_back( + SkRasterPipelineContexts::MemoryCtxInfo{ctx, bytesPerPixel, load, store}); } } diff --git a/gfx/skia/skia/src/core/SkRasterPipeline.h b/gfx/skia/skia/src/core/SkRasterPipeline.h index 2c03b0d0df6f..2a2fd2cc4233 100644 --- a/gfx/skia/skia/src/core/SkRasterPipeline.h +++ b/gfx/skia/skia/src/core/SkRasterPipeline.h @@ -62,8 +62,8 @@ struct SkRasterPipelineStage { // `ctx` holds data used by the stage function. // Most context structures are declared in SkRasterPipelineOpContexts.h, and have names ending - // in Ctx (e.g. "SkRasterPipeline_SamplerCtx"). Some Raster Pipeline stages pack non-pointer - // data into this field using `SkRPCtxUtils::Pack`. + // in Ctx (e.g. "SkRasterPipelineContexts::SamplerCtx"). Some Raster Pipeline stages pack + // non-pointer data into this field using `SkRPCtxUtils::Pack`. void* ctx; }; SK_END_REQUIRE_DENSE @@ -126,9 +126,9 @@ public: this->appendSetRGB(alloc, color.vec()); } - void appendLoad (SkColorType, const SkRasterPipeline_MemoryCtx*); - void appendLoadDst(SkColorType, const SkRasterPipeline_MemoryCtx*); - void appendStore (SkColorType, const SkRasterPipeline_MemoryCtx*); + void appendLoad(SkColorType, const SkRasterPipelineContexts::MemoryCtx*); + void appendLoadDst(SkColorType, const SkRasterPipelineContexts::MemoryCtx*); + void appendStore(SkColorType, const SkRasterPipelineContexts::MemoryCtx*); void appendClampIfNormalized(const SkImageInfo&); @@ -144,25 +144,29 @@ private: using StartPipelineFn = void (*)(size_t, size_t, size_t, size_t, SkRasterPipelineStage* program, - SkSpan, + SkSpan, uint8_t*); StartPipelineFn buildPipeline(SkRasterPipelineStage*) const; void uncheckedAppend(SkRasterPipelineOp, void*); int stagesNeeded() const; - void addMemoryContext(SkRasterPipeline_MemoryCtx*, int bytesPerPixel, bool load, bool store); + void addMemoryContext(SkRasterPipelineContexts::MemoryCtx*, + int bytesPerPixel, + bool load, + bool store); uint8_t* tailPointer(); - SkArenaAlloc* fAlloc; - SkRasterPipeline_RewindCtx* fRewindCtx; + SkArenaAlloc* fAlloc; + SkRasterPipelineContexts::RewindCtx* fRewindCtx; + // A linked list of stages. fStages and getStageList() return the latest stage. StageList* fStages; uint8_t* fTailPointer; int fNumStages; // Only 1 in 2 million CPU-backend pipelines used more than two MemoryCtxs. // (See the comment in SkRasterPipelineOpContexts.h for how MemoryCtx patching works) - skia_private::STArray<2, SkRasterPipeline_MemoryCtxInfo> fMemoryCtxInfos; + skia_private::STArray<2, SkRasterPipelineContexts::MemoryCtxInfo> fMemoryCtxInfos; }; template diff --git a/gfx/skia/skia/src/core/SkRasterPipelineBlitter.cpp b/gfx/skia/skia/src/core/SkRasterPipelineBlitter.cpp index c60420536ea0..0cf84c750887 100644 --- a/gfx/skia/skia/src/core/SkRasterPipelineBlitter.cpp +++ b/gfx/skia/skia/src/core/SkRasterPipelineBlitter.cpp @@ -6,6 +6,7 @@ */ #include "include/core/SkAlphaType.h" +#include "include/core/SkBitmap.h" #include "include/core/SkBlendMode.h" #include "include/core/SkBlender.h" #include "include/core/SkColor.h" @@ -19,6 +20,7 @@ #include "include/core/SkSurfaceProps.h" #include "include/private/base/SkAssert.h" #include "include/private/base/SkCPUTypes.h" +#include "include/private/base/SkOnce.h" #include "include/private/base/SkTemplates.h" #include "src/base/SkArenaAlloc.h" #include "src/core/SkBlendModePriv.h" @@ -26,20 +28,24 @@ #include "src/core/SkBlitter.h" #include "src/core/SkColorSpacePriv.h" #include "src/core/SkColorSpaceXformSteps.h" +#include "src/core/SkCoreBlitters.h" #include "src/core/SkEffectPriv.h" #include "src/core/SkMask.h" #include "src/core/SkMemset.h" #include "src/core/SkRasterPipeline.h" #include "src/core/SkRasterPipelineOpContexts.h" #include "src/core/SkRasterPipelineOpList.h" +#include "src/core/SkRasterPipelineVizualizer.h" #include "src/effects/colorfilters/SkColorFilterBase.h" #include "src/shaders/SkShaderBase.h" +#include #include #include #include #include #include +#include class SkColorSpace; class SkShader; @@ -73,7 +79,6 @@ public: void blitV (int x, int y, int height, SkAlpha alpha) override; private: - void blitRectWithTrace(int x, int y, int w, int h, bool trace); void appendLoadDst (SkRasterPipeline*) const; void appendStore (SkRasterPipeline*) const; @@ -90,10 +95,10 @@ private: // set to pipeline storage (for alpha) if we have a clipShader void* fClipShaderBuffer = nullptr; // "native" : float or U16 - SkRasterPipeline_MemoryCtx + SkRasterPipelineContexts::MemoryCtx fDstPtr = {nullptr,0}, // Always points to the top-left of fDst. fMaskPtr = {nullptr,0}; // Updated each call to blitMask(). - SkRasterPipeline_EmbossCtx fEmbossCtx; // Used only for k3D_Format masks. + SkRasterPipelineContexts::EmbossCtx fEmbossCtx; // Used only for k3D_Format masks. // We may be able to specialize blitH() or blitRect() into a memset. void (*fMemset2D)(SkPixmap*, int x,int y, int w,int h, uint64_t color) = nullptr; @@ -121,45 +126,134 @@ static SkColor4f paint_color_to_dst(const SkPaint& paint, const SkPixmap& dst) { return paintColor; } +static bool create_pipeline_for_blitter(const SkPixmap& dst, + const SkPaint& paint, + const SkMatrix& ctm, + SkArenaAlloc* alloc, + const SkSurfaceProps& props, + SkRasterPipeline* shaderPipeline, + SkColor4f* dstPaintColor, + bool* isOpaqueOut, + bool* isConstantOut) { + *dstPaintColor = paint_color_to_dst(paint, dst); + + auto shader = as_SB(paint.getShader()); + + if (!shader) { + // Having no shader makes things nice and easy... just use the paint color + shaderPipeline->appendConstantColor(alloc, dstPaintColor->premul().vec()); + *isOpaqueOut = dstPaintColor->fA == 1.0f; + *isConstantOut = true; + return true; + } + + SkColorSpace* dstCS = dst.colorSpace(); + SkColorType dstCT = dst.colorType(); + *isOpaqueOut = shader->isOpaque() && dstPaintColor->fA == 1.0f; + *isConstantOut = shader->isConstant(); + if (shader->appendRootStages( + SkStageRec{shaderPipeline, alloc, dstCT, dstCS, *dstPaintColor, props}, ctm)) { + if (dstPaintColor->fA != 1.0f) { + shaderPipeline->append(SkRasterPipelineOp::scale_1_float, + alloc->make(dstPaintColor->fA)); + } + return true; + } + // The shader can't draw with SkRasterPipeline. + return false; +} + SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap& dst, const SkPaint& paint, const SkMatrix& ctm, SkArenaAlloc* alloc, sk_sp clipShader, const SkSurfaceProps& props) { - SkColorSpace* dstCS = dst.colorSpace(); - SkColorType dstCT = dst.colorType(); - SkColor4f dstPaintColor = paint_color_to_dst(paint, dst); - - auto shader = as_SB(paint.getShader()); - SkRasterPipeline_<256> shaderPipeline; - if (!shader) { - // Having no shader makes things nice and easy... just use the paint color - shaderPipeline.appendConstantColor(alloc, dstPaintColor.premul().vec()); - bool is_opaque = dstPaintColor.fA == 1.0f, - is_constant = true; - return SkRasterPipelineBlitter::Create(dst, paint, dstPaintColor, alloc, shaderPipeline, - is_opaque, is_constant, clipShader.get()); + SkColor4f dstPaintColor; + bool is_opaque, is_constant; + if (!create_pipeline_for_blitter(dst, + paint, + ctm, + alloc, + props, + &shaderPipeline, + &dstPaintColor, + &is_opaque, + &is_constant)) { + return nullptr; } - bool is_opaque = shader->isOpaque() && dstPaintColor.fA == 1.0f; - bool is_constant = shader->isConstant(); - - if (shader->appendRootStages({&shaderPipeline, alloc, dstCT, dstCS, dstPaintColor, props}, - ctm)) { - if (dstPaintColor.fA != 1.0f) { - shaderPipeline.append(SkRasterPipelineOp::scale_1_float, - alloc->make(dstPaintColor.fA)); - } - return SkRasterPipelineBlitter::Create(dst, paint, dstPaintColor, alloc, shaderPipeline, - is_opaque, is_constant, clipShader.get()); - } - - // The shader can't draw with SkRasterPipeline. - return nullptr; + return SkRasterPipelineBlitter::Create(dst, + paint, + dstPaintColor, + alloc, + shaderPipeline, + is_opaque, + is_constant, + clipShader.get()); } +namespace SkRasterPipelineVisualizer { + +SkBlitter* CreateBlitter(const SkPixmap& dst, + const std::vector& debugStages, + const SkPaint& paint, + const SkMatrix& ctm, + SkArenaAlloc* alloc, + sk_sp clipShader, + const SkSurfaceProps& props) { + SkRasterPipeline_<256> shaderPipeline; + SkColor4f dstPaintColor; + bool is_opaque, is_constant; + if (!create_pipeline_for_blitter(dst, + paint, + ctm, + alloc, + props, + &shaderPipeline, + &dstPaintColor, + &is_opaque, + &is_constant)) { + return nullptr; + } + + static SkOnce once; + once([&] { shaderPipeline.dump(); }); + + std::vector stages; + for (auto st = shaderPipeline.getStageList(); st; st = st->prev) { + stages.push_back(*st); + } + std::reverse(stages.begin(), stages.end()); + + SkASSERT_RELEASE(stages.size() == debugStages.size()); + SkRasterPipeline_<256> shaderPipeline2; + + for (size_t i = 0; i < stages.size(); i++) { + shaderPipeline2.append(stages[i].stage, stages[i].ctx); + auto stage = debugStages[i]; + SkASSERT_RELEASE(stage.panels.size() == stage.ops.size()); + for (size_t j = 0; j < stage.panels.size() && j < stage.ops.size(); j++) { + auto panelCtx = alloc->make(); + panelCtx->pixels = stage.panels[j].pixmap().writable_addr(); + panelCtx->stride = stage.panels[j].pixmap().rowBytesAsPixels(); + shaderPipeline2.append(stage.ops[j], panelCtx); + } + } + + return SkRasterPipelineBlitter::Create(dst, + paint, + dstPaintColor, + alloc, + shaderPipeline2, + is_opaque, + is_constant, + clipShader.get()); +} + +} // namespace SkRasterPipelineVisualizer + SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap& dst, const SkPaint& paint, const SkRasterPipeline& shaderPipeline, @@ -202,7 +296,7 @@ SkBlitter* SkRasterPipelineBlitter::Create(const SkPixmap& dst, if (as_SB(clipShader)->appendRootStages(rec, SkMatrix::I())) { struct Storage { // large enough for highp (float) or lowp(U16) - float fA[SkRasterPipeline_kMaxStride]; + float fA[SkRasterPipelineContexts::kMaxStride]; }; auto storage = alloc->make(); clipP->append(SkRasterPipelineOp::store_src_a, storage->fA); @@ -278,7 +372,7 @@ SkBlitter* SkRasterPipelineBlitter::Create(const SkPixmap& dst, // Optimization: A pipeline that's still constant here can collapse back into a constant color. if (is_constant) { SkColor4f constantColor; - SkRasterPipeline_MemoryCtx constantColorPtr = { &constantColor, 0 }; + SkRasterPipelineContexts::MemoryCtx constantColorPtr = {&constantColor, 0}; // We could remove this clamp entirely, but if the destination is 8888, doing the clamp // here allows the color pipeline to still run in lowp (we'll use uniform_color, rather than // unbounded_uniform_color). @@ -312,7 +406,7 @@ SkBlitter* SkRasterPipelineBlitter::Create(const SkPixmap& dst, // Not all blits can memset, so we need to keep colorPipeline too. SkRasterPipeline_<256> p; p.extend(*colorPipeline); - blitter->fDstPtr = SkRasterPipeline_MemoryCtx{&blitter->fMemsetColor, 0}; + blitter->fDstPtr = SkRasterPipelineContexts::MemoryCtx{&blitter->fMemsetColor, 0}; blitter->appendStore(&p); p.run(0,0,1,1); @@ -351,7 +445,7 @@ SkBlitter* SkRasterPipelineBlitter::Create(const SkPixmap& dst, blitter->fBlendMode = as_BB(blender)->asBlendMode(); } - blitter->fDstPtr = SkRasterPipeline_MemoryCtx{ + blitter->fDstPtr = SkRasterPipelineContexts::MemoryCtx{ blitter->fDst.writable_addr(), blitter->fDst.rowBytesAsPixels(), }; @@ -390,10 +484,6 @@ void SkRasterPipelineBlitter::blitH(int x, int y, int w) { } void SkRasterPipelineBlitter::blitRect(int x, int y, int w, int h) { - this->blitRectWithTrace(x, y, w, h, true); -} - -void SkRasterPipelineBlitter::blitRectWithTrace(int x, int y, int w, int h, bool trace) { if (fMemset2D) { fMemset2D(&fDst, x,y, w,h, fMemsetColor); return; @@ -455,8 +545,8 @@ void SkRasterPipelineBlitter::blitAntiH(int x, int y, const SkAlpha aa[], const for (int16_t run = *runs; run > 0; run = *runs) { switch (*aa) { - case 0x00: break; - case 0xff:this->blitRectWithTrace(x,y,run, 1, false); break; + case 0x00: break; + case 0xff: this->blitRect(x,y,run, 1); break; default: fCurrentCoverage = *aa * (1/255.0f); fBlitAntiH(x,y,run,1); @@ -500,7 +590,7 @@ void SkRasterPipelineBlitter::blitMask(const SkMask& mask, const SkIRect& clip) || mask.fFormat == SkMask::kLCD16_Format || mask.fFormat == SkMask::k3D_Format); - auto extract_mask_plane = [&mask](int plane, SkRasterPipeline_MemoryCtx* ctx) { + auto extract_mask_plane = [&mask](int plane, SkRasterPipelineContexts::MemoryCtx* ctx) { // LCD is 16-bit per pixel; A8 and 3D are 8-bit per pixel. size_t bpp = mask.fFormat == SkMask::kLCD16_Format ? 2 : 1; diff --git a/gfx/skia/skia/src/core/SkRasterPipelineOpContexts.h b/gfx/skia/skia/src/core/SkRasterPipelineOpContexts.h index 479b7ffae859..3d9fbfcf69ed 100644 --- a/gfx/skia/skia/src/core/SkRasterPipelineOpContexts.h +++ b/gfx/skia/skia/src/core/SkRasterPipelineOpContexts.h @@ -13,21 +13,26 @@ #include namespace SkSL { class TraceHook; } +// state shared by stack_checkpoint and stack_rewind +struct SkRasterPipelineStage; +enum class SkPerlinNoiseShaderType; + +namespace SkRasterPipelineContexts { // The largest number of pixels we handle at a time. We have a separate value for the largest number // of pixels we handle in the highp pipeline. Many of the context structs in this file are only used // by stages that have no lowp implementation. They can therefore use the (smaller) highp value to // save memory in the arena. -inline static constexpr int SkRasterPipeline_kMaxStride = 16; -inline static constexpr int SkRasterPipeline_kMaxStride_highp = 16; +inline static constexpr int kMaxStride = 16; +inline static constexpr int kMaxStride_highp = 16; // How much space to allocate for each MemoryCtx scratch buffer, as part of tail-pixel handling. -inline static constexpr size_t SkRasterPipeline_MaxScratchPerPatch = - std::max(SkRasterPipeline_kMaxStride_highp * 16, // 16 == largest highp bpp (RGBA_F32) - SkRasterPipeline_kMaxStride * 4); // 4 == largest lowp bpp (RGBA_8888) +inline static constexpr size_t kMaxScratchPerPatch = + std::max(kMaxStride_highp * 16, // 16 == largest highp bpp (RGBA_F32) + kMaxStride * 4); // 4 == largest lowp bpp (RGBA_8888) // These structs hold the context data for many of the Raster Pipeline ops. -struct SkRasterPipeline_MemoryCtx { +struct MemoryCtx { void* pixels; int stride; }; @@ -38,7 +43,7 @@ struct SkRasterPipeline_MemoryCtx { // when we reach the tail: // // 1) Source buffers have their tail contents copied to a scratch buffer that is at least N wide. -// In practice, each scratch buffer uses SkRasterPipeline_MaxScratchPerPatch bytes. +// In practice, each scratch buffer uses kMaxScratchPerPatch bytes. // 2) Each MemoryCtx in the pipeline is patched, such that access to them (at the current scanline // and x-offset) will land in the scratch buffer. // 3) Pipeline is run as normal (with all memory access happening safely in the scratch buffers). @@ -47,22 +52,22 @@ struct SkRasterPipeline_MemoryCtx { // // To do all of this, the pipeline creates a MemoryCtxPatch for each unique MemoryCtx referenced by // the pipeline. -struct SkRasterPipeline_MemoryCtxInfo { - SkRasterPipeline_MemoryCtx* context; +struct MemoryCtxInfo { + MemoryCtx* context; int bytesPerPixel; bool load; bool store; }; -struct SkRasterPipeline_MemoryCtxPatch { - SkRasterPipeline_MemoryCtxInfo info; +struct MemoryCtxPatch { + MemoryCtxInfo info; void* backup; // Remembers context->pixels so we can restore it - std::byte scratch[SkRasterPipeline_MaxScratchPerPatch]; + std::byte scratch[kMaxScratchPerPatch]; }; -struct SkRasterPipeline_GatherCtx { +struct GatherCtx { const void* pixels; int stride; float width; @@ -73,44 +78,42 @@ struct SkRasterPipeline_GatherCtx { }; // State shared by save_xy, accumulate, and bilinear_* / bicubic_*. -struct SkRasterPipeline_SamplerCtx { - float x[SkRasterPipeline_kMaxStride_highp]; - float y[SkRasterPipeline_kMaxStride_highp]; - float fx[SkRasterPipeline_kMaxStride_highp]; - float fy[SkRasterPipeline_kMaxStride_highp]; - float scalex[SkRasterPipeline_kMaxStride_highp]; - float scaley[SkRasterPipeline_kMaxStride_highp]; +struct SamplerCtx { + float x[kMaxStride_highp]; + float y[kMaxStride_highp]; + float fx[kMaxStride_highp]; + float fy[kMaxStride_highp]; + float scalex[kMaxStride_highp]; + float scaley[kMaxStride_highp]; // for bicubic_[np][13][xy] float weights[16]; - float wx[4][SkRasterPipeline_kMaxStride_highp]; - float wy[4][SkRasterPipeline_kMaxStride_highp]; + float wx[4][kMaxStride_highp]; + float wy[4][kMaxStride_highp]; }; -struct SkRasterPipeline_TileCtx { +struct TileCtx { float scale; float invScale; // cache of 1/scale // When in the reflection portion of mirror tiling we need to snap the opposite direction // at integer sample points than when in the forward direction. This controls which way we bias - // in the reflection. It should be 1 if SkRasterPipeline_GatherCtx::roundDownAtInteger is true + // in the reflection. It should be 1 if GatherCtx::roundDownAtInteger is true // and otherwise -1. int mirrorBiasDir = -1; }; -struct SkRasterPipeline_DecalTileCtx { - uint32_t mask[SkRasterPipeline_kMaxStride]; +struct DecalTileCtx { + uint32_t mask[kMaxStride]; float limit_x; float limit_y; // These control which edge of the interval is included (i.e. closed interval at 0 or at limit). - // They should be set to limit_x and limit_y if SkRasterPipeline_GatherCtx::roundDownAtInteger + // They should be set to limit_x and limit_y if GatherCtx::roundDownAtInteger // is true and otherwise zero. float inclusiveEdge_x = 0; float inclusiveEdge_y = 0; }; -enum class SkPerlinNoiseShaderType; - -struct SkRasterPipeline_PerlinNoiseCtx { +struct PerlinNoiseCtx { SkPerlinNoiseShaderType noiseType; float baseFrequencyX, baseFrequencyY; float stitchDataInX, stitchDataInY; @@ -121,16 +124,16 @@ struct SkRasterPipeline_PerlinNoiseCtx { }; // State used by mipmap_linear_* -struct SkRasterPipeline_MipmapCtx { +struct MipmapCtx { // Original coords, saved before the base level logic - float x[SkRasterPipeline_kMaxStride_highp]; - float y[SkRasterPipeline_kMaxStride_highp]; + float x[kMaxStride_highp]; + float y[kMaxStride_highp]; // Base level color - float r[SkRasterPipeline_kMaxStride_highp]; - float g[SkRasterPipeline_kMaxStride_highp]; - float b[SkRasterPipeline_kMaxStride_highp]; - float a[SkRasterPipeline_kMaxStride_highp]; + float r[kMaxStride_highp]; + float g[kMaxStride_highp]; + float b[kMaxStride_highp]; + float a[kMaxStride_highp]; // Scale factors to transform base level coords to lower level coords float scaleX; @@ -139,122 +142,119 @@ struct SkRasterPipeline_MipmapCtx { float lowerWeight; }; -struct SkRasterPipeline_CoordClampCtx { +struct CoordClampCtx { float min_x, min_y; float max_x, max_y; }; -struct SkRasterPipeline_CallbackCtx { - void (*fn)(SkRasterPipeline_CallbackCtx* self, - int active_pixels /*<= SkRasterPipeline_kMaxStride_highp*/); +struct CallbackCtx { + void (*fn)(CallbackCtx* self, int active_pixels /*<= kMaxStride_highp*/); // When called, fn() will have our active pixels available in rgba. // When fn() returns, the pipeline will read back those active pixels from read_from. - float rgba[4*SkRasterPipeline_kMaxStride_highp]; + float rgba[4 * kMaxStride_highp]; float* read_from = rgba; }; -// state shared by stack_checkpoint and stack_rewind -struct SkRasterPipelineStage; - -struct SkRasterPipeline_RewindCtx { - float r[SkRasterPipeline_kMaxStride_highp]; - float g[SkRasterPipeline_kMaxStride_highp]; - float b[SkRasterPipeline_kMaxStride_highp]; - float a[SkRasterPipeline_kMaxStride_highp]; - float dr[SkRasterPipeline_kMaxStride_highp]; - float dg[SkRasterPipeline_kMaxStride_highp]; - float db[SkRasterPipeline_kMaxStride_highp]; - float da[SkRasterPipeline_kMaxStride_highp]; +struct RewindCtx { + float r[kMaxStride_highp]; + float g[kMaxStride_highp]; + float b[kMaxStride_highp]; + float a[kMaxStride_highp]; + float dr[kMaxStride_highp]; + float dg[kMaxStride_highp]; + float db[kMaxStride_highp]; + float da[kMaxStride_highp]; std::byte* base; SkRasterPipelineStage* stage; }; -struct SkRasterPipeline_GradientCtx { +constexpr size_t kRGBAChannels = 4; + +struct GradientCtx { size_t stopCount; - float* fs[4]; - float* bs[4]; + float* factors[kRGBAChannels]; + float* biases[kRGBAChannels]; float* ts; }; -struct SkRasterPipeline_EvenlySpaced2StopGradientCtx { - float f[4]; - float b[4]; +struct EvenlySpaced2StopGradientCtx { + float factor[kRGBAChannels]; + float bias[kRGBAChannels]; }; -struct SkRasterPipeline_2PtConicalCtx { - uint32_t fMask[SkRasterPipeline_kMaxStride_highp]; +struct Conical2PtCtx { + uint32_t fMask[kMaxStride_highp]; float fP0, fP1; }; -struct SkRasterPipeline_UniformColorCtx { +struct UniformColorCtx { float r,g,b,a; uint16_t rgba[4]; // [0,255] in a 16-bit lane. }; -struct SkRasterPipeline_EmbossCtx { - SkRasterPipeline_MemoryCtx mul, - add; +struct EmbossCtx { + MemoryCtx mul, add; }; -struct SkRasterPipeline_TablesCtx { +struct TablesCtx { const uint8_t *r, *g, *b, *a; }; using SkRPOffset = uint32_t; -struct SkRasterPipeline_InitLaneMasksCtx { +struct InitLaneMasksCtx { uint8_t* tail; }; -struct SkRasterPipeline_ConstantCtx { +struct ConstantCtx { int32_t value; SkRPOffset dst; }; -struct SkRasterPipeline_UniformCtx { +struct UniformCtx { int32_t* dst; const int32_t* src; }; -struct SkRasterPipeline_BinaryOpCtx { +struct BinaryOpCtx { SkRPOffset dst; SkRPOffset src; }; -struct SkRasterPipeline_TernaryOpCtx { +struct TernaryOpCtx { SkRPOffset dst; SkRPOffset delta; }; -struct SkRasterPipeline_MatrixMultiplyCtx { +struct MatrixMultiplyCtx { SkRPOffset dst; uint8_t leftColumns, leftRows, rightColumns, rightRows; }; -struct SkRasterPipeline_SwizzleCtx { +struct SwizzleCtx { // If we are processing more than 16 pixels at a time, an 8-bit offset won't be sufficient and // `offsets` will need to use uint16_t (or dial down the premultiplication). - static_assert(SkRasterPipeline_kMaxStride_highp <= 16); + static_assert(kMaxStride_highp <= 16); SkRPOffset dst; uint8_t offsets[4]; // values must be byte offsets (4 * highp-stride * component-index) }; -struct SkRasterPipeline_ShuffleCtx { +struct ShuffleCtx { int32_t* ptr; int count; uint16_t offsets[16]; // values must be byte offsets (4 * highp-stride * component-index) }; -struct SkRasterPipeline_SwizzleCopyCtx { +struct SwizzleCopyCtx { int32_t* dst; const int32_t* src; // src values must _not_ overlap dst values uint16_t offsets[4]; // values must be byte offsets (4 * highp-stride * component-index) }; -struct SkRasterPipeline_CopyIndirectCtx { +struct CopyIndirectCtx { int32_t* dst; const int32_t* src; const uint32_t *indirectOffset; // this applies to `src` or `dst` based on the op @@ -262,47 +262,47 @@ struct SkRasterPipeline_CopyIndirectCtx { uint32_t slots; // the number of slots to copy }; -struct SkRasterPipeline_SwizzleCopyIndirectCtx : public SkRasterPipeline_CopyIndirectCtx { +struct SwizzleCopyIndirectCtx : public CopyIndirectCtx { uint16_t offsets[4]; // values must be byte offsets (4 * highp-stride * component-index) }; -struct SkRasterPipeline_BranchCtx { +struct BranchCtx { int offset; // contains the label ID during compilation, and the program offset when compiled }; -struct SkRasterPipeline_BranchIfAllLanesActiveCtx : public SkRasterPipeline_BranchCtx { +struct BranchIfAllLanesActiveCtx : public BranchCtx { uint8_t* tail = nullptr; // lanes past the tail are _never_ active, so we need to exclude them }; -struct SkRasterPipeline_BranchIfEqualCtx : public SkRasterPipeline_BranchCtx { +struct BranchIfEqualCtx : public BranchCtx { int value; const int* ptr; }; -struct SkRasterPipeline_CaseOpCtx { +struct CaseOpCtx { int expectedValue; SkRPOffset offset; // points to a pair of adjacent I32s: {I32 actualValue, I32 defaultMask} }; -struct SkRasterPipeline_TraceFuncCtx { +struct TraceFuncCtx { const int* traceMask; SkSL::TraceHook* traceHook; int funcIdx; }; -struct SkRasterPipeline_TraceScopeCtx { +struct TraceScopeCtx { const int* traceMask; SkSL::TraceHook* traceHook; int delta; }; -struct SkRasterPipeline_TraceLineCtx { +struct TraceLineCtx { const int* traceMask; SkSL::TraceHook* traceHook; int lineNumber; }; -struct SkRasterPipeline_TraceVarCtx { +struct TraceVarCtx { const int* traceMask; SkSL::TraceHook* traceHook; int slotIdx, numSlots; @@ -311,4 +311,6 @@ struct SkRasterPipeline_TraceVarCtx { uint32_t indirectLimit; // the indirect offset is clamped to this upper bound }; +} // namespace SkRasterPipelineContexts + #endif // SkRasterPipelineOpContexts_DEFINED diff --git a/gfx/skia/skia/src/core/SkRasterPipelineOpList.h b/gfx/skia/skia/src/core/SkRasterPipelineOpList.h index 6a63e568041b..547c26f078fa 100644 --- a/gfx/skia/skia/src/core/SkRasterPipelineOpList.h +++ b/gfx/skia/skia/src/core/SkRasterPipelineOpList.h @@ -9,48 +9,52 @@ #define SkRasterPipelineOpList_DEFINED // `SK_RASTER_PIPELINE_OPS_LOWP` defines ops that have parallel lowp and highp implementations. -#define SK_RASTER_PIPELINE_OPS_LOWP(M) \ - M(move_src_dst) M(move_dst_src) M(swap_src_dst) \ - M(clamp_01) M(clamp_a_01) M(clamp_gamut) \ - M(premul) M(premul_dst) \ - M(force_opaque) M(force_opaque_dst) \ - M(set_rgb) M(swap_rb) M(swap_rb_dst) \ - M(black_color) M(white_color) \ - M(uniform_color) M(uniform_color_dst) \ - M(seed_shader) \ - M(load_a8) M(load_a8_dst) M(store_a8) M(gather_a8) \ - M(load_565) M(load_565_dst) M(store_565) M(gather_565) \ - M(load_4444) M(load_4444_dst) M(store_4444) M(gather_4444) \ - M(load_8888) M(load_8888_dst) M(store_8888) M(gather_8888) \ - M(load_rg88) M(load_rg88_dst) M(store_rg88) M(gather_rg88) \ - M(store_r8) \ - M(alpha_to_gray) M(alpha_to_gray_dst) \ - M(alpha_to_red) M(alpha_to_red_dst) \ - M(bt709_luminance_or_luma_to_alpha) M(bt709_luminance_or_luma_to_rgb) \ - M(bilerp_clamp_8888) \ - M(load_src) M(store_src) M(store_src_a) M(load_dst) M(store_dst) \ - M(scale_u8) M(scale_565) M(scale_1_float) M(scale_native) \ - M( lerp_u8) M( lerp_565) M( lerp_1_float) M(lerp_native) \ - M(dstatop) M(dstin) M(dstout) M(dstover) \ - M(srcatop) M(srcin) M(srcout) M(srcover) \ - M(clear) M(modulate) M(multiply) M(plus_) M(screen) M(xor_) \ - M(darken) M(difference) \ - M(exclusion) M(hardlight) M(lighten) M(overlay) \ - M(srcover_rgba_8888) \ - M(matrix_translate) M(matrix_scale_translate) \ - M(matrix_2x3) \ - M(matrix_perspective) \ - M(decal_x) M(decal_y) M(decal_x_and_y) \ - M(check_decal_mask) \ - M(clamp_x_1) M(mirror_x_1) M(repeat_x_1) \ - M(clamp_x_and_y) \ - M(evenly_spaced_gradient) \ - M(gradient) \ - M(evenly_spaced_2_stop_gradient) \ - M(xy_to_unit_angle) \ - M(xy_to_radius) \ - M(emboss) \ - M(swizzle) +#define SK_RASTER_PIPELINE_OPS_LOWP(M) \ + M(move_src_dst) M(move_dst_src) M(swap_src_dst) \ + M(clamp_01) M(clamp_a_01) M(clamp_gamut) \ + M(premul) M(premul_dst) \ + M(force_opaque) M(force_opaque_dst) \ + M(set_rgb) M(swap_rb) M(swap_rb_dst) \ + M(black_color) M(white_color) \ + M(uniform_color) M(uniform_color_dst) \ + M(seed_shader) \ + M(load_a8) M(load_a8_dst) M(store_a8) M(gather_a8) \ + M(load_565) M(load_565_dst) M(store_565) M(gather_565) \ + M(load_4444) M(load_4444_dst) M(store_4444) M(gather_4444) \ + M(load_8888) M(load_8888_dst) M(store_8888) M(gather_8888) \ + M(load_rg88) M(load_rg88_dst) M(store_rg88) M(gather_rg88) \ + M(store_r8) \ + M(alpha_to_gray) M(alpha_to_gray_dst) \ + M(alpha_to_red) M(alpha_to_red_dst) \ + M(bt709_luminance_or_luma_to_alpha) \ + M(bt709_luminance_or_luma_to_rgb) \ + M(bilerp_clamp_8888) \ + M(load_src) M(store_src) M(store_src_a) \ + M(load_dst) M(store_dst) \ + M(scale_u8) M(scale_565) M(scale_1_float) M(scale_native) \ + M( lerp_u8) M( lerp_565) M( lerp_1_float) M(lerp_native) \ + M(dstatop) M(dstin) M(dstout) M(dstover) \ + M(srcatop) M(srcin) M(srcout) M(srcover) \ + M(clear) M(modulate) M(multiply) M(plus_) M(screen) M(xor_) \ + M(darken) M(difference) \ + M(exclusion) M(hardlight) M(lighten) M(overlay) \ + M(srcover_rgba_8888) \ + M(matrix_translate) M(matrix_scale_translate) \ + M(matrix_2x3) \ + M(matrix_perspective) \ + M(decal_x) M(decal_y) M(decal_x_and_y) \ + M(check_decal_mask) \ + M(clamp_x_1) M(mirror_x_1) M(repeat_x_1) \ + M(clamp_x_and_y) \ + M(evenly_spaced_gradient) \ + M(gradient) \ + M(evenly_spaced_2_stop_gradient) \ + M(xy_to_unit_angle) \ + M(xy_to_radius) \ + M(emboss) \ + M(swizzle) \ + M(debug_x) M(debug_y) M(debug_r) M(debug_g) M(debug_b) M(debug_a) \ + M(debug_r_255) M(debug_g_255) M(debug_b_255) M(debug_a_255) /** * `SK_RASTER_PIPELINE_OPS_SKSL` defines ops used by SkSL. diff --git a/gfx/skia/skia/src/core/SkRasterPipelineVizualizer.h b/gfx/skia/skia/src/core/SkRasterPipelineVizualizer.h new file mode 100644 index 000000000000..7eef6f2dc98b --- /dev/null +++ b/gfx/skia/skia/src/core/SkRasterPipelineVizualizer.h @@ -0,0 +1,114 @@ +/* + * Copyright 2025 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SkRasterPipelineVizualizer_DEFINED +#define SkRasterPipelineVizualizer_DEFINED + +#include "include/core/SkBitmap.h" +#include "include/core/SkRefCnt.h" // IWYU pragma: keep + +#include + +class SkArenaAlloc; +class SkBlitter; +class SkMatrix; +class SkPaint; +class SkPixmap; +class SkShader; +class SkSurfaceProps; +enum class SkRasterPipelineOp; + +namespace SkRasterPipelineVisualizer { + +// After each stage, visualize 0 or more (probably 1-4) lanes into the provided panels. +// The vectors are parallel data structures with the op chosen by the user of what +// and how to be visualized. The number of panels per stage can differ (the pipeline +// frequently can be thought of as a ragged 2 dimensional array of images). +struct DebugStage { + std::vector panels; + // Should be only ops that start with debug_ + std::vector ops; +}; + +// The same as SkCreateRasterPipelineBlitter but takes in some stages to +// visualize the output of each of the main stages. This will inject some +// stages into the shader on the provided paint and then return a blitter +// which can be used to draw the final output and the intermediate represenations. +// If stages is not the same size as the actual number of pipeline stages, this +// will abort. The first time this function is called, the original pipeline will be +// displayed via standard out to make it easier to get the number of stages right. +// +// Note that different methods on the provided Blitter may add stages after creation +// (e.g. for blending with the destination) so this won't visualize those, but the main +// ones from the shader should be visualized via this code path. +SkBlitter* CreateBlitter(const SkPixmap& output, + const std::vector& stages, + const SkPaint&, + const SkMatrix& ctm, + SkArenaAlloc*, + sk_sp clipShader, + const SkSurfaceProps& props); + +// A convinence class for making many debug stages. The user chooses which lanes are +// useful to be visualized for each lane. +// SkBitmap* panels = ...; // make a bunch of bitmaps somehow +// +// DebugStageBuilder stageBuilder; +// stageBuilder.add(panels[0], SkRasterPipelineOp::debug_x, +// panels[1], SkRasterPipelineOp::debug_y) +// .add(panels[2], SkRasterPipelineOp::debug_b) +// .add(panels[3], SkRasterPipelineOp::debug_r_255, +// panels[4], SkRasterPipelineOp::debug_g_255, +// panels[5], SkRasterPipelineOp::debug_b_255, +// panels[6], SkRasterPipelineOp::debug_a_255); +// auto stages = stageBuilder.build() +class DebugStageBuilder { +public: + DebugStageBuilder() = default; + DebugStageBuilder(const DebugStageBuilder&) = delete; + DebugStageBuilder(DebugStageBuilder&&) = delete; + DebugStageBuilder& operator=(const DebugStageBuilder&) = delete; + DebugStageBuilder& operator=(DebugStageBuilder&&) = delete; + + template + DebugStageBuilder& add(const SkBitmap& panel, SkRasterPipelineOp op, Args... args) { + std::vector panels; + std::vector ops; + + add_next(panels, ops, panel, op, args...); + fDebugStages.push_back({panels, ops}); + return *this; + } + + DebugStageBuilder& add() { + std::vector panels; + std::vector ops; + fDebugStages.push_back({panels, ops}); + return *this; + } + + std::vector build() { return fDebugStages; } + +private: + std::vector fDebugStages; + + static void add_next(std::vector& v, std::vector& ops) {} + + template + static void add_next(std::vector& panels, + std::vector& ops, + const SkBitmap& panel, + SkRasterPipelineOp op, + Args... args) { + panels.emplace_back(panel); + ops.emplace_back(op); + add_next(panels, ops, args...); + } +}; + +} // namespace SkRasterPipelineVisualizer + +#endif diff --git a/gfx/skia/skia/src/core/SkResourceCache.cpp b/gfx/skia/skia/src/core/SkResourceCache.cpp index cbd39270afc2..b4cd638f9c11 100644 --- a/gfx/skia/skia/src/core/SkResourceCache.cpp +++ b/gfx/skia/skia/src/core/SkResourceCache.cpp @@ -463,7 +463,6 @@ void SkResourceCache::checkMessages() { /////////////////////////////////////////////////////////////////////////////// -static SkResourceCache* gResourceCache = nullptr; static SkMutex& resource_cache_mutex() { static SkMutex& mutex = *(new SkMutex); return mutex; @@ -473,8 +472,9 @@ static SkMutex& resource_cache_mutex() { static SkResourceCache* get_cache() { // resource_cache_mutex() is always held when this is called, so we don't need to be fancy in here. resource_cache_mutex().assertHeld(); + static SkResourceCache* gResourceCache = nullptr; if (nullptr == gResourceCache) { -#ifdef SK_USE_DISCARDABLE_SCALEDIMAGECACHE +#if defined(SK_USE_DISCARDABLE_SCALEDIMAGECACHE) gResourceCache = new SkResourceCache(SkDiscardableMemory::Create); #else gResourceCache = new SkResourceCache(SK_DEFAULT_IMAGE_CACHE_LIMIT); diff --git a/gfx/skia/skia/src/core/SkRuntimeBlender.cpp b/gfx/skia/skia/src/core/SkRuntimeBlender.cpp index 8425ba5524ea..f72d6147f192 100644 --- a/gfx/skia/skia/src/core/SkRuntimeBlender.cpp +++ b/gfx/skia/skia/src/core/SkRuntimeBlender.cpp @@ -15,12 +15,15 @@ #include "include/private/base/SkDebug.h" #include "include/private/base/SkTArray.h" #include "src/core/SkEffectPriv.h" +#include "src/core/SkKnownRuntimeEffects.h" +#include "src/core/SkPicturePriv.h" #include "src/core/SkReadBuffer.h" #include "src/core/SkRuntimeEffectPriv.h" #include "src/core/SkWriteBuffer.h" #include "src/shaders/SkShaderBase.h" #include "src/sksl/codegen/SkSLRasterPipelineBuilder.h" +#include #include using namespace skia_private; @@ -31,22 +34,45 @@ using namespace skia_private; constexpr bool kLenientSkSLDeserialization = false; #endif +void SkRuntimeBlender::flatten(SkWriteBuffer& buffer) const { + if (SkKnownRuntimeEffects::IsSkiaKnownRuntimeEffect(fEffect->fStableKey)) { + // We only serialize Skia-internal stableKeys. First party stable keys are not serialized. + buffer.write32(fEffect->fStableKey); + } else { + buffer.write32(0); + buffer.writeString(fEffect->source().c_str()); + } + buffer.writeDataAsByteArray(fUniforms.get()); + SkRuntimeEffectPriv::WriteChildEffects(buffer, fChildren); +} + sk_sp SkRuntimeBlender::CreateProc(SkReadBuffer& buffer) { if (!buffer.validate(buffer.allowSkSL())) { return nullptr; } - SkString sksl; - buffer.readString(&sksl); - sk_sp uniforms = buffer.readByteArrayAsData(); + sk_sp effect; + if (!buffer.isVersionLT(SkPicturePriv::kSerializeStableKeys)) { + uint32_t candidateStableKey = buffer.readUInt(); + effect = SkKnownRuntimeEffects::MaybeGetKnownRuntimeEffect(candidateStableKey); + if (!effect && !buffer.validate(candidateStableKey == 0)) { + return nullptr; + } + } - auto effect = SkMakeCachedRuntimeEffect(SkRuntimeEffect::MakeForBlender, std::move(sksl)); + if (!effect) { + SkString sksl; + buffer.readString(&sksl); + effect = SkMakeCachedRuntimeEffect(SkRuntimeEffect::MakeForBlender, std::move(sksl)); + } if constexpr (!kLenientSkSLDeserialization) { if (!buffer.validate(effect != nullptr)) { return nullptr; } } + sk_sp uniforms = buffer.readByteArrayAsData(); + STArray<4, SkRuntimeEffect::ChildPtr> children; if (!SkRuntimeEffectPriv::ReadChildEffects(buffer, effect.get(), &children)) { return nullptr; @@ -83,10 +109,3 @@ bool SkRuntimeBlender::onAppendStages(const SkStageRec& rec) const { } return false; } - -void SkRuntimeBlender::flatten(SkWriteBuffer& buffer) const { - buffer.writeString(fEffect->source().c_str()); - buffer.writeDataAsByteArray(fUniforms.get()); - SkRuntimeEffectPriv::WriteChildEffects(buffer, fChildren); -} - diff --git a/gfx/skia/skia/src/core/SkRuntimeEffect.cpp b/gfx/skia/skia/src/core/SkRuntimeEffect.cpp index 9d61f3df8e76..3beeb1146db5 100644 --- a/gfx/skia/skia/src/core/SkRuntimeEffect.cpp +++ b/gfx/skia/skia/src/core/SkRuntimeEffect.cpp @@ -188,7 +188,7 @@ sk_sp SkRuntimeEffectPriv::TransformUniforms( for (const auto& u : uniforms) { if (u.flags & Flags::kColor_Flag) { SkASSERT(u.type == Type::kFloat3 || u.type == Type::kFloat4); - if (steps.flags.mask()) { + if (steps.fFlags.mask()) { float* color = SkTAddOffset(writableData(), u.offset); if (u.type == Type::kFloat4) { // RGBA, easy case @@ -324,7 +324,7 @@ void RuntimeEffectRPCallbacks::toLinearSrgb(const void* color) { if (fStage.fDstCS) { SkColorSpaceXformSteps xform{fStage.fDstCS, kUnpremul_SkAlphaType, sk_srgb_linear_singleton(), kUnpremul_SkAlphaType}; - if (xform.flags.mask()) { + if (xform.fFlags.mask()) { // We have a non-identity colorspace transform; apply it. this->applyColorSpaceXform(xform, color); } @@ -335,7 +335,7 @@ void RuntimeEffectRPCallbacks::fromLinearSrgb(const void* color) { if (fStage.fDstCS) { SkColorSpaceXformSteps xform{sk_srgb_linear_singleton(), kUnpremul_SkAlphaType, fStage.fDstCS, kUnpremul_SkAlphaType}; - if (xform.flags.mask()) { + if (xform.fFlags.mask()) { // We have a non-identity colorspace transform; apply it. this->applyColorSpaceXform(xform, color); } @@ -747,6 +747,7 @@ SkRuntimeEffect::SkRuntimeEffect(std::unique_ptr baseProgram, uint32_t flags) : fHash(SkChecksum::Hash32(baseProgram->fSource->c_str(), baseProgram->fSource->size())) , fStableKey(options.fStableKey) + , fName(options.fName) , fBaseProgram(std::move(baseProgram)) , fMain(main) , fUniforms(std::move(uniforms)) @@ -761,7 +762,9 @@ SkRuntimeEffect::SkRuntimeEffect(std::unique_ptr baseProgram, // assert below to trigger, please incorporate your field into `fHash` and update KnownOptions // to match the layout of Options. struct KnownOptions { - bool forceUnoptimized, allowPrivateAccess; + bool forceUnoptimized; + std::string_view fName; + bool allowPrivateAccess; uint32_t fStableKey; SkSL::Version maxVersionAllowed; }; diff --git a/gfx/skia/skia/src/core/SkRuntimeEffectPriv.h b/gfx/skia/skia/src/core/SkRuntimeEffectPriv.h index 5165b81e5f17..788992180c3e 100644 --- a/gfx/skia/skia/src/core/SkRuntimeEffectPriv.h +++ b/gfx/skia/skia/src/core/SkRuntimeEffectPriv.h @@ -18,6 +18,7 @@ #include "include/private/base/SkSpan_impl.h" #include "include/private/base/SkTArray.h" #include "src/core/SkEffectPriv.h" +#include "src/core/SkKnownRuntimeEffects.h" #include "src/sksl/codegen/SkSLRasterPipelineBuilder.h" #include @@ -79,10 +80,36 @@ public: return effect.hash(); } + static bool HasName(const SkRuntimeEffect& effect) { + return !effect.fName.isEmpty(); + } + + static const char* GetName(const SkRuntimeEffect& effect) { + return effect.fName.c_str(); + } + static uint32_t StableKey(const SkRuntimeEffect& effect) { return effect.fStableKey; } + // This method is only used on user-defined known runtime effects + static void SetStableKey(SkRuntimeEffect* effect, uint32_t stableKey) { + SkASSERT(!effect->fStableKey); + SkASSERT(SkKnownRuntimeEffects::IsViableUserDefinedKnownRuntimeEffect(stableKey)); + effect->fStableKey = stableKey; + } + + // This method is only used for Skia-internal known runtime effects + static void SetStableKeyOnOptions(SkRuntimeEffect::Options* options, uint32_t stableKey) { + SkASSERT(!options->fStableKey); + SkASSERT(SkKnownRuntimeEffects::IsSkiaKnownRuntimeEffect(stableKey)); + options->fStableKey = stableKey; + } + + static void ResetStableKey(SkRuntimeEffect* effect) { + effect->fStableKey = 0; + } + static const SkSL::Program& Program(const SkRuntimeEffect& effect) { return *effect.fBaseProgram; } @@ -97,10 +124,6 @@ public: options->allowPrivateAccess = true; } - static void SetStableKey(SkRuntimeEffect::Options* options, uint32_t stableKey) { - options->fStableKey = stableKey; - } - static SkRuntimeEffect::Uniform VarAsUniform(const SkSL::Variable&, const SkSL::Context&, size_t* offset); @@ -159,7 +182,7 @@ inline sk_sp SkMakeCachedRuntimeEffect( // Internal API that assumes (and asserts) that the shader code is valid, but does no internal // caching. Used when the caller will cache the result in a static variable. Ownership is passed to -// the caller; the effect will be leaked if it the pointer is not stored or explicitly deleted. +// the caller; the effect will be leaked if the pointer is not stored or explicitly deleted. inline SkRuntimeEffect* SkMakeRuntimeEffect( SkRuntimeEffect::Result (*make)(SkString, const SkRuntimeEffect::Options&), const char* sksl, diff --git a/gfx/skia/skia/src/core/SkScalerContext.cpp b/gfx/skia/skia/src/core/SkScalerContext.cpp index bc47f11eae0d..c4938d24efed 100644 --- a/gfx/skia/skia/src/core/SkScalerContext.cpp +++ b/gfx/skia/skia/src/core/SkScalerContext.cpp @@ -18,7 +18,6 @@ #include "include/core/SkPathEffect.h" #include "include/core/SkPixmap.h" #include "include/core/SkStrokeRec.h" -#include "include/private/SkColorData.h" #include "include/private/base/SkAlign.h" #include "include/private/base/SkCPUTypes.h" #include "include/private/base/SkDebug.h" @@ -30,6 +29,7 @@ #include "src/base/SkAutoMalloc.h" #include "src/core/SkAutoPixmapStorage.h" #include "src/core/SkBlitter_A8.h" +#include "src/core/SkColorData.h" #include "src/core/SkDescriptor.h" #include "src/core/SkDrawBase.h" #include "src/core/SkFontPriv.h" @@ -86,10 +86,10 @@ SkScalerContextRec SkScalerContext::PreprocessRec(const SkTypeface& typeface, return rec; } -SkScalerContext::SkScalerContext(sk_sp typeface, const SkScalerContextEffects& effects, +SkScalerContext::SkScalerContext(SkTypeface& typeface, const SkScalerContextEffects& effects, const SkDescriptor* desc) - : fRec(PreprocessRec(*typeface, effects, *desc)) - , fTypeface(std::move(typeface)) + : fRec(PreprocessRec(typeface, effects, *desc)) + , fTypeface(typeface) , fPathEffect(sk_ref_sp(effects.fPathEffect)) , fMaskFilter(sk_ref_sp(effects.fMaskFilter)) // Initialize based on our settings. Subclasses can also force this. @@ -566,7 +566,8 @@ void SkScalerContext::GenerateImageFromPath( draw.fDst = dst; draw.fRC = &clip; draw.fCTM = &matrix; - draw.drawPath(*pathToUse, paint); + // We can save a copy if we had to use the local strokePath + draw.drawPath(*pathToUse, paint, nullptr, pathToUse == &strokePath); switch (dstMask.fFormat) { case SkMask::kBW_Format: @@ -1287,13 +1288,13 @@ bool SkScalerContext::CheckBufferSizeForRec(const SkScalerContextRec& rec, } std::unique_ptr SkScalerContext::MakeEmpty( - sk_sp typeface, const SkScalerContextEffects& effects, + SkTypeface& typeface, const SkScalerContextEffects& effects, const SkDescriptor* desc) { class SkScalerContext_Empty : public SkScalerContext { public: - SkScalerContext_Empty(sk_sp typeface, const SkScalerContextEffects& effects, + SkScalerContext_Empty(SkTypeface& typeface, const SkScalerContextEffects& effects, const SkDescriptor* desc) - : SkScalerContext(std::move(typeface), effects, desc) {} + : SkScalerContext(typeface, effects, desc) {} protected: GlyphMetrics generateMetrics(const SkGlyph& glyph, SkArenaAlloc*) override { @@ -1311,7 +1312,7 @@ std::unique_ptr SkScalerContext::MakeEmpty( } }; - return std::make_unique(std::move(typeface), effects, desc); + return std::make_unique(typeface, effects, desc); } diff --git a/gfx/skia/skia/src/core/SkScalerContext.h b/gfx/skia/skia/src/core/SkScalerContext.h index 7737607c4c9d..b73585d8dd38 100644 --- a/gfx/skia/skia/src/core/SkScalerContext.h +++ b/gfx/skia/skia/src/core/SkScalerContext.h @@ -287,10 +287,10 @@ public: kHinting_Mask = kHintingBit1_Flag | kHintingBit2_Flag, }; - SkScalerContext(sk_sp, const SkScalerContextEffects&, const SkDescriptor*); + SkScalerContext(SkTypeface&, const SkScalerContextEffects&, const SkDescriptor*); virtual ~SkScalerContext(); - SkTypeface* getTypeface() const { return fTypeface.get(); } + SkTypeface* getTypeface() const { return &fTypeface; } SkMask::Format getMaskFormat() const { return fRec.fMaskFormat; @@ -343,7 +343,7 @@ public: } static std::unique_ptr MakeEmpty( - sk_sp typeface, const SkScalerContextEffects& effects, + SkTypeface& typeface, const SkScalerContextEffects& effects, const SkDescriptor* desc); static SkDescriptor* AutoDescriptorGivenRecAndEffects( @@ -450,14 +450,16 @@ private: friend class PathText; // For debug purposes friend class PathTextBench; // For debug purposes friend class RandomScalerContext; // For debug purposes - friend class SkScalerContext_fontconfig; + friend class SkScalerContext_proxy; static SkScalerContextRec PreprocessRec(const SkTypeface&, const SkScalerContextEffects&, const SkDescriptor&); - // never null - sk_sp fTypeface; + // In order for a SkScalerContext to be in use this typeface must exist. + // The SkScalerContext does not keep a reference to this typeface, so this reference may be + // a dangling reference when the SkScalerContext is destroyed. + SkTypeface& fTypeface; // optional objects, which may be null sk_sp fPathEffect; diff --git a/gfx/skia/skia/src/core/SkScan_AAAPath.cpp b/gfx/skia/skia/src/core/SkScan_AAAPath.cpp index 531ea39cd744..c5d1182fde5f 100644 --- a/gfx/skia/skia/src/core/SkScan_AAAPath.cpp +++ b/gfx/skia/skia/src/core/SkScan_AAAPath.cpp @@ -104,8 +104,8 @@ public: virtual SkBlitter* getRealBlitter(bool forceRealBlitter = false) = 0; virtual void blitAntiH(int x, int y, const SkAlpha antialias[], int len) = 0; - virtual void blitAntiH(int x, int y, const SkAlpha alpha) = 0; - virtual void blitAntiH(int x, int y, int width, const SkAlpha alpha) = 0; + virtual void blitAntiH(int x, int y, SkAlpha alpha) = 0; + virtual void blitAntiH(int x, int y, int width, SkAlpha alpha) = 0; void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) override { SkDEBUGFAIL("Please call real blitter's blitAntiH instead."); @@ -156,8 +156,8 @@ public: // Allowing following methods are used to blit rectangles during aaa_walk_convex_edges // Since there aren't many rectangles, we can still bear the slow speed of virtual functions. - void blitAntiH(int x, int y, const SkAlpha alpha) override; - void blitAntiH(int x, int y, int width, const SkAlpha alpha) override; + void blitAntiH(int x, int y, SkAlpha alpha) override; + void blitAntiH(int x, int y, int width, SkAlpha alpha) override; void blitV(int x, int y, int height, SkAlpha alpha) override; void blitRect(int x, int y, int width, int height) override; void blitAntiRect(int x, int y, int width, int height, SkAlpha leftAlpha, SkAlpha rightAlpha) @@ -230,12 +230,12 @@ void MaskAdditiveBlitter::blitAntiH(int x, int y, const SkAlpha antialias[], int SK_ABORT("Don't use this; directly add alphas to the mask."); } -void MaskAdditiveBlitter::blitAntiH(int x, int y, const SkAlpha alpha) { +void MaskAdditiveBlitter::blitAntiH(int x, int y, SkAlpha alpha) { SkASSERT(x >= fMask.fBounds.fLeft - 1); add_alpha(&this->getRow(y)[x], alpha); } -void MaskAdditiveBlitter::blitAntiH(int x, int y, int width, const SkAlpha alpha) { +void MaskAdditiveBlitter::blitAntiH(int x, int y, int width, SkAlpha alpha) { SkASSERT(x >= fMask.fBounds.fLeft - 1); uint8_t* row = this->getRow(y); for (int i = 0; i < width; ++i) { @@ -291,8 +291,8 @@ public: SkBlitter* getRealBlitter(bool forceRealBlitter) override { return fRealBlitter; } void blitAntiH(int x, int y, const SkAlpha antialias[], int len) override; - void blitAntiH(int x, int y, const SkAlpha alpha) override; - void blitAntiH(int x, int y, int width, const SkAlpha alpha) override; + void blitAntiH(int x, int y, SkAlpha alpha) override; + void blitAntiH(int x, int y, int width, SkAlpha alpha) override; int getWidth() override { return fWidth; } @@ -429,7 +429,7 @@ void RunBasedAdditiveBlitter::blitAntiH(int x, int y, const SkAlpha antialias[], } } -void RunBasedAdditiveBlitter::blitAntiH(int x, int y, const SkAlpha alpha) { +void RunBasedAdditiveBlitter::blitAntiH(int x, int y, SkAlpha alpha) { checkY(y); x -= fLeft; @@ -442,7 +442,7 @@ void RunBasedAdditiveBlitter::blitAntiH(int x, int y, const SkAlpha alpha) { } } -void RunBasedAdditiveBlitter::blitAntiH(int x, int y, int width, const SkAlpha alpha) { +void RunBasedAdditiveBlitter::blitAntiH(int x, int y, int width, SkAlpha alpha) { checkY(y); x -= fLeft; @@ -466,8 +466,8 @@ public: : RunBasedAdditiveBlitter(realBlitter, ir, clipBounds, isInverse) {} void blitAntiH(int x, int y, const SkAlpha antialias[], int len) override; - void blitAntiH(int x, int y, const SkAlpha alpha) override; - void blitAntiH(int x, int y, int width, const SkAlpha alpha) override; + void blitAntiH(int x, int y, SkAlpha alpha) override; + void blitAntiH(int x, int y, int width, SkAlpha alpha) override; }; void SafeRLEAdditiveBlitter::blitAntiH(int x, int y, const SkAlpha antialias[], int len) { @@ -499,7 +499,7 @@ void SafeRLEAdditiveBlitter::blitAntiH(int x, int y, const SkAlpha antialias[], } } -void SafeRLEAdditiveBlitter::blitAntiH(int x, int y, const SkAlpha alpha) { +void SafeRLEAdditiveBlitter::blitAntiH(int x, int y, SkAlpha alpha) { checkY(y); x -= fLeft; @@ -514,7 +514,7 @@ void SafeRLEAdditiveBlitter::blitAntiH(int x, int y, const SkAlpha alpha) { } } -void SafeRLEAdditiveBlitter::blitAntiH(int x, int y, int width, const SkAlpha alpha) { +void SafeRLEAdditiveBlitter::blitAntiH(int x, int y, int width, SkAlpha alpha) { checkY(y); x -= fLeft; @@ -604,10 +604,10 @@ static void compute_alpha_above_line(SkAlpha* alphas, SkFixed last = r - ((R - 1) << 16); // horizontal edge length of the right-most triangle SkFixed firstH = SkFixedMul(first, dY); // vertical edge of the left-most triangle alphas[0] = SkFixedMul(first, firstH) >> 9; // triangle alpha - SkFixed alpha16 = firstH + (dY >> 1); // rectangle plus triangle + SkFixed alpha16 = Sk32_sat_add(firstH, dY >> 1); // rectangle plus triangle for (int i = 1; i < R - 1; ++i) { alphas[i] = alpha16 >> 8; - alpha16 += dY; + alpha16 = Sk32_sat_add(alpha16, dY); } alphas[R - 1] = fullAlpha - partial_triangle_to_alpha(last, dY); } @@ -631,10 +631,10 @@ static void compute_alpha_below_line(SkAlpha* alphas, SkFixed last = r - ((R - 1) << 16); // horizontal edge length of the right-most triangle SkFixed lastH = SkFixedMul(last, dY); // vertical edge of the right-most triangle alphas[R - 1] = SkFixedMul(last, lastH) >> 9; // triangle alpha - SkFixed alpha16 = lastH + (dY >> 1); // rectangle plus triangle + SkFixed alpha16 = Sk32_sat_add(lastH, dY >> 1); // rectangle plus triangle for (int i = R - 2; i > 0; i--) { alphas[i] = (alpha16 >> 8) & 0xFF; - alpha16 += dY; + alpha16 = Sk32_sat_add(alpha16, dY); } alphas[0] = fullAlpha - partial_triangle_to_alpha(first, dY); } @@ -946,25 +946,20 @@ static void blit_trapezoid_row(AdditiveBlitter* blitter, } } -static bool operator<(const SkAnalyticEdge& a, const SkAnalyticEdge& b) { - int valuea = a.fUpperY; - int valueb = b.fUpperY; - - if (valuea == valueb) { - valuea = a.fX; - valueb = b.fX; +static bool compare_edges(const SkAnalyticEdge* a, const SkAnalyticEdge* b) { + if (a->fUpperY != b->fUpperY) { + return a->fUpperY < b->fUpperY; } - if (valuea == valueb) { - valuea = a.fDX; - valueb = b.fDX; + if (a->fX != b->fX) { + return a->fX < b->fX; } - return valuea < valueb; + return a->fDX < b->fDX; } static SkAnalyticEdge* sort_edges(SkAnalyticEdge* list[], int count, SkAnalyticEdge** last) { - SkTQSort(list, list + count); + SkTQSort(list, list + count, compare_edges); // now make the edges linked in sorted order for (int i = 1; i < count; ++i) { @@ -1008,8 +1003,9 @@ static bool is_smooth_enough(SkAnalyticEdge* thisEdge, SkAnalyticEdge* nextEdge, // current Dy is (fQDy - fQDDy) >> shift (qEdge.fQDy - qEdge.fQDDy) >> qEdge.fCurveShift >= SK_Fixed1; } - return SkAbs32(nextEdge->fDX - thisEdge->fDX) <= SK_Fixed1 && // DDx should be small - nextEdge->fLowerY - nextEdge->fUpperY >= SK_Fixed1; // Dy should be large + // DDx should be small and Dy should be large + return SkAbs32(Sk32_sat_sub(nextEdge->fDX, thisEdge->fDX)) <= SK_Fixed1 && + nextEdge->fLowerY - nextEdge->fUpperY >= SK_Fixed1; } // Check if the leftE and riteE are changing smoothly in terms of fDX. @@ -1495,7 +1491,7 @@ static void aaa_walk_edges(SkAnalyticEdge* prevHead, SkASSERT(currE->fLowerY >= nextY); SkASSERT(currE->fY == y); - w += currE->fWinding; + w += static_cast(currE->fWinding); bool prev_in_interval = in_interval; in_interval = !(w & windingMask) == isInverse; diff --git a/gfx/skia/skia/src/core/SkScan_Antihair.cpp b/gfx/skia/skia/src/core/SkScan_Antihair.cpp index 72b348fc1e62..97041f262435 100644 --- a/gfx/skia/skia/src/core/SkScan_Antihair.cpp +++ b/gfx/skia/skia/src/core/SkScan_Antihair.cpp @@ -5,7 +5,6 @@ * found in the LICENSE file. */ -#include "include/core/SkColorPriv.h" #include "include/core/SkPoint.h" #include "include/core/SkRect.h" #include "include/core/SkRegion.h" @@ -18,6 +17,7 @@ #include "include/private/base/SkSafe32.h" #include "include/private/base/SkTo.h" #include "src/core/SkBlitter.h" +#include "src/core/SkColorPriv.h" #include "src/core/SkFDot6.h" #include "src/core/SkLineClipper.h" #include "src/core/SkRasterClip.h" diff --git a/gfx/skia/skia/src/core/SkScan_Path.cpp b/gfx/skia/skia/src/core/SkScan_Path.cpp index 5584f297890b..9e9daf6c2512 100644 --- a/gfx/skia/skia/src/core/SkScan_Path.cpp +++ b/gfx/skia/skia/src/core/SkScan_Path.cpp @@ -139,7 +139,7 @@ static void walk_edges(SkEdge* prevHead, SkPathFillType fillType, left = x; } - w += currE->fWinding; + w += static_cast(currE->fWinding); if ((w & windingMask) == 0) { // we finished an interval int width = x - left; @@ -375,20 +375,16 @@ static void PrePostInverseBlitterProc(SkBlitter* blitter, int y, bool isStart) { #pragma warning ( pop ) #endif -static bool operator<(const SkEdge& a, const SkEdge& b) { - int valuea = a.fFirstY; - int valueb = b.fFirstY; - - if (valuea == valueb) { - valuea = a.fX; - valueb = b.fX; +static bool compare_edges(const SkEdge* a, const SkEdge* b) { + if (a->fFirstY != b->fFirstY) { + return a->fFirstY < b->fFirstY; } - return valuea < valueb; + return a->fX < b->fX; } static SkEdge* sort_edges(SkEdge* list[], int count, SkEdge** last) { - SkTQSort(list, list + count); + SkTQSort(list, list + count, compare_edges); // now make the edges linked in sorted order for (int i = 1; i < count; i++) { diff --git a/gfx/skia/skia/src/core/SkStream.cpp b/gfx/skia/skia/src/core/SkStream.cpp index 8b06eba3dff0..22472e3f14a2 100644 --- a/gfx/skia/skia/src/core/SkStream.cpp +++ b/gfx/skia/skia/src/core/SkStream.cpp @@ -41,6 +41,10 @@ bool SkStream::readS32(int32_t* i) { return this->read(i, sizeof(*i)) == sizeof(*i); } +bool SkStream::readS64(int64_t* i) { + return this->read(i, sizeof(*i)) == sizeof(*i); +} + bool SkStream::readScalar(SkScalar* i) { return this->read(i, sizeof(*i)) == sizeof(*i); } diff --git a/gfx/skia/skia/src/core/SkString.cpp b/gfx/skia/skia/src/core/SkString.cpp index 53c1d9ab58f8..6dec147e69a3 100644 --- a/gfx/skia/skia/src/core/SkString.cpp +++ b/gfx/skia/skia/src/core/SkString.cpp @@ -74,7 +74,7 @@ bool SkStrEndsWith(const char string[], const char suffixStr[]) { !strncmp(string + strLen - suffixLen, suffixStr, suffixLen); } -bool SkStrEndsWith(const char string[], const char suffixChar) { +bool SkStrEndsWith(const char string[], char suffixChar) { SkASSERT(string); size_t strLen = strlen(string); if (0 == strLen) { diff --git a/gfx/skia/skia/src/core/SkSwizzlePriv.h b/gfx/skia/skia/src/core/SkSwizzlePriv.h index 682c409adc14..2edc1e097c03 100644 --- a/gfx/skia/skia/src/core/SkSwizzlePriv.h +++ b/gfx/skia/skia/src/core/SkSwizzlePriv.h @@ -8,8 +8,8 @@ #ifndef SkSwizzlePriv_DEFINED #define SkSwizzlePriv_DEFINED -#include "include/private/SkColorData.h" #include "src/base/SkVx.h" +#include "src/core/SkColorData.h" #include diff --git a/gfx/skia/skia/src/core/SkTHash.h b/gfx/skia/skia/src/core/SkTHash.h index b6a1a481c22c..cf2717cfa723 100644 --- a/gfx/skia/skia/src/core/SkTHash.h +++ b/gfx/skia/skia/src/core/SkTHash.h @@ -27,6 +27,10 @@ namespace skia_private { // Traits must have: // - static K GetKey(T) // - static uint32_t Hash(K) +// Traits may also define (both required if either is defined): +// - static bool ShouldGrow(int count, int capacity) +// - static bool ShouldShrink(int count, int capacity) +// , which specify the max/min load factor of the table. // If the key is large and stored inside T, you may want to make K a const&. // Similarly, if T is large you might want it to be a pointer. template @@ -98,7 +102,13 @@ public: // Copy val into the hash table, returning a pointer to the copy now in the table. // If there already is an entry in the table with the same key, we overwrite it. T* set(T val) { - if (4 * fCount >= 3 * fCapacity) { + bool shouldGrow = false; + if constexpr (HasShouldGrow::value) { + shouldGrow = Traits::ShouldGrow(fCount, fCapacity); + } else { + shouldGrow = (4 * fCount >= 3 * fCapacity); + } + if (shouldGrow) { this->resize(fCapacity > 0 ? fCapacity * 2 : 4); } return this->uncheckedSet(std::move(val)); @@ -143,8 +153,16 @@ public: } if (hash == s.fHash && key == Traits::GetKey(*s)) { this->removeSlot(index); - if (4 * fCount <= fCapacity && fCapacity > 4) { - this->resize(fCapacity / 2); + if (fCapacity > 4) { + bool shouldShrink = false; + if constexpr (HasShouldShrink::value) { + shouldShrink = Traits::ShouldShrink(fCount, fCapacity); + } else { + shouldShrink = (4 * fCount <= fCapacity); + } + if (shouldShrink) { + this->resize(fCapacity / 2); + } } return true; } @@ -192,7 +210,14 @@ public: // - Hash tables grow when they exceed 3/4 capacity, not when they are full. void reserve(int n) { int newCapacity = SkNextPow2(n); - if (n * 4 > newCapacity * 3) { + + bool shouldGrow = false; + if constexpr (HasShouldGrow::value) { + shouldGrow = Traits::ShouldGrow(n, newCapacity); + } else { + shouldGrow = (n * 4 > newCapacity * 3); + } + if (shouldGrow) { newCapacity *= 2; } @@ -274,6 +299,27 @@ public: }; private: + template struct HasShouldGrow : std::false_type {}; + template struct HasShouldShrink : std::false_type {}; + + template + struct HasShouldGrow< + U, + std::void_t(), std::declval()))>> + : std::true_type { + static_assert(HasShouldShrink::value, + "The traits class must also provide ShouldShrink() method."); + }; + + template + struct HasShouldShrink< + U, + std::void_t(), std::declval()))>> + : std::true_type { + static_assert(HasShouldGrow::value, + "The traits class must also provide ShouldGrow() method."); + }; + // Finds the first non-empty slot for an iterator. int firstPopulatedSlot() const { for (int i = 0; i < fCapacity; i++) { diff --git a/gfx/skia/skia/src/core/SkTextBlob.cpp b/gfx/skia/skia/src/core/SkTextBlob.cpp index f20b6cd415eb..82a92eb80ad9 100644 --- a/gfx/skia/skia/src/core/SkTextBlob.cpp +++ b/gfx/skia/skia/src/core/SkTextBlob.cpp @@ -52,7 +52,7 @@ size_t SkTextBlob::RunRecord::StorageSize(uint32_t glyphCount, uint32_t textSize SkSafeMath* safe) { static_assert(SkIsAlign4(sizeof(SkScalar)), "SkScalar size alignment"); - auto glyphSize = safe->mul(glyphCount, sizeof(uint16_t)), + auto glyphSize = safe->mul(glyphCount, sizeof(SkGlyphID)), posSize = safe->mul(PosCount(glyphCount, positioning, safe), sizeof(SkScalar)); // RunRecord object + (aligned) glyph buffer + position buffer @@ -93,7 +93,7 @@ void SkTextBlob::RunRecord::validate(const uint8_t* storageTop) const { SkASSERT(kRunRecordMagic == fMagic); SkASSERT((const uint8_t*)NextUnchecked(this) <= storageTop); - SkASSERT(glyphBuffer() + fCount <= (uint16_t*)posBuffer()); + SkASSERT(glyphBuffer() + fCount <= (SkGlyphID*)posBuffer()); SkASSERT(posBuffer() + fCount * ScalarsPerGlyph(positioning()) <= (const SkScalar*)NextUnchecked(this)); if (isExtended()) { @@ -281,7 +281,7 @@ SkRect SkTextBlobBuilder::TightRunBounds(const SkTextBlob::RunRecord& run) { SkRect bounds; if (SkTextBlob::kDefault_Positioning == run.positioning()) { - font.measureText(run.glyphBuffer(), run.glyphCount() * sizeof(uint16_t), + font.measureText(run.glyphBuffer(), run.glyphCount() * sizeof(SkGlyphID), SkTextEncoding::kGlyphID, &bounds); return bounds.makeOffset(run.offset().x(), run.offset().y()); } @@ -685,7 +685,7 @@ void SkTextBlobPriv::Flatten(const SkTextBlob& blob, SkWriteBuffer& buffer) { SkFontPriv::Flatten(it.font(), buffer); - buffer.writeByteArray(it.glyphs(), it.glyphCount() * sizeof(uint16_t)); + buffer.writeByteArray(it.glyphs(), it.glyphCount() * sizeof(SkGlyphID)); buffer.writeByteArray(it.pos(), it.glyphCount() * sizeof(SkScalar) * SkTextBlob::ScalarsPerGlyph( @@ -733,7 +733,7 @@ sk_sp SkTextBlobPriv::MakeFromBuffer(SkReadBuffer& reader) { // Compute the expected size of the buffer and ensure we have enough to deserialize // a run before allocating it. - const size_t glyphSize = safe.mul(glyphCount, sizeof(uint16_t)), + const size_t glyphSize = safe.mul(glyphCount, sizeof(SkGlyphID)), posSize = safe.mul(glyphCount, safe.mul(sizeof(SkScalar), SkTextBlob::ScalarsPerGlyph(pos))), diff --git a/gfx/skia/skia/src/core/SkTextBlobPriv.h b/gfx/skia/skia/src/core/SkTextBlobPriv.h index 7e08192033c9..bb09c220c129 100644 --- a/gfx/skia/skia/src/core/SkTextBlobPriv.h +++ b/gfx/skia/skia/src/core/SkTextBlobPriv.h @@ -95,17 +95,17 @@ public: return static_cast(fFlags & kPositioning_Mask); } - uint16_t* glyphBuffer() const { + SkGlyphID* glyphBuffer() const { static_assert(SkIsAlignPtr(sizeof(RunRecord)), ""); // Glyphs are stored immediately following the record. - return reinterpret_cast(const_cast(this) + 1); + return reinterpret_cast(const_cast(this) + 1); } // can be aliased with pointBuffer() or xformBuffer() SkScalar* posBuffer() const { // Position scalars follow the (aligned) glyph buffer. return reinterpret_cast(reinterpret_cast(this->glyphBuffer()) + - SkAlign4(fCount * sizeof(uint16_t))); + SkAlign4(fCount * sizeof(SkGlyphID))); } // alias for posBuffer() @@ -202,7 +202,7 @@ public: SkASSERT(!this->done()); return fCurrentRun->glyphCount(); } - const uint16_t* glyphs() const { + const SkGlyphID* glyphs() const { SkASSERT(!this->done()); return fCurrentRun->glyphBuffer(); } diff --git a/gfx/skia/skia/src/core/SkTraceEventCommon.h b/gfx/skia/skia/src/core/SkTraceEventCommon.h index 3b24d6588292..e6cd5a2190da 100644 --- a/gfx/skia/skia/src/core/SkTraceEventCommon.h +++ b/gfx/skia/skia/src/core/SkTraceEventCommon.h @@ -184,8 +184,6 @@ public: gUsePerfettoTrackEvents = usePerfettoTrackEvents; return true; #else // !SK_ANDROID_FRAMEWORK_USE_PERFETTO - // Note: please reach out to skia-android@google.com if you encounter this unexpectedly. - SkDebugf("Tracing Skia with Perfetto is not supported in this environment (host build?)"); return false; #endif // SK_ANDROID_FRAMEWORK_USE_PERFETTO } diff --git a/gfx/skia/skia/src/core/SkTypeface.cpp b/gfx/skia/skia/src/core/SkTypeface.cpp index 4abadecbfa60..f2edaae30a2d 100644 --- a/gfx/skia/skia/src/core/SkTypeface.cpp +++ b/gfx/skia/skia/src/core/SkTypeface.cpp @@ -88,8 +88,7 @@ protected: std::unique_ptr onCreateScalerContext( const SkScalerContextEffects& effects, const SkDescriptor* desc) const override { - return SkScalerContext::MakeEmpty( - sk_ref_sp(const_cast(this)), effects, desc); + return SkScalerContext::MakeEmpty(*const_cast(this), effects, desc); } void onFilterRec(SkScalerContextRec*) const override { } std::unique_ptr onGetAdvancedMetrics() const override { @@ -357,7 +356,7 @@ std::unique_ptr SkTypeface::createScalerContext( std::unique_ptr SkTypeface::onCreateScalerContextAsProxyTypeface (const SkScalerContextEffects&, const SkDescriptor*, - sk_sp) const { + SkTypeface*) const { SK_ABORT("Not implemented."); } @@ -445,7 +444,7 @@ int SkTypeface::getUnitsPerEm() const { return this->onGetUPEM(); } -bool SkTypeface::getKerningPairAdjustments(const uint16_t glyphs[], int count, +bool SkTypeface::getKerningPairAdjustments(const SkGlyphID glyphs[], int count, int32_t adjustments[]) const { SkASSERT(count >= 0); // check for the only legal way to pass a nullptr.. everything is 0 @@ -472,6 +471,38 @@ bool SkTypeface::getPostScriptName(SkString* name) const { return this->onGetPostScriptName(name); } +int SkTypeface::getResourceName(SkString* resourceName) const { + return this->onGetResourceName(resourceName); +} + +int SkTypeface::onGetResourceName(SkString* resourceName) const { + return 0; +} + +SkFontStyle SkTypeface::fontStyle() const { + return this->onGetFontStyle(); +} + +SkFontStyle SkTypeface::onGetFontStyle() const { + return fStyle; +} + +bool SkTypeface::isBold() const { + return this->onGetFontStyle().weight() >= SkFontStyle::kSemiBold_Weight; +} + +bool SkTypeface::isItalic() const { + return this->onGetFontStyle().slant() != SkFontStyle::kUpright_Slant; +} + +bool SkTypeface::isFixedPitch() const { + return this->onGetFixedPitch(); +} + +bool SkTypeface::onGetFixedPitch() const { + return fIsFixedPitch; +} + void SkTypeface::getGlyphToUnicodeMap(SkUnichar* dst) const { sk_bzero(dst, sizeof(SkUnichar) * this->countGlyphs()); } @@ -500,7 +531,7 @@ std::unique_ptr SkTypeface::getAdvancedMetrics() cons return result; } -bool SkTypeface::onGetKerningPairAdjustments(const uint16_t glyphs[], int count, +bool SkTypeface::onGetKerningPairAdjustments(const SkGlyphID glyphs[], int count, int32_t adjustments[]) const { return false; } diff --git a/gfx/skia/skia/src/core/SkTypeface_remote.cpp b/gfx/skia/skia/src/core/SkTypeface_remote.cpp index e38b57b7ff51..a13a7ae69ed1 100644 --- a/gfx/skia/skia/src/core/SkTypeface_remote.cpp +++ b/gfx/skia/skia/src/core/SkTypeface_remote.cpp @@ -24,11 +24,11 @@ class SkArenaAlloc; class SkDescriptor; class SkPath; -SkScalerContextProxy::SkScalerContextProxy(sk_sp tf, +SkScalerContextProxy::SkScalerContextProxy(SkTypeface& tf, const SkScalerContextEffects& effects, const SkDescriptor* desc, sk_sp manager) - : SkScalerContext{std::move(tf), effects, desc} + : SkScalerContext{tf, effects, desc} , fDiscardableManager{std::move(manager)} {} SkScalerContext::GlyphMetrics SkScalerContextProxy::generateMetrics(const SkGlyph& glyph, diff --git a/gfx/skia/skia/src/core/SkTypeface_remote.h b/gfx/skia/skia/src/core/SkTypeface_remote.h index afab6c0811e7..577e6b42b909 100644 --- a/gfx/skia/skia/src/core/SkTypeface_remote.h +++ b/gfx/skia/skia/src/core/SkTypeface_remote.h @@ -38,7 +38,7 @@ struct SkFontMetrics; class SkScalerContextProxy : public SkScalerContext { public: - SkScalerContextProxy(sk_sp tf, + SkScalerContextProxy(SkTypeface& tf, const SkScalerContextEffects& effects, const SkDescriptor* desc, sk_sp manager); @@ -146,7 +146,7 @@ protected: const SkScalerContextEffects& effects, const SkDescriptor* desc) const override { return std::make_unique( - sk_ref_sp(const_cast(this)), effects, desc, fDiscardableManager); + *const_cast(this), effects, desc, fDiscardableManager); } void onFilterRec(SkScalerContextRec* rec) const override { // The rec filtering is already applied by the server when generating diff --git a/gfx/skia/skia/src/core/SkUnPreMultiply.cpp b/gfx/skia/skia/src/core/SkUnPreMultiply.cpp index 01950ddb0a4d..583be5f28b91 100644 --- a/gfx/skia/skia/src/core/SkUnPreMultiply.cpp +++ b/gfx/skia/skia/src/core/SkUnPreMultiply.cpp @@ -6,7 +6,7 @@ */ #include "include/core/SkUnPreMultiply.h" -#include "include/core/SkColorPriv.h" +#include "src/core/SkColorPriv.h" SkColor SkUnPreMultiply::PMColorToColor(SkPMColor c) { const unsigned a = SkGetPackedA32(c); diff --git a/gfx/skia/skia/src/core/SkVertices.cpp b/gfx/skia/skia/src/core/SkVertices.cpp index 3492acbc9234..e4d5c2b2dbff 100644 --- a/gfx/skia/skia/src/core/SkVertices.cpp +++ b/gfx/skia/skia/src/core/SkVertices.cpp @@ -7,16 +7,20 @@ #include "include/core/SkVertices.h" #include "include/core/SkTypes.h" +#include "include/private/base/SkDebug.h" #include "include/private/base/SkMalloc.h" #include "include/private/base/SkTo.h" #include "src/base/SkSafeMath.h" +#include "src/base/SkVx.h" #include "src/core/SkPicturePriv.h" #include "src/core/SkReadBuffer.h" #include "src/core/SkSafeRange.h" #include "src/core/SkVerticesPriv.h" #include "src/core/SkWriteBuffer.h" +#include #include +#include #include #include @@ -82,7 +86,10 @@ struct SkVertices::Sizes { } } - bool isValid() const { return fTotal != 0; } + bool isValid() const { + SkASSERT(fTotal >= fVSize); + return fVSize > 0; + } size_t fTotal = 0; // size of entire SkVertices allocation (obj + arrays) size_t fArrays; // size of all the data arrays (V + D + T + C + I) @@ -212,8 +219,41 @@ sk_sp SkVertices::MakeCopy(VertexMode mode, int vertexCount, sk_careful_memcpy(builder.positions(), pos, sizes.fVSize); sk_careful_memcpy(builder.texCoords(), texs, sizes.fTSize); sk_careful_memcpy(builder.colors(), colors, sizes.fCSize); - size_t isize = (mode == kTriangleFan_VertexMode) ? sizes.fBuilderTriFanISize : sizes.fISize; - sk_careful_memcpy(builder.indices(), indices, isize); + + // The builder can update the number of indices. + const size_t isize = ((mode == kTriangleFan_VertexMode) ? sizes.fBuilderTriFanISize + : sizes.fISize), + icount = isize / sizeof(uint16_t); + + // Ensure that indices are valid for the given vertex count. + SkASSERT(vertexCount > 0); + const uint16_t max_index = SkToU16(vertexCount - 1); + + size_t i = 0; + for (; i + 8 <= icount; i += 8) { + const skvx::ushort8 ind8 = skvx::ushort8::Load(indices + i), + clamped_ind8 = skvx::min(ind8, max_index); + clamped_ind8.store(builder.indices() + i); + } + if (i + 4 <= icount) { + const skvx::ushort4 ind4 = skvx::ushort4::Load(indices + i), + clamped_ind4 = skvx::min(ind4, max_index); + clamped_ind4.store(builder.indices() + i); + + i += 4; + } + if (i + 2 <= icount) { + const skvx::ushort2 ind2 = skvx::ushort2::Load(indices + i), + clamped_ind2 = skvx::min(ind2, max_index); + clamped_ind2.store(builder.indices() + i); + + i += 2; + } + if (i < icount) { + builder.indices()[i] = std::min(indices[i], max_index); + SkDEBUGCODE(i += 1); + } + SkASSERT(i == icount); return builder.detach(); } diff --git a/gfx/skia/skia/src/effects/SkEmbossMaskFilter.h b/gfx/skia/skia/src/effects/SkEmbossMaskFilter.h index 199d10abdf47..3903b218e4be 100644 --- a/gfx/skia/skia/src/effects/SkEmbossMaskFilter.h +++ b/gfx/skia/skia/src/effects/SkEmbossMaskFilter.h @@ -10,7 +10,6 @@ #include "include/core/SkFlattenable.h" #include "include/core/SkMaskFilter.h" -#include "include/core/SkRect.h" #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" #include "src/core/SkMask.h" diff --git a/gfx/skia/skia/src/effects/SkTableMaskFilter.cpp b/gfx/skia/skia/src/effects/SkTableMaskFilter.cpp index 59b1b07db31e..e2055988ff33 100644 --- a/gfx/skia/skia/src/effects/SkTableMaskFilter.cpp +++ b/gfx/skia/skia/src/effects/SkTableMaskFilter.cpp @@ -7,13 +7,16 @@ #include "include/effects/SkTableMaskFilter.h" +#include "include/core/SkColorFilter.h" #include "include/core/SkFlattenable.h" +#include "include/core/SkImageFilter.h" #include "include/core/SkMaskFilter.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/effects/SkImageFilters.h" #include "include/private/base/SkAlign.h" #include "include/private/base/SkFixed.h" #include "include/private/base/SkFloatingPoint.h" @@ -36,6 +39,7 @@ public: SkMask::Format getFormat() const override; bool filterMask(SkMaskBuilder*, const SkMask&, const SkMatrix&, SkIPoint*) const override; SkMaskFilterBase::Type type() const override { return SkMaskFilterBase::Type::kTable; } + sk_sp asImageFilter(const SkMatrix&) const override; protected: ~SkTableMaskFilterImpl() override; @@ -50,6 +54,8 @@ private: uint8_t fTable[256]; using INHERITED = SkMaskFilter; + + friend class SkTableMaskFilter; }; SkTableMaskFilterImpl::SkTableMaskFilterImpl() { @@ -122,6 +128,13 @@ sk_sp SkTableMaskFilterImpl::CreateProc(SkReadBuffer& buffer) { return sk_sp(SkTableMaskFilter::Create(table)); } +sk_sp SkTableMaskFilterImpl::asImageFilter(const SkMatrix&) const { + sk_sp colorFilter = SkColorFilters::TableARGB(fTable, + nullptr, + nullptr, + nullptr); + return SkImageFilters::ColorFilter(colorFilter, nullptr); +} /////////////////////////////////////////////////////////////////////////////// SkMaskFilter* SkTableMaskFilter::Create(const uint8_t table[256]) { @@ -185,3 +198,9 @@ void SkTableMaskFilter::MakeClipTable(uint8_t table[256], uint8_t min, SkDebugf("\n\n"); #endif } + +void SkTableMaskFilter::RegisterFlattenables() { + SK_REGISTER_FLATTENABLE(SkTableMaskFilterImpl); + // Previous name + SkFlattenable::Register("SkTableMF", SkTableMaskFilterImpl::CreateProc); +} diff --git a/gfx/skia/skia/src/effects/colorfilters/SkBlendModeColorFilter.cpp b/gfx/skia/skia/src/effects/colorfilters/SkBlendModeColorFilter.cpp index b8b0d34e7698..dd09844cef60 100644 --- a/gfx/skia/skia/src/effects/colorfilters/SkBlendModeColorFilter.cpp +++ b/gfx/skia/skia/src/effects/colorfilters/SkBlendModeColorFilter.cpp @@ -12,7 +12,6 @@ #include "include/core/SkColorFilter.h" #include "include/core/SkColorSpace.h" #include "include/core/SkRefCnt.h" -#include "include/private/SkColorData.h" #include "src/core/SkBlendModePriv.h" #include "src/core/SkColorSpacePriv.h" #include "src/core/SkColorSpaceXformSteps.h" @@ -25,13 +24,6 @@ #include "src/core/SkWriteBuffer.h" #include "src/effects/colorfilters/SkColorFilterBase.h" -template -static SkRGBA4f map_color(const SkColor4f& c, SkColorSpace* src, SkColorSpace* dst) { - SkRGBA4f color = {c.fR, c.fG, c.fB, c.fA}; - SkColorSpaceXformSteps(src, kUnpremul_SkAlphaType, dst, kDstAT).apply(color.vec()); - return color; -} - SkBlendModeColorFilter::SkBlendModeColorFilter(const SkColor4f& color, SkBlendMode mode) : fColor(color), fMode(mode) {} @@ -78,7 +70,9 @@ sk_sp SkBlendModeColorFilter::CreateProc(SkReadBuffer& buffer) { bool SkBlendModeColorFilter::appendStages(const SkStageRec& rec, bool shaderIsOpaque) const { rec.fPipeline->append(SkRasterPipelineOp::move_src_dst); - SkPMColor4f color = map_color(fColor, sk_srgb_singleton(), rec.fDstCS); + SkColor4f color = fColor; + SkColorSpaceXformSteps(sk_srgb_singleton(), kUnpremul_SkAlphaType, + rec.fDstCS, kPremul_SkAlphaType).apply(color.vec()); rec.fPipeline->appendConstantColor(rec.fAlloc, color.vec()); SkBlendMode_AppendStages(fMode, rec.fPipeline); return true; @@ -94,8 +88,10 @@ sk_sp SkColorFilters::Blend(const SkColor4f& color, } // First map to sRGB to simplify storage in the actual SkColorFilter instance, staying unpremul - // until the final dst color space is known when actually filtering. - SkColor4f srgb = map_color(color, colorSpace.get(), sk_srgb_singleton()); + // until the final dst color space is known when actually filtering. Also pin the alpha to [0,1] + SkColor4f srgb = color.pinAlpha(); + SkColorSpaceXformSteps(colorSpace.get(), kUnpremul_SkAlphaType, + sk_srgb_singleton(), kUnpremul_SkAlphaType).apply(srgb.vec()); // Next collapse some modes if possible float alpha = srgb.fA; diff --git a/gfx/skia/skia/src/effects/colorfilters/SkColorFilterBase.cpp b/gfx/skia/skia/src/effects/colorfilters/SkColorFilterBase.cpp index 08ba76f09f8c..eeed119e5ca4 100644 --- a/gfx/skia/skia/src/effects/colorfilters/SkColorFilterBase.cpp +++ b/gfx/skia/skia/src/effects/colorfilters/SkColorFilterBase.cpp @@ -10,9 +10,9 @@ #include "include/core/SkColorSpace.h" // IWYU pragma: keep #include "include/core/SkColorType.h" #include "include/core/SkSurfaceProps.h" -#include "include/private/SkColorData.h" #include "include/private/base/SkAssert.h" #include "src/base/SkArenaAlloc.h" +#include "src/core/SkColorData.h" #include "src/core/SkEffectPriv.h" #include "src/core/SkRasterPipeline.h" #include "src/core/SkRasterPipelineOpContexts.h" @@ -41,7 +41,7 @@ SkPMColor4f SkColorFilterBase::onFilterColor4f(const SkPMColor4f& color, if (as_CFB(this)->appendStages(rec, color.fA == 1)) { SkPMColor4f dst; - SkRasterPipeline_MemoryCtx dstPtr = { &dst, 0 }; + SkRasterPipelineContexts::MemoryCtx dstPtr = {&dst, 0}; pipeline.append(SkRasterPipelineOp::store_f32, &dstPtr); pipeline.run(0,0, 1,1); return dst; diff --git a/gfx/skia/skia/src/effects/colorfilters/SkColorFilterBase.h b/gfx/skia/skia/src/effects/colorfilters/SkColorFilterBase.h index 0cbd6b0e6ef2..04beb1beeeec 100644 --- a/gfx/skia/skia/src/effects/colorfilters/SkColorFilterBase.h +++ b/gfx/skia/skia/src/effects/colorfilters/SkColorFilterBase.h @@ -12,7 +12,7 @@ #include "include/core/SkColorFilter.h" #include "include/core/SkFlattenable.h" #include "include/core/SkRefCnt.h" -#include "include/private/SkColorData.h" +#include "src/core/SkColorData.h" #include diff --git a/gfx/skia/skia/src/effects/colorfilters/SkRuntimeColorFilter.cpp b/gfx/skia/skia/src/effects/colorfilters/SkRuntimeColorFilter.cpp index 687e23d89160..6bd7c273df9c 100644 --- a/gfx/skia/skia/src/effects/colorfilters/SkRuntimeColorFilter.cpp +++ b/gfx/skia/skia/src/effects/colorfilters/SkRuntimeColorFilter.cpp @@ -4,10 +4,8 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ - #include "src/effects/colorfilters/SkRuntimeColorFilter.h" -#include "include/core/SkAlphaType.h" #include "include/core/SkCapabilities.h" #include "include/core/SkColor.h" #include "include/core/SkColorFilter.h" @@ -17,19 +15,21 @@ #include "include/effects/SkLumaColorFilter.h" #include "include/effects/SkOverdrawColorFilter.h" #include "include/effects/SkRuntimeEffect.h" -#include "include/private/SkColorData.h" #include "include/private/SkSLSampleUsage.h" #include "include/private/base/SkDebug.h" #include "include/private/base/SkFloatingPoint.h" #include "include/private/base/SkTArray.h" +#include "src/core/SkColorData.h" #include "src/core/SkEffectPriv.h" #include "src/core/SkKnownRuntimeEffects.h" +#include "src/core/SkPicturePriv.h" #include "src/core/SkReadBuffer.h" #include "src/core/SkRuntimeEffectPriv.h" #include "src/core/SkWriteBuffer.h" #include "src/shaders/SkShaderBase.h" #include "src/sksl/codegen/SkSLRasterPipelineBuilder.h" +#include #include #include @@ -73,7 +73,13 @@ bool SkRuntimeColorFilter::onIsAlphaUnchanged() const { } void SkRuntimeColorFilter::flatten(SkWriteBuffer& buffer) const { - buffer.writeString(fEffect->source().c_str()); + if (SkKnownRuntimeEffects::IsSkiaKnownRuntimeEffect(fEffect->fStableKey)) { + // We only serialize Skia-internal stableKeys. First party stable keys are not serialized. + buffer.write32(fEffect->fStableKey); + } else { + buffer.write32(0); + buffer.writeString(fEffect->source().c_str()); + } buffer.writeDataAsByteArray(fUniforms.get()); SkRuntimeEffectPriv::WriteChildEffects(buffer, fChildren); } @@ -85,17 +91,28 @@ sk_sp SkRuntimeColorFilter::CreateProc(SkReadBuffer& buffer) { return nullptr; } - SkString sksl; - buffer.readString(&sksl); - sk_sp uniforms = buffer.readByteArrayAsData(); + sk_sp effect; + if (!buffer.isVersionLT(SkPicturePriv::kSerializeStableKeys)) { + uint32_t candidateStableKey = buffer.readUInt(); + effect = SkKnownRuntimeEffects::MaybeGetKnownRuntimeEffect(candidateStableKey); + if (!effect && !buffer.validate(candidateStableKey == 0)) { + return nullptr; + } + } - auto effect = SkMakeCachedRuntimeEffect(SkRuntimeEffect::MakeForColorFilter, std::move(sksl)); + if (!effect) { + SkString sksl; + buffer.readString(&sksl); + effect = SkMakeCachedRuntimeEffect(SkRuntimeEffect::MakeForColorFilter, std::move(sksl)); + } if constexpr (!kLenientSkSLDeserialization) { if (!buffer.validate(effect != nullptr)) { return nullptr; } } + sk_sp uniforms = buffer.readByteArrayAsData(); + skia_private::STArray<4, SkRuntimeEffect::ChildPtr> children; if (!SkRuntimeEffectPriv::ReadChildEffects(buffer, effect.get(), &children)) { return nullptr; diff --git a/gfx/skia/skia/src/effects/colorfilters/SkTableColorFilter.cpp b/gfx/skia/skia/src/effects/colorfilters/SkTableColorFilter.cpp index dac07e4f410e..37f55fdb3505 100644 --- a/gfx/skia/skia/src/effects/colorfilters/SkTableColorFilter.cpp +++ b/gfx/skia/skia/src/effects/colorfilters/SkTableColorFilter.cpp @@ -28,7 +28,8 @@ bool SkTableColorFilter::appendStages(const SkStageRec& rec, bool shaderIsOpaque p->append(SkRasterPipelineOp::unpremul); } - SkRasterPipeline_TablesCtx* tables = rec.fAlloc->make(); + SkRasterPipelineContexts::TablesCtx* tables = + rec.fAlloc->make(); tables->a = fTable->alphaTable(); tables->r = fTable->redTable(); tables->g = fTable->greenTable(); diff --git a/gfx/skia/skia/src/effects/colorfilters/SkWorkingFormatColorFilter.cpp b/gfx/skia/skia/src/effects/colorfilters/SkWorkingFormatColorFilter.cpp index cd2798a65f92..47eb61d4dfb3 100644 --- a/gfx/skia/skia/src/effects/colorfilters/SkWorkingFormatColorFilter.cpp +++ b/gfx/skia/skia/src/effects/colorfilters/SkWorkingFormatColorFilter.cpp @@ -12,6 +12,7 @@ #include "include/core/SkColorType.h" #include "include/core/SkImageInfo.h" #include "include/core/SkRefCnt.h" +#include "include/core/SkScalar.h" #include "include/private/base/SkAssert.h" #include "modules/skcms/skcms.h" #include "src/base/SkArenaAlloc.h" @@ -27,37 +28,15 @@ SkWorkingFormatColorFilter::SkWorkingFormatColorFilter(sk_sp child, const skcms_TransferFunction* tf, const skcms_Matrix3x3* gamut, - const SkAlphaType* at) { + const SkAlphaType* at) + : fWorkingFormatCalculator(tf, gamut, at) { SkASSERT(child); fChild = std::move(child); - if (tf) { - fTF = *tf; - fUseDstTF = false; - } - if (gamut) { - fGamut = *gamut; - fUseDstGamut = false; - } - if (at) { - fAT = *at; - fUseDstAT = false; - } } sk_sp SkWorkingFormatColorFilter::workingFormat(const sk_sp& dstCS, - SkAlphaType* at) const { - skcms_TransferFunction tf = fTF; - skcms_Matrix3x3 gamut = fGamut; - - if (fUseDstTF) { - SkAssertResult(dstCS->isNumericalTransferFn(&tf)); - } - if (fUseDstGamut) { - SkAssertResult(dstCS->toXYZD50(&gamut)); - } - - *at = fUseDstAT ? kPremul_SkAlphaType : fAT; - return SkColorSpace::MakeRGB(tf, gamut); + SkAlphaType* outAT) const { + return fWorkingFormatCalculator.workingFormat(dstCS, outAT); } bool SkWorkingFormatColorFilter::appendStages(const SkStageRec& rec, bool shaderIsOpaque) const { @@ -129,18 +108,7 @@ bool SkWorkingFormatColorFilter::onAsAColorMatrix(float matrix[20]) const { void SkWorkingFormatColorFilter::flatten(SkWriteBuffer& buffer) const { buffer.writeFlattenable(fChild.get()); - buffer.writeBool(fUseDstTF); - buffer.writeBool(fUseDstGamut); - buffer.writeBool(fUseDstAT); - if (!fUseDstTF) { - buffer.writeScalarArray(&fTF.g, 7); - } - if (!fUseDstGamut) { - buffer.writeScalarArray(&fGamut.vals[0][0], 9); - } - if (!fUseDstAT) { - buffer.writeInt(fAT); - } + fWorkingFormatCalculator.flatten(buffer); } sk_sp SkWorkingFormatColorFilter::CreateProc(SkReadBuffer& buffer) { @@ -153,10 +121,10 @@ sk_sp SkWorkingFormatColorFilter::CreateProc(SkReadBuffer& buffer SkAlphaType at; if (!useDstTF) { - buffer.readScalarArray(&tf.g, 7); + buffer.readScalarArray(&tf.g, sizeof(skcms_TransferFunction) / sizeof(SkScalar)); } if (!useDstGamut) { - buffer.readScalarArray(&gamut.vals[0][0], 9); + buffer.readScalarArray(&gamut.vals[0][0], sizeof(skcms_Matrix3x3) / sizeof(SkScalar)); } if (!useDstAT) { at = buffer.read32LE(kLastEnum_SkAlphaType); @@ -185,3 +153,55 @@ sk_sp SkColorFilterPriv::WithWorkingFormat(sk_sp c void SkRegisterWorkingFormatColorFilterFlattenable() { SK_REGISTER_FLATTENABLE(SkWorkingFormatColorFilter); } + +SkWorkingFormatCalculator::SkWorkingFormatCalculator(const skcms_TransferFunction* tf, + const skcms_Matrix3x3* gamut, + const SkAlphaType* at) { + if (tf) { + fTF = *tf; + fUseDstTF = false; + } + if (gamut) { + fGamut = *gamut; + fUseDstGamut = false; + } + if (at) { + fAT = *at; + fUseDstAT = false; + } +} + +sk_sp SkWorkingFormatCalculator::workingFormat(const sk_sp& dstCS, + SkAlphaType* outAT) const { + skcms_TransferFunction tf; + skcms_Matrix3x3 gamut; + + if (fUseDstTF) { + SkAssertResult(dstCS->isNumericalTransferFn(&tf)); + } else { + tf = fTF; + } + if (fUseDstGamut) { + SkAssertResult(dstCS->toXYZD50(&gamut)); + } else { + gamut = fGamut; + } + *outAT = fUseDstAT ? kPremul_SkAlphaType : fAT; + + return SkColorSpace::MakeRGB(tf, gamut); +} + +void SkWorkingFormatCalculator::flatten(SkWriteBuffer& buffer) const { + buffer.writeBool(fUseDstTF); + buffer.writeBool(fUseDstGamut); + buffer.writeBool(fUseDstAT); + if (!fUseDstTF) { + buffer.writeScalarArray(&fTF.g, sizeof(skcms_TransferFunction) / sizeof(SkScalar)); + } + if (!fUseDstGamut) { + buffer.writeScalarArray(&fGamut.vals[0][0], sizeof(skcms_Matrix3x3) / sizeof(SkScalar)); + } + if (!fUseDstAT) { + buffer.writeInt(fAT); + } +} diff --git a/gfx/skia/skia/src/effects/colorfilters/SkWorkingFormatColorFilter.h b/gfx/skia/skia/src/effects/colorfilters/SkWorkingFormatColorFilter.h index 2736de5ca025..1dca569f6e6e 100644 --- a/gfx/skia/skia/src/effects/colorfilters/SkWorkingFormatColorFilter.h +++ b/gfx/skia/skia/src/effects/colorfilters/SkWorkingFormatColorFilter.h @@ -11,8 +11,8 @@ #include "include/core/SkColorFilter.h" #include "include/core/SkFlattenable.h" #include "include/core/SkRefCnt.h" -#include "include/private/SkColorData.h" #include "modules/skcms/skcms.h" +#include "src/core/SkColorData.h" #include "src/effects/colorfilters/SkColorFilterBase.h" class SkColorSpace; @@ -22,6 +22,25 @@ enum SkAlphaType : int; enum class SkBlendMode; struct SkStageRec; +class SkWorkingFormatCalculator { +public: + SkWorkingFormatCalculator(const skcms_TransferFunction* tf, + const skcms_Matrix3x3* gamut, + const SkAlphaType* at); + + sk_sp workingFormat(const sk_sp& dstCS, SkAlphaType* outAT) const; + + void flatten(SkWriteBuffer& buffer) const; + +private: + skcms_TransferFunction fTF; + bool fUseDstTF = true; + skcms_Matrix3x3 fGamut; + bool fUseDstGamut = true; + SkAlphaType fAT; + bool fUseDstAT = true; +}; + class SkWorkingFormatColorFilter final : public SkColorFilterBase { public: SkWorkingFormatColorFilter(sk_sp child, @@ -29,7 +48,7 @@ public: const skcms_Matrix3x3* gamut, const SkAlphaType* at); - sk_sp workingFormat(const sk_sp& dstCS, SkAlphaType* at) const; + sk_sp workingFormat(const sk_sp& dstCS, SkAlphaType* outAT) const; SkColorFilterBase::Type type() const override { return SkColorFilterBase::Type::kWorkingFormat; @@ -57,12 +76,7 @@ private: bool onAsAColorMatrix(float[20]) const override; sk_sp fChild; - skcms_TransferFunction fTF; - bool fUseDstTF = true; - skcms_Matrix3x3 fGamut; - bool fUseDstGamut = true; - SkAlphaType fAT; - bool fUseDstAT = true; + SkWorkingFormatCalculator fWorkingFormatCalculator; }; #endif diff --git a/gfx/skia/skia/src/effects/imagefilters/SkDisplacementMapImageFilter.cpp b/gfx/skia/skia/src/effects/imagefilters/SkDisplacementMapImageFilter.cpp index bb75f615f7a8..f1c975657ba5 100644 --- a/gfx/skia/skia/src/effects/imagefilters/SkDisplacementMapImageFilter.cpp +++ b/gfx/skia/skia/src/effects/imagefilters/SkDisplacementMapImageFilter.cpp @@ -20,6 +20,7 @@ #include "include/core/SkSize.h" #include "include/core/SkTypes.h" #include "include/effects/SkRuntimeEffect.h" +#include "include/private/base/SkFloatingPoint.h" #include "include/private/base/SkSpan_impl.h" #include "src/core/SkImageFilterTypes.h" #include "src/core/SkImageFilter_Base.h" @@ -42,12 +43,16 @@ class SkDisplacementMapImageFilter final : public SkImageFilter_Base { static constexpr SkSamplingOptions kDisplacementSampling{SkFilterMode::kNearest}; public: - SkDisplacementMapImageFilter(SkColorChannel xChannel, SkColorChannel yChannel, - SkScalar scale, sk_sp inputs[2]) + SkDisplacementMapImageFilter(SkColorChannel xChannel, + SkColorChannel yChannel, + SkScalar scale, + sk_sp inputs[2]) : SkImageFilter_Base(inputs, 2) , fXChannel(xChannel) , fYChannel(yChannel) - , fScale(scale) {} + , fScale(scale) { + SkASSERT(SkIsFinite(fScale)); + } SkRect computeFastBounds(const SkRect& src) const override; @@ -148,6 +153,9 @@ sk_sp SkImageFilters::DisplacementMap( !channel_selector_type_is_valid(yChannelSelector)) { return nullptr; } + if (!SkIsFinite(scale)) { + return nullptr; + } sk_sp inputs[2] = { std::move(displacement), std::move(color) }; sk_sp filter(new SkDisplacementMapImageFilter(xChannelSelector, yChannelSelector, diff --git a/gfx/skia/skia/src/encode/SkJpegEncoderImpl.cpp b/gfx/skia/skia/src/encode/SkJpegEncoderImpl.cpp index 7434ef30c28c..406a93b01ca1 100644 --- a/gfx/skia/skia/src/encode/SkJpegEncoderImpl.cpp +++ b/gfx/skia/skia/src/encode/SkJpegEncoderImpl.cpp @@ -26,6 +26,8 @@ #include "src/base/SkMSAN.h" #include "src/codec/SkJpegConstants.h" #include "src/codec/SkJpegPriv.h" +#include "src/core/SkConvertPixels.h" +#include "src/core/SkImageInfoPriv.h" #include "src/encode/SkImageEncoderFns.h" #include "src/encode/SkImageEncoderPriv.h" #include "src/encode/SkJPEGWriteUtility.h" @@ -66,12 +68,13 @@ public: skjpeg_error_mgr* errorMgr() { return &fErrMgr; } - transform_scanline_proc proc() const { return fProc; } + bool shouldUseColorXform() { return fUseColorXform; } + bool colorTransformProc(void* dst, const void* src, int width); ~SkJpegEncoderMgr() { jpeg_destroy_compress(&fCInfo); } private: - SkJpegEncoderMgr(SkWStream* stream) : fDstMgr(stream), fProc(nullptr) { + SkJpegEncoderMgr(SkWStream* stream) : fDstMgr(stream) { fCInfo.err = jpeg_std_error(&fErrMgr); fErrMgr.error_exit = skjpeg_error_exit; jpeg_create_compress(&fCInfo); @@ -82,65 +85,78 @@ private: jpeg_compress_struct fCInfo; skjpeg_error_mgr fErrMgr; skjpeg_destination_mgr fDstMgr; - transform_scanline_proc fProc; + + std::optional fSrcInfo; + std::optional fDstInfo; + bool fUseColorXform = false; }; +// This function should only be called if fUseColorXform is true and thus fSrcInfo +// and fDstInfo have value. fSrcInfo, fDstInfo, and width must all have the same width. +bool SkJpegEncoderMgr::colorTransformProc(void* dst, const void* src, const int width) { + SkASSERT(fUseColorXform); + SkASSERT(fSrcInfo && fDstInfo); + SkASSERT(width == fSrcInfo->width()); + return SkConvertPixels(fDstInfo.value(), dst, fDstInfo->minRowBytes(), + fSrcInfo.value(), src, fSrcInfo->minRowBytes()); +} + bool SkJpegEncoderMgr::initializeRGB(const SkImageInfo& srcInfo, const SkJpegEncoder::Options& options, const SkJpegMetadataEncoder::SegmentList& metadataSegments) { - auto chooseProc8888 = [&]() { - if (kUnpremul_SkAlphaType == srcInfo.alphaType() && - options.fAlphaOption == SkJpegEncoder::AlphaOption::kBlendOnBlack) { - return transform_scanline_to_premul_legacy; - } - return (transform_scanline_proc) nullptr; - }; - J_COLOR_SPACE jpegColorType = JCS_EXT_RGBA; int numComponents = 0; - switch (srcInfo.colorType()) { - case kRGBA_8888_SkColorType: - fProc = chooseProc8888(); - jpegColorType = JCS_EXT_RGBA; - numComponents = 4; - break; - case kBGRA_8888_SkColorType: - fProc = chooseProc8888(); - jpegColorType = JCS_EXT_BGRA; - numComponents = 4; - break; - case kRGB_565_SkColorType: - fProc = transform_scanline_565; - jpegColorType = JCS_RGB; - numComponents = 3; - break; - case kARGB_4444_SkColorType: - if (SkJpegEncoder::AlphaOption::kBlendOnBlack == options.fAlphaOption) { - return false; - } + SkImageInfo dstInfo; + fUseColorXform = false; - fProc = transform_scanline_444; - jpegColorType = JCS_RGB; - numComponents = 3; - break; - case kGray_8_SkColorType: - case kAlpha_8_SkColorType: - case kR8_unorm_SkColorType: - jpegColorType = JCS_GRAYSCALE; - numComponents = 1; - break; - case kRGBA_F16_SkColorType: - if (kUnpremul_SkAlphaType == srcInfo.alphaType() && - options.fAlphaOption == SkJpegEncoder::AlphaOption::kBlendOnBlack) { - fProc = transform_scanline_F16_to_premul_8888; - } else { - fProc = transform_scanline_F16_to_8888; - } + SkColorType srcCT = srcInfo.colorType(); + const bool applyPremul = SkJpegEncoder::AlphaOption::kBlendOnBlack == options.fAlphaOption + && srcInfo.alphaType() == kUnpremul_SkAlphaType; + if (srcCT == kRGB_888x_SkColorType) { + jpegColorType = JCS_EXT_RGBX; + numComponents = 4; + } else if (!applyPremul && srcCT == kRGBA_8888_SkColorType){ + jpegColorType = JCS_EXT_RGBA; + numComponents = 4; + } else if (!applyPremul && srcCT == kBGRA_8888_SkColorType) { + jpegColorType = JCS_EXT_BGRA; + numComponents = 4; + } else { + // Color type conversion is needed. + switch(SkColorTypeNumChannels(srcCT)) { + case 1: + // We support encoding kAlpha_8_SkColorType pixmaps as JCS_GRAYSCALE as + // this come up often. Otherwise we have no sensible way to encode alpha + // images. + if (SkColorTypeIsAlphaOnly(srcCT) && srcCT != kAlpha_8_SkColorType) { + return false; + } + jpegColorType = JCS_GRAYSCALE; + numComponents = 1; + break; + case 3: + jpegColorType = JCS_EXT_RGBX; + numComponents = 4; + dstInfo = SkImageInfo::Make(srcInfo.width(), 1, kRGB_888x_SkColorType, kUnpremul_SkAlphaType); + fUseColorXform = true; + break; + case 4: { + SkAlphaType dstAT = applyPremul ? kPremul_SkAlphaType : srcInfo.alphaType(); jpegColorType = JCS_EXT_RGBA; numComponents = 4; + dstInfo = SkImageInfo::Make(srcInfo.width(), 1, kRGBA_8888_SkColorType, dstAT); + fUseColorXform = true; break; + } default: return false; + } + } + SkASSERT(numComponents != 0); + + if (fUseColorXform) { + fSrcInfo = srcInfo.makeWH(srcInfo.width(), 1); + fDstInfo = dstInfo; } fCInfo.image_width = srcInfo.width(); @@ -325,7 +341,7 @@ std::unique_ptr SkJpegEncoderImpl::MakeRGB( SkJpegEncoderImpl::SkJpegEncoderImpl(std::unique_ptr encoderMgr, const SkPixmap& src) : SkEncoder(src, - encoderMgr->proc() ? encoderMgr->cinfo()->input_components * src.width() : 0) + encoderMgr->shouldUseColorXform() ? encoderMgr->cinfo()->input_components * src.width() : 0) , fEncoderMgr(std::move(encoderMgr)) {} SkJpegEncoderImpl::SkJpegEncoderImpl(std::unique_ptr encoderMgr, @@ -355,12 +371,11 @@ bool SkJpegEncoderImpl::onEncodeRows(int numRows) { const void* srcRow = fSrc.addr(0, fCurrRow); for (int i = 0; i < numRows; i++) { JSAMPLE* jpegSrcRow = (JSAMPLE*)(const_cast(srcRow)); - if (fEncoderMgr->proc()) { + if (fEncoderMgr->shouldUseColorXform()) { sk_msan_assert_initialized(srcRow, SkTAddOffset(srcRow, srcBytes)); - fEncoderMgr->proc()((char*)fStorage.get(), - (const char*)srcRow, - fSrc.width(), - fEncoderMgr->cinfo()->input_components); + if (!fEncoderMgr->colorTransformProc((void*)fStorage.get(), srcRow, fSrc.width())) { + return false; + } jpegSrcRow = fStorage.get(); sk_msan_assert_initialized(jpegSrcRow, SkTAddOffset(jpegSrcRow, jpegSrcBytes)); diff --git a/gfx/skia/skia/src/encode/SkJpegGainmapEncoder.cpp b/gfx/skia/skia/src/encode/SkJpegGainmapEncoder.cpp index 356bc71508d5..14d78aefa768 100644 --- a/gfx/skia/skia/src/encode/SkJpegGainmapEncoder.cpp +++ b/gfx/skia/skia/src/encode/SkJpegGainmapEncoder.cpp @@ -337,23 +337,11 @@ static size_t mp_segment_offset(const SkData* image) { } const auto& segments = scan.getSegments(); - // Search for the Exif segment and place the MP parameters immediately after. See 5.1. - // Basic MP File Structure, which indicates "The MP Extensions are specified in the APP2 - // marker segment which follows immediately after the Exif Attributes in the APP1 marker - // segment except as specified in section 7". - for (size_t segmentIndex = 0; segmentIndex < segments.size() - 1; ++segmentIndex) { - const auto& segment = segments[segmentIndex]; - if (segment.marker != kExifMarker) { - continue; - } - auto params = SkJpegSegmentScanner::GetParameters(image, segment); - if (params->size() < sizeof(kExifSig) || - memcmp(params->data(), kExifSig, sizeof(kExifSig)) != 0) { - continue; - } - // Insert the MPF segment at the offset of the next segment. - return segments[segmentIndex + 1].offset; - } + // According to CIPA DC-007 section 5.1, "Basic MP File Structure", "The MP Extensions are + // specified in the APP2 marker segment which follows immediately after the Exif Attributes in + // the APP1 marker segment except as specified in section 7". In practice, this is rarely + // obeyed, and further, makes the file dangerous for use by less robust editors (see + // b/355642172). Instead, place the MP segment just before the StartOfScan marker. // If there is no Exif segment, then insert the MPF segment just before the StartOfScan. return segments.back().offset; diff --git a/gfx/skia/skia/src/encode/SkPngEncoderBase.cpp b/gfx/skia/skia/src/encode/SkPngEncoderBase.cpp new file mode 100644 index 000000000000..c1393b75a86b --- /dev/null +++ b/gfx/skia/skia/src/encode/SkPngEncoderBase.cpp @@ -0,0 +1,276 @@ +/* + * Copyright 2024 Google LLC. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "src/encode/SkPngEncoderBase.h" + +#include + +#include "include/core/SkAlphaType.h" +#include "include/core/SkColorType.h" +#include "include/core/SkImageInfo.h" +#include "include/core/SkPixmap.h" +#include "include/core/SkSpan.h" +#include "include/private/SkEncodedInfo.h" +#include "include/private/base/SkAssert.h" +#include "include/private/base/SkTemplates.h" +#include "src/base/SkMSAN.h" +#include "src/base/SkSafeMath.h" + +namespace { + +SkEncodedInfo makeInfo(const SkImageInfo& srcInfo, + SkEncodedInfo::Color color, + int bitsPerComponent) { + SkEncodedInfo::Alpha alpha = + color == SkEncodedInfo::kGray_Color || color == SkEncodedInfo::kRGB_Color + ? SkEncodedInfo::kOpaque_Alpha + : SkEncodedInfo::kUnpremul_Alpha; + + return SkEncodedInfo::Make(srcInfo.width(), srcInfo.height(), color, alpha, bitsPerComponent); +} + +SkEncodedInfo makeGray8Info(const SkImageInfo& srcInfo) { + return makeInfo(srcInfo, SkEncodedInfo::kGray_Color, 8); +} + +SkEncodedInfo makeGrayAlpha8Info(const SkImageInfo& srcInfo) { + return makeInfo(srcInfo, SkEncodedInfo::kGrayAlpha_Color, 8); +} + +SkEncodedInfo makeRgb8Info(const SkImageInfo& srcInfo) { + return makeInfo(srcInfo, SkEncodedInfo::kRGB_Color, 8); +} + +SkEncodedInfo makeRgba8Info(const SkImageInfo& srcInfo) { + return makeInfo(srcInfo, SkEncodedInfo::kRGBA_Color, 8); +} + +SkEncodedInfo makeRgb16Info(const SkImageInfo& srcInfo) { + return makeInfo(srcInfo, SkEncodedInfo::kRGB_Color, 16); +} + +SkEncodedInfo makeRgba16Info(const SkImageInfo& srcInfo) { + return makeInfo(srcInfo, SkEncodedInfo::kRGBA_Color, 16); +} + +std::optional makeTargetInfo(SkEncodedInfo dstInfo, + transform_scanline_proc transformProc) { + // `static_cast`(dstInfo.bitsPerPixel())` uses trustworthy, bounded + // data as input - no need to use `SkSafeMath` for this part. + SkASSERT(dstInfo.bitsPerComponent() == 8 || dstInfo.bitsPerComponent() == 16); + SkASSERT(dstInfo.bitsPerPixel() <= (16 * 4)); + size_t bitsPerPixel = static_cast(dstInfo.bitsPerPixel()); + SkASSERT((bitsPerPixel % 8) == 0); + size_t bytesPerPixel = bitsPerPixel / 8; + + SkSafeMath safe; + size_t dstRowSize = safe.mul(safe.castTo(dstInfo.width()), bytesPerPixel); + if (!safe.ok()) { + return std::nullopt; + } + + return SkPngEncoderBase::TargetInfo{std::move(dstInfo), transformProc, dstRowSize}; +} + +} // namespace + +// static +std::optional SkPngEncoderBase::getTargetInfo( + const SkImageInfo& srcInfo) { + switch (srcInfo.colorType()) { + case kUnknown_SkColorType: + return std::nullopt; + + // TODO: I don't think this can just use kRGBA's procs. + // kPremul is especially tricky here, since it's presumably TF⁻¹(rgb * a), + // so to get at unpremul rgb we'd need to undo the transfer function first. + case kSRGBA_8888_SkColorType: + return std::nullopt; + + case kRGBA_8888_SkColorType: + switch (srcInfo.alphaType()) { + case kOpaque_SkAlphaType: + return makeTargetInfo(makeRgb8Info(srcInfo), transform_scanline_RGBX); + case kUnpremul_SkAlphaType: + return makeTargetInfo(makeRgba8Info(srcInfo), transform_scanline_memcpy); + case kPremul_SkAlphaType: + return makeTargetInfo(makeRgba8Info(srcInfo), transform_scanline_rgbA); + default: + SkDEBUGFAIL("unknown alpha type"); + return std::nullopt; + } + case kBGRA_8888_SkColorType: + switch (srcInfo.alphaType()) { + case kOpaque_SkAlphaType: + return makeTargetInfo(makeRgb8Info(srcInfo), transform_scanline_BGRX); + case kUnpremul_SkAlphaType: + return makeTargetInfo(makeRgba8Info(srcInfo), transform_scanline_BGRA); + case kPremul_SkAlphaType: + return makeTargetInfo(makeRgba8Info(srcInfo), transform_scanline_bgrA); + default: + SkDEBUGFAIL("unknown alpha type"); + return std::nullopt; + } + case kRGB_565_SkColorType: + SkASSERT(srcInfo.isOpaque()); + return makeTargetInfo(makeRgb8Info(srcInfo), transform_scanline_565); + case kRGB_888x_SkColorType: + SkASSERT(srcInfo.isOpaque()); + return makeTargetInfo(makeRgb8Info(srcInfo), transform_scanline_RGBX); + case kARGB_4444_SkColorType: + switch (srcInfo.alphaType()) { + case kOpaque_SkAlphaType: + return makeTargetInfo(makeRgb8Info(srcInfo), transform_scanline_444); + case kPremul_SkAlphaType: + return makeTargetInfo(makeRgba8Info(srcInfo), transform_scanline_4444); + default: + SkDEBUGFAIL("unknown alpha type"); + return std::nullopt; + } + case kGray_8_SkColorType: + SkASSERT(srcInfo.isOpaque()); + return makeTargetInfo(makeGray8Info(srcInfo), transform_scanline_memcpy); + + case kRGBA_F16Norm_SkColorType: + case kRGBA_F16_SkColorType: + switch (srcInfo.alphaType()) { + case kOpaque_SkAlphaType: + case kUnpremul_SkAlphaType: + return makeTargetInfo(makeRgba16Info(srcInfo), transform_scanline_F16); + case kPremul_SkAlphaType: + return makeTargetInfo(makeRgba16Info(srcInfo), transform_scanline_F16_premul); + default: + SkDEBUGFAIL("unknown alpha type"); + return std::nullopt; + } + case kRGB_F16F16F16x_SkColorType: + SkASSERT(srcInfo.isOpaque()); + return makeTargetInfo(makeRgb16Info(srcInfo), transform_scanline_F16F16F16x); + case kRGBA_F32_SkColorType: + switch (srcInfo.alphaType()) { + case kOpaque_SkAlphaType: + case kUnpremul_SkAlphaType: + return makeTargetInfo(makeRgba16Info(srcInfo), transform_scanline_F32); + case kPremul_SkAlphaType: + return makeTargetInfo(makeRgba16Info(srcInfo), transform_scanline_F32_premul); + default: + SkDEBUGFAIL("unknown alpha type"); + return std::nullopt; + } + case kRGBA_1010102_SkColorType: + switch (srcInfo.alphaType()) { + case kOpaque_SkAlphaType: + case kUnpremul_SkAlphaType: + return makeTargetInfo(makeRgba16Info(srcInfo), transform_scanline_1010102); + case kPremul_SkAlphaType: + return makeTargetInfo(makeRgba16Info(srcInfo), + transform_scanline_1010102_premul); + default: + SkDEBUGFAIL("unknown alpha type"); + return std::nullopt; + } + case kBGRA_1010102_SkColorType: + switch (srcInfo.alphaType()) { + case kOpaque_SkAlphaType: + case kUnpremul_SkAlphaType: + return makeTargetInfo(makeRgba16Info(srcInfo), transform_scanline_bgra_1010102); + case kPremul_SkAlphaType: + return makeTargetInfo(makeRgba16Info(srcInfo), + transform_scanline_bgra_1010102_premul); + default: + SkDEBUGFAIL("unknown alpha type"); + return std::nullopt; + } + case kRGB_101010x_SkColorType: + return makeTargetInfo(makeRgb16Info(srcInfo), transform_scanline_101010x); + case kBGR_101010x_SkColorType: + return makeTargetInfo(makeRgb16Info(srcInfo), transform_scanline_bgr_101010x); + case kBGR_101010x_XR_SkColorType: + switch (srcInfo.alphaType()) { + case kOpaque_SkAlphaType: + return makeTargetInfo(makeRgb16Info(srcInfo), + transform_scanline_bgr_101010x_xr); + default: + SkDEBUGFAIL("unsupported color type"); + return std::nullopt; + } + case kBGRA_10101010_XR_SkColorType: + switch (srcInfo.alphaType()) { + case kOpaque_SkAlphaType: + case kUnpremul_SkAlphaType: + return makeTargetInfo(makeRgba16Info(srcInfo), + transform_scanline_bgra_10101010_xr); + case kPremul_SkAlphaType: + return makeTargetInfo(makeRgba16Info(srcInfo), + transform_scanline_bgra_10101010_xr_premul); + default: + SkDEBUGFAIL("unknown alpha type"); + return std::nullopt; + } + case kAlpha_8_SkColorType: + return makeTargetInfo(makeGrayAlpha8Info(srcInfo), transform_scanline_A8_to_GrayAlpha); + case kR8G8_unorm_SkColorType: + case kR16G16_unorm_SkColorType: + case kR16G16_float_SkColorType: + case kA16_unorm_SkColorType: + case kA16_float_SkColorType: + case kR16G16B16A16_unorm_SkColorType: + case kR8_unorm_SkColorType: + case kRGBA_10x6_SkColorType: + return std::nullopt; + } + SkDEBUGFAIL("unsupported color type"); + return std::nullopt; +} + +SkPngEncoderBase::SkPngEncoderBase(TargetInfo targetInfo, const SkPixmap& src) + : SkEncoder(src, targetInfo.fDstRowSize), fTargetInfo(std::move(targetInfo)) { + SkASSERT(fTargetInfo.fTransformProc); + SkASSERT(fTargetInfo.fDstRowSize > 0); +} + +bool SkPngEncoderBase::onEncodeRows(int numRows) { + // https://www.w3.org/TR/png-3/#11IHDR says that "zero is an invalid value" + // for width and height. + if (fSrc.width() == 0 || fSrc.height() == 0) { + return false; + } + + if (numRows < 0) { + return false; + } + + while (numRows > 0) { + if (fCurrRow == fSrc.height()) { + return false; + } + + const void* srcRow = fSrc.addr(0, fCurrRow); + sk_msan_assert_initialized(srcRow, + (const uint8_t*)srcRow + (fSrc.width() << fSrc.shiftPerPixel())); + + fTargetInfo.fTransformProc((char*)fStorage.get(), + (const char*)srcRow, + fSrc.width(), + SkColorTypeBytesPerPixel(fSrc.colorType())); + + SkSpan rowToEncode(fStorage.get(), fTargetInfo.fDstRowSize); + if (!this->onEncodeRow(rowToEncode)) { + return false; + } + + fCurrRow++; + numRows--; + } + + if (fCurrRow == fSrc.height() && !fFinishedEncoding) { + fFinishedEncoding = true; + return this->onFinishEncoding(); + } + + return true; +} diff --git a/gfx/skia/skia/src/encode/SkPngEncoderBase.h b/gfx/skia/skia/src/encode/SkPngEncoderBase.h new file mode 100644 index 000000000000..eb42c8626961 --- /dev/null +++ b/gfx/skia/skia/src/encode/SkPngEncoderBase.h @@ -0,0 +1,64 @@ +/* + * 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 SkPngEncoderBase_DEFINED +#define SkPngEncoderBase_DEFINED + +#include +#include + +#include + +#include "include/encode/SkEncoder.h" +#include "include/private/SkEncodedInfo.h" +#include "src/encode/SkImageEncoderFns.h" + +struct SkImageInfo; +class SkPixmap; +template class SkSpan; + +// This class implements functionality shared between `SkPngEncoderImpl` and +// `SkPngRustEncoderImpl` (the latter is from `experimental/rust_png`). +class SkPngEncoderBase : public SkEncoder { +public: + struct TargetInfo { + SkEncodedInfo fDstInfo; + transform_scanline_proc fTransformProc; + size_t fDstRowSize; + }; + + // Gets the `fDstInfo` that `srcInfo` should be converted into before + // encoding and a `fTransformProc` that can transform source rows into + // ready-to-encode rows (and the `fDstRowSize` of such rows). + // + // For example, `kRGBA_F32_SkColorType` source will be encoded as + // `SkEncodedInfo::kRGBA_Color` with 16 `bitsPerComponent`. Depending on + // `src`'s alpha type, such transformation can be handled by either + // `transform_scanline_F32` or `transform_scanline_F32_premul`. + // + // Returns `std::nullopt` if `srcInfo` is not supported by the PNG encoder. + static std::optional getTargetInfo(const SkImageInfo& srcInfo); + +protected: + SkPngEncoderBase(TargetInfo targetInfo, const SkPixmap& src); + + // SkEncoder override: + bool onEncodeRows(int numRows) final; + + // Called from `onEncodeRows` to encode the given `row` (in `dstInfo` format + // that was passed to the `SkPngEncoderBase`'s constructor). + virtual bool onEncodeRow(SkSpan row) = 0; + + // Called from `onEncodeRows` to finalize the encoded PNG (e.g. write the + // `IEND` chunk). + virtual bool onFinishEncoding() = 0; + +private: + TargetInfo fTargetInfo; + bool fFinishedEncoding = false; +}; + +#endif // SkPngEncoderBase_DEFINED diff --git a/gfx/skia/skia/src/encode/SkPngEncoderImpl.cpp b/gfx/skia/skia/src/encode/SkPngEncoderImpl.cpp index 6206eefc45af..7f3c5c43451d 100644 --- a/gfx/skia/skia/src/encode/SkPngEncoderImpl.cpp +++ b/gfx/skia/skia/src/encode/SkPngEncoderImpl.cpp @@ -7,7 +7,8 @@ #include "src/encode/SkPngEncoderImpl.h" -#include "include/core/SkAlphaType.h" +#include + #include "include/core/SkBitmap.h" #include "include/core/SkColorSpace.h" #include "include/core/SkColorType.h" @@ -16,22 +17,25 @@ #include "include/core/SkImageInfo.h" #include "include/core/SkPixmap.h" #include "include/core/SkRefCnt.h" +#include "include/core/SkSpan.h" #include "include/core/SkStream.h" #include "include/core/SkString.h" #include "include/encode/SkEncoder.h" #include "include/encode/SkPngEncoder.h" +#include "include/private/SkEncodedInfo.h" +#include "include/private/SkGainmapInfo.h" #include "include/private/base/SkAssert.h" #include "include/private/base/SkDebug.h" #include "include/private/base/SkNoncopyable.h" -#include "include/private/base/SkTemplates.h" #include "modules/skcms/skcms.h" -#include "src/base/SkMSAN.h" #include "src/codec/SkPngPriv.h" #include "src/encode/SkImageEncoderFns.h" #include "src/encode/SkImageEncoderPriv.h" +#include "src/encode/SkPngEncoderBase.h" #include "src/image/SkImage_Base.h" #include +#include #include #include #include @@ -52,7 +56,7 @@ static_assert(PNG_FILTER_AVG == (int)SkPngEncoder::FilterFlag::kAvg, "Skia l static_assert(PNG_FILTER_PAETH == (int)SkPngEncoder::FilterFlag::kPaeth, "Skia libpng filter err."); static_assert(PNG_ALL_FILTERS == (int)SkPngEncoder::FilterFlag::kAll, "Skia libpng filter err."); -static constexpr bool kSuppressPngEncodeWarnings = true; +static constexpr bool kSuppressPngEncodeWarnings = false; static void sk_error_fn(png_structp png_ptr, png_const_charp msg) { if (!kSuppressPngEncodeWarnings) { @@ -77,14 +81,15 @@ public: */ static std::unique_ptr Make(SkWStream* stream); - bool setHeader(const SkImageInfo& srcInfo, const SkPngEncoder::Options& options); + bool setHeader(const SkEncodedInfo& dstInfo, + const SkImageInfo& srcInfo, + const SkPngEncoder::Options& options); bool setColorSpace(const SkImageInfo& info, const SkPngEncoder::Options& options); + bool setV0Gainmap(const SkPngEncoder::Options& options); bool writeInfo(const SkImageInfo& srcInfo); - void chooseProc(const SkImageInfo& srcInfo); png_structp pngPtr() { return fPngPtr; } png_infop infoPtr() { return fInfoPtr; } - int pngBytesPerPixel() const { return fPngBytesPerPixel; } transform_scanline_proc proc() const { return fProc; } ~SkPngEncoderMgr() { png_destroy_write_struct(&fPngPtr, &fInfoPtr); } @@ -94,8 +99,7 @@ private: png_structp fPngPtr; png_infop fInfoPtr; - int fPngBytesPerPixel; - transform_scanline_proc fProc; + transform_scanline_proc fProc = nullptr; }; std::unique_ptr SkPngEncoderMgr::Make(SkWStream* stream) { @@ -115,14 +119,33 @@ std::unique_ptr SkPngEncoderMgr::Make(SkWStream* stream) { return std::unique_ptr(new SkPngEncoderMgr(pngPtr, infoPtr)); } -bool SkPngEncoderMgr::setHeader(const SkImageInfo& srcInfo, const SkPngEncoder::Options& options) { +bool SkPngEncoderMgr::setHeader(const SkEncodedInfo& dstInfo, + const SkImageInfo& srcInfo, + const SkPngEncoder::Options& options) { if (setjmp(png_jmpbuf(fPngPtr))) { return false; } int pngColorType; + switch (dstInfo.color()) { + case SkEncodedInfo::kRGB_Color: + pngColorType = PNG_COLOR_TYPE_RGB; + break; + case SkEncodedInfo::kRGBA_Color: + pngColorType = PNG_COLOR_TYPE_RGB_ALPHA; + break; + case SkEncodedInfo::kGray_Color: + pngColorType = PNG_COLOR_TYPE_GRAY; + break; + case SkEncodedInfo::kGrayAlpha_Color: + pngColorType = PNG_COLOR_TYPE_GRAY_ALPHA; + break; + default: + SkDEBUGFAIL("`getTargetInfo` returned unexpected `SkEncodedInfo::Color`"); + return false; + } + png_color_8 sigBit; - int bitDepth = 8; switch (srcInfo.colorType()) { case kRGBA_F16Norm_SkColorType: case kRGBA_F16_SkColorType: @@ -131,23 +154,14 @@ bool SkPngEncoderMgr::setHeader(const SkImageInfo& srcInfo, const SkPngEncoder:: sigBit.green = 16; sigBit.blue = 16; sigBit.alpha = 16; - bitDepth = 16; - pngColorType = srcInfo.isOpaque() ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA; - fPngBytesPerPixel = 8; break; case kRGB_F16F16F16x_SkColorType: sigBit.red = 16; sigBit.green = 16; sigBit.blue = 16; - pngColorType = PNG_COLOR_TYPE_RGB; - fPngBytesPerPixel = 8; - SkASSERT(srcInfo.isOpaque()); break; case kGray_8_SkColorType: sigBit.gray = 8; - pngColorType = PNG_COLOR_TYPE_GRAY; - fPngBytesPerPixel = 1; - SkASSERT(srcInfo.isOpaque()); break; case kRGBA_8888_SkColorType: case kBGRA_8888_SkColorType: @@ -155,69 +169,44 @@ bool SkPngEncoderMgr::setHeader(const SkImageInfo& srcInfo, const SkPngEncoder:: sigBit.green = 8; sigBit.blue = 8; sigBit.alpha = 8; - pngColorType = srcInfo.isOpaque() ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA; - fPngBytesPerPixel = srcInfo.isOpaque() ? 3 : 4; break; case kRGB_888x_SkColorType: sigBit.red = 8; sigBit.green = 8; sigBit.blue = 8; - pngColorType = PNG_COLOR_TYPE_RGB; - fPngBytesPerPixel = 3; - SkASSERT(srcInfo.isOpaque()); break; case kARGB_4444_SkColorType: - if (kUnpremul_SkAlphaType == srcInfo.alphaType()) { - return false; - } - sigBit.red = 4; sigBit.green = 4; sigBit.blue = 4; sigBit.alpha = 4; - pngColorType = srcInfo.isOpaque() ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA; - fPngBytesPerPixel = srcInfo.isOpaque() ? 3 : 4; break; case kRGB_565_SkColorType: sigBit.red = 5; sigBit.green = 6; sigBit.blue = 5; - pngColorType = PNG_COLOR_TYPE_RGB; - fPngBytesPerPixel = 3; - SkASSERT(srcInfo.isOpaque()); break; case kAlpha_8_SkColorType: // store as gray+alpha, but ignore gray sigBit.gray = kGraySigBit_GrayAlphaIsJustAlpha; sigBit.alpha = 8; - pngColorType = PNG_COLOR_TYPE_GRAY_ALPHA; - fPngBytesPerPixel = 2; break; case kRGBA_1010102_SkColorType: - bitDepth = 16; sigBit.red = 10; sigBit.green = 10; sigBit.blue = 10; sigBit.alpha = 2; - pngColorType = srcInfo.isOpaque() ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA; - fPngBytesPerPixel = 8; break; case kBGR_101010x_XR_SkColorType: case kRGB_101010x_SkColorType: - bitDepth = 16; sigBit.red = 10; sigBit.green = 10; sigBit.blue = 10; - pngColorType = PNG_COLOR_TYPE_RGB; - fPngBytesPerPixel = 6; break; case kBGRA_10101010_XR_SkColorType: - bitDepth = 16; sigBit.red = 10; sigBit.green = 10; sigBit.blue = 10; sigBit.alpha = 10; - pngColorType = srcInfo.isOpaque() ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA; - fPngBytesPerPixel = 8; break; default: return false; @@ -227,7 +216,7 @@ bool SkPngEncoderMgr::setHeader(const SkImageInfo& srcInfo, const SkPngEncoder:: fInfoPtr, srcInfo.width(), srcInfo.height(), - bitDepth, + dstInfo.bitsPerComponent(), pngColorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, @@ -245,6 +234,10 @@ bool SkPngEncoderMgr::setHeader(const SkImageInfo& srcInfo, const SkPngEncoder:: // Set comments in tEXt chunk const sk_sp& comments = options.fComments; if (comments != nullptr) { + if (comments->count() % 2 != 0) { + return false; + } + std::vector png_texts(comments->count()); std::vector clippedKeys; for (int i = 0; i < comments->count() / 2; ++i) { @@ -272,144 +265,6 @@ bool SkPngEncoderMgr::setHeader(const SkImageInfo& srcInfo, const SkPngEncoder:: return true; } -static transform_scanline_proc choose_proc(const SkImageInfo& info) { - switch (info.colorType()) { - case kUnknown_SkColorType: - break; - - // TODO: I don't think this can just use kRGBA's procs. - // kPremul is especially tricky here, since it's presumably TF⁻¹(rgb * a), - // so to get at unpremul rgb we'd need to undo the transfer function first. - case kSRGBA_8888_SkColorType: - return nullptr; - - case kRGBA_8888_SkColorType: - switch (info.alphaType()) { - case kOpaque_SkAlphaType: - return transform_scanline_RGBX; - case kUnpremul_SkAlphaType: - return transform_scanline_memcpy; - case kPremul_SkAlphaType: - return transform_scanline_rgbA; - default: - SkDEBUGFAIL("unknown alpha type"); - return nullptr; - } - case kBGRA_8888_SkColorType: - switch (info.alphaType()) { - case kOpaque_SkAlphaType: - return transform_scanline_BGRX; - case kUnpremul_SkAlphaType: - return transform_scanline_BGRA; - case kPremul_SkAlphaType: - return transform_scanline_bgrA; - default: - SkDEBUGFAIL("unknown alpha type"); - return nullptr; - } - case kRGB_565_SkColorType: - return transform_scanline_565; - case kRGB_888x_SkColorType: - return transform_scanline_RGBX; - case kARGB_4444_SkColorType: - switch (info.alphaType()) { - case kOpaque_SkAlphaType: - return transform_scanline_444; - case kPremul_SkAlphaType: - return transform_scanline_4444; - default: - SkDEBUGFAIL("unknown alpha type"); - return nullptr; - } - case kGray_8_SkColorType: - return transform_scanline_memcpy; - - case kRGBA_F16Norm_SkColorType: - case kRGBA_F16_SkColorType: - switch (info.alphaType()) { - case kOpaque_SkAlphaType: - case kUnpremul_SkAlphaType: - return transform_scanline_F16; - case kPremul_SkAlphaType: - return transform_scanline_F16_premul; - default: - SkDEBUGFAIL("unknown alpha type"); - return nullptr; - } - case kRGB_F16F16F16x_SkColorType: - return transform_scanline_F16F16F16x; - case kRGBA_F32_SkColorType: - switch (info.alphaType()) { - case kOpaque_SkAlphaType: - case kUnpremul_SkAlphaType: - return transform_scanline_F32; - case kPremul_SkAlphaType: - return transform_scanline_F32_premul; - default: - SkDEBUGFAIL("unknown alpha type"); - return nullptr; - } - case kRGBA_1010102_SkColorType: - switch (info.alphaType()) { - case kOpaque_SkAlphaType: - case kUnpremul_SkAlphaType: - return transform_scanline_1010102; - case kPremul_SkAlphaType: - return transform_scanline_1010102_premul; - default: - SkDEBUGFAIL("unknown alpha type"); - return nullptr; - } - case kBGRA_1010102_SkColorType: - switch (info.alphaType()) { - case kOpaque_SkAlphaType: - case kUnpremul_SkAlphaType: - return transform_scanline_bgra_1010102; - case kPremul_SkAlphaType: - return transform_scanline_bgra_1010102_premul; - default: - SkDEBUGFAIL("unknown alpha type"); - return nullptr; - } - case kRGB_101010x_SkColorType: - return transform_scanline_101010x; - case kBGR_101010x_SkColorType: - return transform_scanline_bgr_101010x; - case kBGR_101010x_XR_SkColorType: - switch (info.alphaType()) { - case kOpaque_SkAlphaType: - return transform_scanline_bgr_101010x_xr; - default: - SkDEBUGFAIL("unsupported color type"); - return nullptr; - } - case kBGRA_10101010_XR_SkColorType: - switch (info.alphaType()) { - case kOpaque_SkAlphaType: - case kUnpremul_SkAlphaType: - return transform_scanline_bgra_10101010_xr; - case kPremul_SkAlphaType: - return transform_scanline_bgra_10101010_xr_premul; - default: - SkDEBUGFAIL("unknown alpha type"); - return nullptr; - } - case kAlpha_8_SkColorType: - return transform_scanline_A8_to_GrayAlpha; - case kR8G8_unorm_SkColorType: - case kR16G16_unorm_SkColorType: - case kR16G16_float_SkColorType: - case kA16_unorm_SkColorType: - case kA16_float_SkColorType: - case kR16G16B16A16_unorm_SkColorType: - case kR8_unorm_SkColorType: - case kRGBA_10x6_SkColorType: - return nullptr; - } - SkDEBUGFAIL("unsupported color type"); - return nullptr; -} - static void set_icc(png_structp png_ptr, png_infop info_ptr, const SkImageInfo& info, @@ -445,54 +300,122 @@ bool SkPngEncoderMgr::setColorSpace(const SkImageInfo& info, const SkPngEncoder: return true; } +bool SkPngEncoderMgr::setV0Gainmap(const SkPngEncoder::Options& options) { +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED + if (setjmp(png_jmpbuf(fPngPtr))) { + return false; + } + + // We require some gainmap information. + if (!options.fGainmapInfo) { + return false; + } + + if (options.fGainmap) { + sk_sp gainmapVersion = SkGainmapInfo::SerializeVersion(); + SkDynamicMemoryWStream gainmapStream; + + // When we encode the gainmap, we need to remove the gainmap from its + // own encoding options, so that we don't recurse. + auto modifiedOptions = options; + modifiedOptions.fGainmap = nullptr; + + auto gainmapInfo = *(options.fGainmapInfo); + auto gainmapPixels = *(options.fGainmap); + auto targetInfo = SkPngEncoderBase::getTargetInfo(gainmapPixels.info()); + + if (targetInfo && targetInfo->fDstInfo.color() != SkEncodedInfo::kGray_Color && + targetInfo->fDstInfo.color() != SkEncodedInfo::kGrayAlpha_Color) { + // Encode the alternate image colorspace directly in the gainmap profile, + // since the ISO gainmap payload does not contain the actual alternative + // image primaries. + const auto& gainmapColorSpace = options.fGainmapInfo->fGainmapMathColorSpace; + gainmapPixels.setColorSpace(gainmapColorSpace); + } else { + // Scrub the gainmap colorspace, since grayscale PNGs don't support + // RGB ICC profiles + gainmapInfo.fGainmapMathColorSpace = nullptr; + modifiedOptions.fGainmapInfo = &gainmapInfo; + } + + bool result = SkPngEncoder::Encode(&gainmapStream, gainmapPixels, modifiedOptions); + if (!result) { + return false; + } + + sk_sp gainmapData = gainmapStream.detachAsData(); + + // The base image contains chunks for both the gainmap versioning (for possible + // forward-compat, and as a cheap way to check a gainmap might exist) as + // well as the gainmap data. + std::array chunks; + auto& gmapChunk = chunks.at(0); + std::strcpy(reinterpret_cast(gmapChunk.name), "gmAP\0"); + gmapChunk.data = reinterpret_cast(gainmapVersion->writable_data()); + gmapChunk.size = gainmapVersion->size(); + gmapChunk.location = PNG_HAVE_IHDR; + + auto& gdatChunk = chunks.at(1); + std::strcpy(reinterpret_cast(gdatChunk.name), "gdAT\0"); + gdatChunk.data = reinterpret_cast(gainmapData->writable_data()); + gdatChunk.size = gainmapData->size(); + gdatChunk.location = PNG_HAVE_IHDR; + + png_set_keep_unknown_chunks(fPngPtr, PNG_HANDLE_CHUNK_ALWAYS, + (png_const_bytep)"gmAP\0gdAT\0", chunks.size()); + png_set_unknown_chunks(fPngPtr, fInfoPtr, chunks.data(), chunks.size()); + } else { + // If there is no gainmap provided for encoding, but we have info, then + // we're currently encoding the gainmap pixels, so we need to encode the + // gainmap metadata to interpret those pixels. + sk_sp data = options.fGainmapInfo->serialize(); + png_unknown_chunk chunk; + std::strcpy(reinterpret_cast(chunk.name), "gmAP\0"); + chunk.data = reinterpret_cast(data->writable_data()); + chunk.size = data->size(); + chunk.location = PNG_HAVE_IHDR; + png_set_keep_unknown_chunks(fPngPtr, PNG_HANDLE_CHUNK_ALWAYS, + (png_const_bytep)"gmAP\0", 1); + png_set_unknown_chunks(fPngPtr, fInfoPtr, &chunk, 1); + } +#endif + return true; +} + bool SkPngEncoderMgr::writeInfo(const SkImageInfo& srcInfo) { if (setjmp(png_jmpbuf(fPngPtr))) { return false; } png_write_info(fPngPtr, fInfoPtr); - if (kRGBA_F16_SkColorType == srcInfo.colorType() && - kOpaque_SkAlphaType == srcInfo.alphaType()) { - // For kOpaque, kRGBA_F16, we will keep the row as RGBA and tell libpng - // to skip the alpha channel. - png_set_filler(fPngPtr, 0, PNG_FILLER_AFTER); - } - return true; } -void SkPngEncoderMgr::chooseProc(const SkImageInfo& srcInfo) { fProc = choose_proc(srcInfo); } - -SkPngEncoderImpl::SkPngEncoderImpl(std::unique_ptr encoderMgr, const SkPixmap& src) - : SkEncoder(src, encoderMgr->pngBytesPerPixel() * src.width()) - , fEncoderMgr(std::move(encoderMgr)) {} +SkPngEncoderImpl::SkPngEncoderImpl(TargetInfo targetInfo, + std::unique_ptr encoderMgr, + const SkPixmap& src) + : SkPngEncoderBase(std::move(targetInfo), src), fEncoderMgr(std::move(encoderMgr)) {} SkPngEncoderImpl::~SkPngEncoderImpl() {} -bool SkPngEncoderImpl::onEncodeRows(int numRows) { +bool SkPngEncoderImpl::onEncodeRow(SkSpan row) { if (setjmp(png_jmpbuf(fEncoderMgr->pngPtr()))) { return false; } - const void* srcRow = fSrc.addr(0, fCurrRow); - for (int y = 0; y < numRows; y++) { - sk_msan_assert_initialized(srcRow, - (const uint8_t*)srcRow + (fSrc.width() << fSrc.shiftPerPixel())); - fEncoderMgr->proc()((char*)fStorage.get(), - (const char*)srcRow, - fSrc.width(), - SkColorTypeBytesPerPixel(fSrc.colorType())); + // `png_bytep` is `uint8_t*` rather than `const uint8_t*`. + png_bytep rowPtr = const_cast(row.data()); - png_bytep rowPtr = (png_bytep)fStorage.get(); - png_write_rows(fEncoderMgr->pngPtr(), &rowPtr, 1); - srcRow = SkTAddOffset(srcRow, fSrc.rowBytes()); - } - - fCurrRow += numRows; - if (fCurrRow == fSrc.height()) { - png_write_end(fEncoderMgr->pngPtr(), fEncoderMgr->infoPtr()); + png_write_rows(fEncoderMgr->pngPtr(), &rowPtr, 1); + return true; +} + +bool SkPngEncoderImpl::onFinishEncoding() { + if (setjmp(png_jmpbuf(fEncoderMgr->pngPtr()))) { + return false; } + png_write_end(fEncoderMgr->pngPtr(), fEncoderMgr->infoPtr()); return true; } @@ -507,7 +430,13 @@ std::unique_ptr Make(SkWStream* dst, const SkPixmap& src, const Optio return nullptr; } - if (!encoderMgr->setHeader(src.info(), options)) { + std::optional targetInfo = + SkPngEncoderBase::getTargetInfo(src.info()); + if (!targetInfo.has_value()) { + return nullptr; + } + + if (!encoderMgr->setHeader(targetInfo->fDstInfo, src.info(), options)) { return nullptr; } @@ -515,13 +444,15 @@ std::unique_ptr Make(SkWStream* dst, const SkPixmap& src, const Optio return nullptr; } + if (options.fGainmapInfo && !encoderMgr->setV0Gainmap(options)) { + return nullptr; + } + if (!encoderMgr->writeInfo(src.info())) { return nullptr; } - encoderMgr->chooseProc(src.info()); - - return std::make_unique(std::move(encoderMgr), src); + return std::make_unique(std::move(*targetInfo), std::move(encoderMgr), src); } bool Encode(SkWStream* dst, const SkPixmap& src, const Options& options) { diff --git a/gfx/skia/skia/src/encode/SkPngEncoderImpl.h b/gfx/skia/skia/src/encode/SkPngEncoderImpl.h index 8ccdfbe4146d..98e3b120f055 100644 --- a/gfx/skia/skia/src/encode/SkPngEncoderImpl.h +++ b/gfx/skia/skia/src/encode/SkPngEncoderImpl.h @@ -8,22 +8,27 @@ #ifndef SkPngEncoderImpl_DEFINED #define SkPngEncoderImpl_DEFINED -#include "include/encode/SkEncoder.h" +#include + +#include "src/encode/SkPngEncoderBase.h" #include class SkPixmap; class SkPngEncoderMgr; +template class SkSpan; -class SkPngEncoderImpl : public SkEncoder { +class SkPngEncoderImpl final : public SkPngEncoderBase { public: // public so it can be called from SkPngEncoder namespace. It should only be made // via SkPngEncoder::Make - SkPngEncoderImpl(std::unique_ptr, const SkPixmap& src); + SkPngEncoderImpl(TargetInfo targetInfo, std::unique_ptr, const SkPixmap& src); ~SkPngEncoderImpl() override; protected: - bool onEncodeRows(int numRows) override; + bool onEncodeRow(SkSpan row) override; + bool onFinishEncoding() override; + std::unique_ptr fEncoderMgr; }; #endif diff --git a/gfx/skia/skia/src/image/SkImage_AndroidFactories.cpp b/gfx/skia/skia/src/image/SkImage_AndroidFactories.cpp index d61508c5b392..57d7173ef07c 100644 --- a/gfx/skia/skia/src/image/SkImage_AndroidFactories.cpp +++ b/gfx/skia/skia/src/image/SkImage_AndroidFactories.cpp @@ -28,6 +28,7 @@ #include "include/gpu/ganesh/GrBackendSurface.h" #include "include/gpu/ganesh/GrContextThreadSafeProxy.h" #include "include/gpu/ganesh/GrDirectContext.h" +#include "include/gpu/ganesh/GrExternalTextureGenerator.h" #include "include/gpu/ganesh/GrRecordingContext.h" #include "include/gpu/ganesh/GrTypes.h" #include "include/gpu/ganesh/SkImageGanesh.h" @@ -77,7 +78,7 @@ namespace SkImages { sk_sp DeferredFromAHardwareBuffer(AHardwareBuffer* graphicBuffer, SkAlphaType at) { auto gen = GrAHardwareBufferImageGenerator::Make(graphicBuffer, at, nullptr, kTopLeft_GrSurfaceOrigin); - return DeferredFromGenerator(std::move(gen)); + return DeferredFromTextureGenerator(std::move(gen)); } sk_sp DeferredFromAHardwareBuffer(AHardwareBuffer* graphicBuffer, @@ -85,7 +86,7 @@ sk_sp DeferredFromAHardwareBuffer(AHardwareBuffer* graphicBuffer, sk_sp cs, GrSurfaceOrigin surfaceOrigin) { auto gen = GrAHardwareBufferImageGenerator::Make(graphicBuffer, at, cs, surfaceOrigin); - return DeferredFromGenerator(std::move(gen)); + return DeferredFromTextureGenerator(std::move(gen)); } sk_sp TextureFromAHardwareBufferWithData(GrDirectContext* dContext, diff --git a/gfx/skia/skia/src/image/SkImage_Picture.cpp b/gfx/skia/skia/src/image/SkImage_Picture.cpp index 308f383a6a63..a2e48c64d131 100644 --- a/gfx/skia/skia/src/image/SkImage_Picture.cpp +++ b/gfx/skia/skia/src/image/SkImage_Picture.cpp @@ -16,6 +16,7 @@ #include "include/core/SkImageInfo.h" #include "include/core/SkMatrix.h" #include "include/core/SkPicture.h" +#include "include/core/SkRect.h" #include "include/core/SkSurfaceProps.h" #include "include/private/base/SkAssert.h" #include "include/private/base/SkMutex.h" @@ -61,6 +62,30 @@ void SkImage_Picture::replay(SkCanvas* canvas) const { pictureIG->fPaint.getMaybeNull()); } +sk_sp SkImage_Picture::onMakeSubset(GrDirectContext*, const SkIRect& subset) const { + auto sharedGenerator = this->generator(); + auto pictureIG = static_cast(sharedGenerator->fGenerator.get()); + + SkMatrix matrix = pictureIG->fMatrix; + matrix.postTranslate(-subset.left(), -subset.top()); + SkImages::BitDepth bitDepth = + this->colorType() == kRGBA_F16_SkColorType ? SkImages::BitDepth::kF16 + : SkImages::BitDepth::kU8; + + return SkImage_Picture::Make(pictureIG->fPicture, subset.size(), + &matrix, pictureIG->fPaint.getMaybeNull(), + bitDepth, this->refColorSpace(), pictureIG->fProps); +} + +sk_sp SkImage_Picture::onMakeSubset(skgpu::graphite::Recorder*, + const SkIRect& subset, + RequiredProperties) const { + // The Ganesh version doesn't make use of GrDirectContext so we can use it to + // generate our initial subset. In addition, requesting mipmaps doesn't make + // much sense in this case so we ignore the props. + return this->onMakeSubset(nullptr, subset); +} + bool SkImage_Picture::getImageKeyValues( uint32_t keyValues[SkTiledImageUtils::kNumImageKeyValues]) const { diff --git a/gfx/skia/skia/src/image/SkImage_Picture.h b/gfx/skia/skia/src/image/SkImage_Picture.h index f68d0a9c774a..91501ddf476c 100644 --- a/gfx/skia/skia/src/image/SkImage_Picture.h +++ b/gfx/skia/skia/src/image/SkImage_Picture.h @@ -14,6 +14,7 @@ #include +class GrDirectContext; class SkCanvas; class SkColorSpace; class SkImage; @@ -21,9 +22,11 @@ class SkMatrix; class SkPaint; class SkPicture; class SkSurfaceProps; +struct SkIRect; struct SkISize; namespace SkImages { enum class BitDepth; } +namespace skgpu::graphite { class Recorder; } class SkImage_Picture : public SkImage_Lazy { public: @@ -42,6 +45,11 @@ public: // Call drawPicture on the provided canvas taking care of any required mutex locking. void replay(SkCanvas*) const; + sk_sp onMakeSubset(GrDirectContext*, const SkIRect&) const override; + sk_sp onMakeSubset(skgpu::graphite::Recorder*, + const SkIRect&, + RequiredProperties) const override; + // If possible, extract key data based on the underlying drawPicture-call's parameters. // Takes care of any required mutex locking. bool getImageKeyValues(uint32_t keyValues[SkTiledImageUtils::kNumImageKeyValues]) const; diff --git a/gfx/skia/skia/src/image/SkSurface.cpp b/gfx/skia/skia/src/image/SkSurface.cpp index 8d7dee9f42a4..0696443f7523 100644 --- a/gfx/skia/skia/src/image/SkSurface.cpp +++ b/gfx/skia/skia/src/image/SkSurface.cpp @@ -105,6 +105,10 @@ sk_sp SkSurface::makeImageSnapshot(const SkIRect& srcBounds) { } } +sk_sp SkSurface::makeTemporaryImage() { + return asSB(this)->onMakeTemporaryImage(); +} + sk_sp SkSurface::makeSurface(const SkImageInfo& info) { return asSB(this)->onNewSurface(info); } diff --git a/gfx/skia/skia/src/image/SkSurface_Base.h b/gfx/skia/skia/src/image/SkSurface_Base.h index 93755184af0d..a4e076332043 100644 --- a/gfx/skia/skia/src/image/SkSurface_Base.h +++ b/gfx/skia/skia/src/image/SkSurface_Base.h @@ -93,6 +93,8 @@ public: */ virtual sk_sp onNewImageSnapshot(const SkIRect* subset = nullptr) { return nullptr; } + virtual sk_sp onMakeTemporaryImage() { return this->makeImageSnapshot(); } + virtual void onWritePixels(const SkPixmap&, int x, int y) = 0; /** diff --git a/gfx/skia/skia/src/opts/SkBlitMask_opts.h b/gfx/skia/skia/src/opts/SkBlitMask_opts.h index 43dd5e207345..88ae5ab38a7a 100644 --- a/gfx/skia/skia/src/opts/SkBlitMask_opts.h +++ b/gfx/skia/skia/src/opts/SkBlitMask_opts.h @@ -19,7 +19,7 @@ namespace SK_OPTS_NS { #if defined(SK_ARM_HAS_NEON) // The Sk4px versions below will work fine with NEON, but we have had many indications - // that it doesn't perform as well as this NEON-specific code. TODO(mtklein): why? + // that it doesn't perform as well as this NEON-specific code. #define NEON_A (SK_A32_SHIFT / 8) #define NEON_R (SK_R32_SHIFT / 8) @@ -46,11 +46,12 @@ namespace SK_OPTS_NS { } - template - static void D32_A8_Opaque_Color_neon(void* SK_RESTRICT dst, size_t dstRB, - const void* SK_RESTRICT maskPtr, size_t maskRB, - SkColor color, int width, int height) { - SkPMColor pmc = SkPreMultiplyColor(color); + template + static void blit_mask_d32_a8_neon(void* SK_RESTRICT dst, size_t dstRB, + const void* SK_RESTRICT maskPtr, size_t maskRB, + SkColor color, int width, int height) { + const SkPMColor pmc = SkPreMultiplyColor(color); + const U8CPU colorAlpha = SkGetPackedA32(pmc); SkPMColor* SK_RESTRICT device = (SkPMColor*)dst; const uint8_t* SK_RESTRICT mask = (const uint8_t*)maskPtr; uint8x8x4_t vpmc; @@ -60,7 +61,7 @@ namespace SK_OPTS_NS { dstRB -= (width << 2); if (width >= 8) { - vpmc.val[NEON_A] = vdup_n_u8(SkGetPackedA32(pmc)); + vpmc.val[NEON_A] = vdup_n_u8(colorAlpha); vpmc.val[NEON_R] = vdup_n_u8(SkGetPackedR32(pmc)); vpmc.val[NEON_G] = vdup_n_u8(SkGetPackedG32(pmc)); vpmc.val[NEON_B] = vdup_n_u8(SkGetPackedB32(pmc)); @@ -69,8 +70,9 @@ namespace SK_OPTS_NS { int w = width; while (w >= 8) { uint8x8_t vmask = vld1_u8(mask); - uint16x8_t vscale, vmask256 = SkAlpha255To256_neon8(vmask); - if (isColor) { + uint16x8_t vmask256 = SkAlpha255To256_neon8(vmask); + uint16x8_t vscale; + if constexpr (isTranslucent) { vscale = vsubw_u8(vdupq_n_u16(256), SkAlphaMul_neon8(vpmc.val[NEON_A], vmask256)); } else { @@ -78,13 +80,13 @@ namespace SK_OPTS_NS { } uint8x8x4_t vdev = vld4_u8((uint8_t*)device); - vdev.val[NEON_A] = SkAlphaMul_neon8(vpmc.val[NEON_A], vmask256) + vdev.val[NEON_A] = SkAlphaMul_neon8(vpmc.val[NEON_A], vmask256) + SkAlphaMul_neon8(vdev.val[NEON_A], vscale); - vdev.val[NEON_R] = SkAlphaMul_neon8(vpmc.val[NEON_R], vmask256) + vdev.val[NEON_R] = SkAlphaMul_neon8(vpmc.val[NEON_R], vmask256) + SkAlphaMul_neon8(vdev.val[NEON_R], vscale); - vdev.val[NEON_G] = SkAlphaMul_neon8(vpmc.val[NEON_G], vmask256) + vdev.val[NEON_G] = SkAlphaMul_neon8(vpmc.val[NEON_G], vmask256) + SkAlphaMul_neon8(vdev.val[NEON_G], vscale); - vdev.val[NEON_B] = SkAlphaMul_neon8(vpmc.val[NEON_B], vmask256) + vdev.val[NEON_B] = SkAlphaMul_neon8(vpmc.val[NEON_B], vmask256) + SkAlphaMul_neon8(vdev.val[NEON_B], vscale); vst4_u8((uint8_t*)device, vdev); @@ -94,14 +96,19 @@ namespace SK_OPTS_NS { w -= 8; } - while (w--) { - unsigned aa = *mask++; - if (isColor) { - *device = SkBlendARGB32(pmc, *device, aa); + while (w-- > 0) { + // These variables aren't actually vectors, but the names are consistent with + // the above to make it easier to compare the operations. + const U8CPU vmask = *mask++; + const U16CPU vmask256 = SkAlpha255To256(vmask); + U16CPU vscale; + if constexpr (isTranslucent) { + vscale = 256 - SkAlphaMulQ(colorAlpha, vmask256); } else { - *device = SkAlphaMulQ(pmc, SkAlpha255To256(aa)) - + SkAlphaMulQ(*device, SkAlpha255To256(255 - aa)); + vscale = 256 - vmask; } + *device = SkAlphaMulQ(pmc, vmask256) + + SkAlphaMulQ(*device, vscale); device += 1; } @@ -114,14 +121,14 @@ namespace SK_OPTS_NS { static void blit_mask_d32_a8_general(SkPMColor* dst, size_t dstRB, const SkAlpha* mask, size_t maskRB, SkColor color, int w, int h) { - D32_A8_Opaque_Color_neon(dst, dstRB, mask, maskRB, color, w, h); + blit_mask_d32_a8_neon(dst, dstRB, mask, maskRB, color, w, h); } // As above, but made slightly simpler by requiring that color is opaque. static void blit_mask_d32_a8_opaque(SkPMColor* dst, size_t dstRB, const SkAlpha* mask, size_t maskRB, SkColor color, int w, int h) { - D32_A8_Opaque_Color_neon(dst, dstRB, mask, maskRB, color, w, h); + blit_mask_d32_a8_neon(dst, dstRB, mask, maskRB, color, w, h); } // Same as _opaque, but assumes color == SK_ColorBLACK, a very common and even simpler case. @@ -151,9 +158,12 @@ namespace SK_OPTS_NS { w -= 8; } while (w-- > 0) { - unsigned aa = *mask++; - *device = (aa << SK_A32_SHIFT) - + SkAlphaMulQ(*device, SkAlpha255To256(255 - aa)); + // These variables aren't actually vectors, but the names are consistent with + // the above to make it easier to compare the operations. + const U8CPU vmask = *mask++; + const U16CPU vscale = 256 - vmask; + *device = SkAlphaMulQ(*device, vscale) + + (vmask << SK_A32_SHIFT); device += 1; } device = (uint32_t*)((char*)device + dstRB); diff --git a/gfx/skia/skia/src/opts/SkBlitRow_opts.h b/gfx/skia/skia/src/opts/SkBlitRow_opts.h index 653e3ecfd9df..d1de5681a72e 100644 --- a/gfx/skia/skia/src/opts/SkBlitRow_opts.h +++ b/gfx/skia/skia/src/opts/SkBlitRow_opts.h @@ -8,9 +8,9 @@ #ifndef SkBlitRow_opts_DEFINED #define SkBlitRow_opts_DEFINED -#include "include/private/SkColorData.h" #include "src/base/SkMSAN.h" #include "src/base/SkVx.h" +#include "src/core/SkColorData.h" // Helpers for blit_row_s32a_opaque(), // then blit_row_s32a_opaque() itself, @@ -246,18 +246,21 @@ inline void blit_row_color32(SkPMColor* dst, int count, SkPMColor color) { using U16 = skvx::Vec<4*N, uint16_t>; using U8 = skvx::Vec<4*N, uint8_t>; + // Note when the kernel is used below, the "src" is the existing pixel color. auto kernel = [color](U32 src) { - unsigned invA = 255 - SkGetPackedA32(color); - invA += invA >> 7; + unsigned invA = SkAlpha255To256(255 - SkGetPackedA32(color)); SkASSERT(0 < invA && invA < 256); // We handle alpha == 0 or alpha == 255 specially. - // (src * invA + (color << 8) + 128) >> 8 - // Should all fit in 16 bits. + // color is premul, so the channels have already been + // scaled by alpha. We just need to scale src by (255 - a) + // using the trick of adding 1 and dividing by 256 which is + // much faster than dividing by 255. Then we can add that + // to color to get the result. U8 s = sk_bit_cast(src), a = U8(invA); U16 c = skvx::cast(sk_bit_cast(U32(color))), - d = (mull(s,a) + (c << 8) + 128)>>8; - return sk_bit_cast(skvx::cast(d)); + r = (mull(s,a) >> 8) + c; + return sk_bit_cast(skvx::cast(r)); }; while (count >= N) { diff --git a/gfx/skia/skia/src/opts/SkOpts_SetTarget.h b/gfx/skia/skia/src/opts/SkOpts_SetTarget.h index 525cfcb862ae..f2debc0444cb 100644 --- a/gfx/skia/skia/src/opts/SkOpts_SetTarget.h +++ b/gfx/skia/skia/src/opts/SkOpts_SetTarget.h @@ -65,6 +65,7 @@ // Each of the specific intrinsic headers also checks to ensure that immintrin.h has been // included, so do that here, first. #if defined(__clang__) && defined(_MSC_VER) + #define __RTMINTRIN_H // Workaround for https://github.com/llvm/llvm-project/issues/95133 #include #endif diff --git a/gfx/skia/skia/src/opts/SkRasterPipeline_opts.h b/gfx/skia/skia/src/opts/SkRasterPipeline_opts.h index 3fb05efbf496..f2c529593ec3 100644 --- a/gfx/skia/skia/src/opts/SkRasterPipeline_opts.h +++ b/gfx/skia/skia/src/opts/SkRasterPipeline_opts.h @@ -35,6 +35,12 @@ #define SK_UNROLL #endif +// Why does RasterPipeline have its own SIMD wrapper and is not using SkVx? SkVx is designed +// for keeping things simple, e.g. so you can put vectors in classes. SkVx has a very simple, +// predictable, memory layout - they are equivalent to a struct with an array of n values. +// Unfortunately, because of that, they will not pass in registers. A core design principle of +// SkRP is to have the 8 parameters passed into a stage be actual hardware registers (for +// optimal performance). #if defined(__clang__) template using Vec = T __attribute__((ext_vector_type(N))); #elif defined(__GNUC__) @@ -150,7 +156,6 @@ namespace SK_OPTS_NS { SI I32 iround(F v) { return (I32)(v + 0.5f); } SI U32 round(F v) { return (U32)(v + 0.5f); } - SI U32 round(F v, F scale) { return (U32)(v*scale + 0.5f); } SI U16 pack(U32 v) { return (U16)v; } SI U8 pack(U16 v) { return (U8)v; } @@ -241,7 +246,6 @@ namespace SK_OPTS_NS { SI F sqrt_(F v) { return vsqrtq_f32(v); } SI I32 iround(F v) { return vcvtnq_s32_f32(v); } SI U32 round(F v) { return vcvtnq_u32_f32(v); } - SI U32 round(F v, F scale) { return vcvtnq_u32_f32(v*scale); } #else SI bool any(I32 c) { return c[0] | c[1] | c[2] | c[3]; } SI bool all(I32 c) { return c[0] & c[1] & c[2] & c[3]; } @@ -273,10 +277,6 @@ namespace SK_OPTS_NS { SI U32 round(F v) { return vcvtq_u32_f32(v + 0.5f); } - - SI U32 round(F v, F scale) { - return vcvtq_u32_f32(mad(v, scale, F() + 0.5f)); - } #endif template @@ -351,7 +351,6 @@ namespace SK_OPTS_NS { } SI I32 iround(F v) { return (I32)_mm512_cvtps_epi32(v); } SI U32 round(F v) { return (U32)_mm512_cvtps_epi32(v); } - SI U32 round(F v, F scale) { return (U32)_mm512_cvtps_epi32(v*scale); } SI U16 pack(U32 v) { __m256i rst = _mm256_packus_epi32(_mm512_castsi512_si256((__m512i)v), _mm512_extracti64x4_epi64((__m512i)v, 1)); @@ -598,7 +597,6 @@ namespace SK_OPTS_NS { SI I32 iround(F v) { return (I32)_mm256_cvtps_epi32(v); } SI U32 round(F v) { return (U32)_mm256_cvtps_epi32(v); } - SI U32 round(F v, F scale) { return (U32)_mm256_cvtps_epi32(v*scale); } SI U16 pack(U32 v) { return (U16)_mm_packus_epi32(_mm256_extractf128_si256((__m256i)v, 0), _mm256_extractf128_si256((__m256i)v, 1)); @@ -792,7 +790,6 @@ namespace SK_OPTS_NS { SI I32 iround(F v) { return (I32)_mm_cvtps_epi32(v); } SI U32 round(F v) { return (U32)_mm_cvtps_epi32(v); } - SI U32 round(F v, F scale) { return (U32)_mm_cvtps_epi32(v*scale); } SI U16 pack(U32 v) { #if defined(SKRP_CPU_SSE41) || defined(SKRP_CPU_AVX) @@ -963,11 +960,6 @@ namespace SK_OPTS_NS { return __lasx_xvftintrz_w_s(v + t); } - SI U32 round(F v, F scale) { - F t = F() + 0.5f; - return __lasx_xvftintrz_w_s(mad(v, scale, t)); - } - SI U16 pack(U32 v) { return __lsx_vpickev_h(__lsx_vsat_wu(emulate_lasx_d_xr2vr_h(v), 15), __lsx_vsat_wu(emulate_lasx_d_xr2vr_l(v), 15)); @@ -1165,10 +1157,6 @@ namespace SK_OPTS_NS { F t = F() + 0.5f; return __lsx_vftintrz_w_s(v + t); } - SI U32 round(F v, F scale) { - F t = F() + 0.5f; - return __lsx_vftintrz_w_s(mad(v, scale, t)); } - SI U16 pack(U32 v) { __m128i tmp = __lsx_vsat_wu(v, 15); auto p = __lsx_vpickev_h(tmp, tmp); @@ -1443,10 +1431,12 @@ SI U16 to_half(F f) { #endif } -static void patch_memory_contexts(SkSpan memoryCtxPatches, - size_t dx, size_t dy, size_t tail) { - for (SkRasterPipeline_MemoryCtxPatch& patch : memoryCtxPatches) { - SkRasterPipeline_MemoryCtx* ctx = patch.info.context; +static void patch_memory_contexts(SkSpan memoryCtxPatches, + const size_t dx, + const size_t dy, + size_t tail) { + for (SkRasterPipelineContexts::MemoryCtxPatch& patch : memoryCtxPatches) { + SkRasterPipelineContexts::MemoryCtx* ctx = patch.info.context; const ptrdiff_t offset = patch.info.bytesPerPixel * (dy * ctx->stride + dx); if (patch.info.load) { @@ -1461,10 +1451,13 @@ static void patch_memory_contexts(SkSpan memory } } -static void restore_memory_contexts(SkSpan memoryCtxPatches, - size_t dx, size_t dy, size_t tail) { - for (SkRasterPipeline_MemoryCtxPatch& patch : memoryCtxPatches) { - SkRasterPipeline_MemoryCtx* ctx = patch.info.context; +static void restore_memory_contexts( + SkSpan memoryCtxPatches, + const size_t dx, + const size_t dy, + size_t tail) { + for (SkRasterPipelineContexts::MemoryCtxPatch& patch : memoryCtxPatches) { + SkRasterPipelineContexts::MemoryCtx* ctx = patch.info.context; SkASSERT(patch.backup != nullptr); ctx->pixels = patch.backup; @@ -1524,14 +1517,14 @@ static constexpr size_t N = sizeof(F) / sizeof(float); }; using Stage = void(ABI*)(Params*, SkRasterPipelineStage* program, F r, F g, F b, F a); #else - using Stage = void(ABI*)(SkRasterPipelineStage* program, size_t dx, size_t dy, + using Stage = void(ABI*)(SkRasterPipelineStage* program, const size_t dx, const size_t dy, std::byte* base, F,F,F,F, F,F,F,F); #endif static void start_pipeline(size_t dx, size_t dy, size_t xlimit, size_t ylimit, SkRasterPipelineStage* program, - SkSpan memoryCtxPatches, + SkSpan memoryCtxPatches, uint8_t* tailPointer) { uint8_t unreferencedTail; if (!tailPointer) { @@ -1578,8 +1571,8 @@ static void start_pipeline(size_t dx, size_t dy, #endif #if SKRP_NARROW_STAGES - #define DECLARE_STAGE(name, ARG, STAGE_RET, INC, OFFSET, MUSTTAIL) \ - SI STAGE_RET name##_k(ARG, size_t dx, size_t dy, std::byte*& base, \ + #define DECLARE_HIGHP_STAGE(name, ARG, STAGE_RET, INC, OFFSET, MUSTTAIL) \ + SI STAGE_RET name##_k(ARG, const size_t dx, const size_t dy, std::byte*& base, \ F& r, F& g, F& b, F& a, F& dr, F& dg, F& db, F& da); \ static void ABI name(Params* params, SkRasterPipelineStage* program, \ F r, F g, F b, F a) { \ @@ -1589,37 +1582,37 @@ static void start_pipeline(size_t dx, size_t dy, auto fn = (Stage)program->fn; \ MUSTTAIL return fn(params, program, r,g,b,a); \ } \ - SI STAGE_RET name##_k(ARG, size_t dx, size_t dy, std::byte*& base, \ + SI STAGE_RET name##_k(ARG, const size_t dx, const size_t dy, std::byte*& base, \ F& r, F& g, F& b, F& a, F& dr, F& dg, F& db, F& da) #else - #define DECLARE_STAGE(name, ARG, STAGE_RET, INC, OFFSET, MUSTTAIL) \ - SI STAGE_RET name##_k(ARG, size_t dx, size_t dy, std::byte*& base, \ + #define DECLARE_HIGHP_STAGE(name, ARG, STAGE_RET, INC, OFFSET, MUSTTAIL) \ + SI STAGE_RET name##_k(ARG, const size_t dx, const size_t dy, std::byte*& base, \ F& r, F& g, F& b, F& a, F& dr, F& dg, F& db, F& da); \ - static void ABI name(SkRasterPipelineStage* program, size_t dx, size_t dy, \ + static void ABI name(SkRasterPipelineStage* program, const size_t dx, const size_t dy, \ std::byte* base, F r, F g, F b, F a, F dr, F dg, F db, F da) { \ OFFSET name##_k(Ctx{program}, dx,dy,base, r,g,b,a, dr,dg,db,da); \ INC; \ auto fn = (Stage)program->fn; \ MUSTTAIL return fn(program, dx,dy,base, r,g,b,a, dr,dg,db,da); \ } \ - SI STAGE_RET name##_k(ARG, size_t dx, size_t dy, std::byte*& base, \ + SI STAGE_RET name##_k(ARG, const size_t dx, const size_t dy, std::byte*& base, \ F& r, F& g, F& b, F& a, F& dr, F& dg, F& db, F& da) #endif // A typical stage returns void, always increments the program counter by 1, and lets the optimizer // decide whether or not tail-calling is appropriate. -#define STAGE(name, arg) \ - DECLARE_STAGE(name, arg, void, ++program, /*no offset*/, /*no musttail*/) +#define HIGHP_STAGE(name, arg) \ + DECLARE_HIGHP_STAGE(name, arg, void, ++program, /*no offset*/, /*no musttail*/) // A tail stage returns void, always increments the program counter by 1, and uses tail-calling. // Tail-calling is necessary in SkSL-generated programs, which can be thousands of ops long, and // could overflow the stack (particularly in debug). -#define STAGE_TAIL(name, arg) \ - DECLARE_STAGE(name, arg, void, ++program, /*no offset*/, SKRP_MUSTTAIL) +#define HIGHP_TAIL_STAGE(name, arg) \ + DECLARE_HIGHP_STAGE(name, arg, void, ++program, /*no offset*/, SKRP_MUSTTAIL) // A branch stage returns an integer, which is added directly to the program counter, and tailcalls. -#define STAGE_BRANCH(name, arg) \ - DECLARE_STAGE(name, arg, int, /*no increment*/, program +=, SKRP_MUSTTAIL) +#define HIGHP_BRANCH_STAGE(name, arg) \ + DECLARE_HIGHP_STAGE(name, arg, int, /*no increment*/, program +=, SKRP_MUSTTAIL) // just_return() is a simple no-op stage that only exists to end the chain, // returning back up to start_pipeline(), and from there to the caller. @@ -1639,7 +1632,7 @@ static void start_pipeline(size_t dx, size_t dy, // the C++ stack will be reset to the state it was at when the stack_checkpoint was initially hit. // // All instances of stack_rewind (as well as the one instance of stack_checkpoint near the start of -// a pipeline) share a single context (of type SkRasterPipeline_RewindCtx). That context holds the +// a pipeline) share a single context (of type SkRasterPipelineContexts::RewindCtx). That context holds the // full state of the mutable registers that are normally passed to the next stage in the program. // // stack_rewind is the only stage other than just_return that actually returns (rather than jumping @@ -1658,7 +1651,7 @@ static void start_pipeline(size_t dx, size_t dy, #if SKRP_NARROW_STAGES static void ABI stack_checkpoint(Params* params, SkRasterPipelineStage* program, F r, F g, F b, F a) { - SkRasterPipeline_RewindCtx* ctx = Ctx{program}; + SkRasterPipelineContexts::RewindCtx* ctx = Ctx{program}; while (program) { auto next = (Stage)(++program)->fn; @@ -1681,7 +1674,7 @@ static void start_pipeline(size_t dx, size_t dy, } static void ABI stack_rewind(Params* params, SkRasterPipelineStage* program, F r, F g, F b, F a) { - SkRasterPipeline_RewindCtx* ctx = Ctx{program}; + SkRasterPipelineContexts::RewindCtx* ctx = Ctx{program}; sk_unaligned_store(ctx->r , r ); sk_unaligned_store(ctx->g , g ); sk_unaligned_store(ctx->b , b ); @@ -1695,9 +1688,9 @@ static void start_pipeline(size_t dx, size_t dy, } #else static void ABI stack_checkpoint(SkRasterPipelineStage* program, - size_t dx, size_t dy, std::byte* base, + const size_t dx, const size_t dy, std::byte* base, F r, F g, F b, F a, F dr, F dg, F db, F da) { - SkRasterPipeline_RewindCtx* ctx = Ctx{program}; + SkRasterPipelineContexts::RewindCtx* ctx = Ctx{program}; while (program) { auto next = (Stage)(++program)->fn; @@ -1719,9 +1712,9 @@ static void start_pipeline(size_t dx, size_t dy, } } static void ABI stack_rewind(SkRasterPipelineStage* program, - size_t dx, size_t dy, std::byte* base, + const size_t dx, const size_t dy, std::byte* base, F r, F g, F b, F a, F dr, F dg, F db, F da) { - SkRasterPipeline_RewindCtx* ctx = Ctx{program}; + SkRasterPipelineContexts::RewindCtx* ctx = Ctx{program}; sk_unaligned_store(ctx->r , r ); sk_unaligned_store(ctx->g , g ); sk_unaligned_store(ctx->b , b ); @@ -1785,25 +1778,28 @@ SI void from_1010102(U32 rgba, F* r, F* g, F* b, F* a) { *a = cast((rgba >> 30) ) * (1/ 3.0f); } SI void from_1010102_xr(U32 rgba, F* r, F* g, F* b, F* a) { - static constexpr float min = -0.752941f; - static constexpr float max = 1.25098f; - static constexpr float range = max - min; - *r = cast((rgba ) & 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; - *a = cast((rgba >> 30) ) * (1/ 3.0f); + // Match https://developer.apple.com/documentation/metal/mtlpixelformat/bgr10_xr?language=objc + // i.e. "float = (xr10_value - 384) / 510.0f", but with the modification that we store 2 bits + // of alpha with a regular unorm encoding. + *r = (cast((rgba ) & 0x3ff) - 384.f) * (1/510.f); + *g = (cast((rgba >> 10) & 0x3ff) - 384.f) * (1/510.f); + *b = (cast((rgba >> 20) & 0x3ff) - 384.f) * (1/510.f); + *a = (cast((rgba >> 30) ) ) * (1/3.f); // A in 1010102_xr is *not* extended range } SI void from_10101010_xr(U64 _10x6, F* r, F* g, F* b, F* a) { - *r = (cast64((_10x6 >> 6) & 0x3ff) - 384.f) / 510.f; - *g = (cast64((_10x6 >> 22) & 0x3ff) - 384.f) / 510.f; - *b = (cast64((_10x6 >> 38) & 0x3ff) - 384.f) / 510.f; - *a = (cast64((_10x6 >> 54) & 0x3ff) - 384.f) / 510.f; + // From https://developer.apple.com/documentation/metal/mtlpixelformat/bgra10_xr?language=objc + // the linear transformation is the same as 1010102_xr, except the integer encoding is shifted + // to have 6 low bits of padding. + *r = (cast64((_10x6 >> ( 0+6)) & 0x3ff) - 384.f) * (1/510.f); + *g = (cast64((_10x6 >> (16+6)) & 0x3ff) - 384.f) * (1/510.f); + *b = (cast64((_10x6 >> (32+6)) & 0x3ff) - 384.f) * (1/510.f); + *a = (cast64((_10x6 >> (48+6)) & 0x3ff) - 384.f) * (1/510.f); } SI void from_10x6(U64 _10x6, F* r, F* g, F* b, F* a) { - *r = cast64((_10x6 >> 6) & 0x3ff) * (1/1023.0f); - *g = cast64((_10x6 >> 22) & 0x3ff) * (1/1023.0f); - *b = cast64((_10x6 >> 38) & 0x3ff) * (1/1023.0f); - *a = cast64((_10x6 >> 54) & 0x3ff) * (1/1023.0f); + *r = cast64((_10x6 >> ( 0+6)) & 0x3ff) * (1/1023.0f); + *g = cast64((_10x6 >> (16+6)) & 0x3ff) * (1/1023.0f); + *b = cast64((_10x6 >> (32+6)) & 0x3ff) * (1/1023.0f); + *a = cast64((_10x6 >> (48+6)) & 0x3ff) * (1/1023.0f); } SI void from_1616(U32 _1616, F* r, F* g) { *r = cast((_1616 ) & 0xffff) * (1/65535.0f); @@ -1818,7 +1814,7 @@ SI void from_16161616(U64 _16161616, F* r, F* g, F* b, F* a) { // Used by load_ and store_ stages to get to the right (dx,dy) starting point of contiguous memory. template -SI T* ptr_at_xy(const SkRasterPipeline_MemoryCtx* ctx, size_t dx, size_t dy) { +SI T* ptr_at_xy(const SkRasterPipelineContexts::MemoryCtx* ctx, const size_t dx, const size_t dy) { return (T*)ctx->pixels + dy*ctx->stride + dx; } @@ -1985,7 +1981,7 @@ SI F atan2_(F y0, F x0) { // Used by gather_ stages to calculate the base pointer and a vector of indices to load. template -SI U32 ix_and_ptr(T** ptr, const SkRasterPipeline_GatherCtx* ctx, F x, F y) { +SI U32 ix_and_ptr(T** ptr, const SkRasterPipelineContexts::GatherCtx* ctx, F x, F y) { // We use exclusive clamp so that our min value is > 0 because ULP subtraction using U32 would // produce a NaN if applied to +0.f. x = clamp_ex(x, ctx->width ); @@ -1997,15 +1993,22 @@ SI U32 ix_and_ptr(T** ptr, const SkRasterPipeline_GatherCtx* ctx, F x, F y) { } // We often have a nominally [0,1] float value we need to scale and convert to an integer, -// whether for a table lookup or to pack back down into bytes for storage. +// whether for a table lookup or to pack back down into bytes for storage. The floating point +// value is mapped to an integer using the equation "v * scale + bias". // // In practice, especially when dealing with interesting color spaces, that notionally -// [0,1] float may be out of [0,1] range. Unorms cannot represent that, so we must clamp. +// [0,1] float may be out of [0,1] range. Unorms cannot represent that, so we must clamp to +// [0,maxI] after the bias and scale has been applied to `v`. This allows callers that explicitly +// support negative float values (extended range) to still pack to a unorm. // -// You can adjust the expected input to [0,bias] by tweaking that parameter. -SI U32 to_unorm(F v, float scale, float bias = 1.0f) { +// In most cases bias is 0 and the max value equals `scale`, but you can adjust the expected input +// by tweaking `maxI` relative to `scale`. +SI U32 to_unorm(F v, float scale, float bias, int maxI) { // Any time we use round() we probably want to use to_unorm(). - return round(min(max(0.0f, v), bias), F_(scale)); + return round(min(max(0.0f, mad(v, scale, bias)), (float) maxI)); +} +SI U32 to_unorm(F v, int scale) { + return to_unorm(v, (float) scale, /*bias=*/0.f, /*maxI=*/scale); } SI I32 cond_to_mask(I32 cond) { @@ -2031,12 +2034,12 @@ SI int32_t select_lane(I32 data, int lane) { return data[lane]; } // Now finally, normal Stages! -STAGE(seed_shader, NoCtx) { +HIGHP_STAGE(seed_shader, NoCtx) { static constexpr float iota[] = { 0.5f, 1.5f, 2.5f, 3.5f, 4.5f, 5.5f, 6.5f, 7.5f, 8.5f, 9.5f,10.5f,11.5f,12.5f,13.5f,14.5f,15.5f, }; - static_assert(std::size(iota) >= SkRasterPipeline_kMaxStride_highp); + static_assert(std::size(iota) >= SkRasterPipelineContexts::kMaxStride_highp); // It's important for speed to explicitly cast(dx) and cast(dy), // which has the effect of splatting them to vectors before converting to floats. @@ -2047,10 +2050,10 @@ STAGE(seed_shader, NoCtx) { a = F0; } -STAGE(dither, const float* rate) { +HIGHP_STAGE(dither, const float* rate) { // Get [(dx,dy), (dx+1,dy), (dx+2,dy), ...] loaded up in integer vectors. uint32_t iota[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; - static_assert(std::size(iota) >= SkRasterPipeline_kMaxStride_highp); + static_assert(std::size(iota) >= SkRasterPipelineContexts::kMaxStride_highp); U32 X = U32_(dx) + sk_unaligned_load(iota), Y = U32_(dy); @@ -2082,20 +2085,20 @@ STAGE(dither, const float* rate) { } // load 4 floats from memory, and splat them into r,g,b,a -STAGE(uniform_color, const SkRasterPipeline_UniformColorCtx* c) { +HIGHP_STAGE(uniform_color, const SkRasterPipelineContexts::UniformColorCtx* c) { r = F_(c->r); g = F_(c->g); b = F_(c->b); a = F_(c->a); } -STAGE(unbounded_uniform_color, const SkRasterPipeline_UniformColorCtx* c) { +HIGHP_STAGE(unbounded_uniform_color, const SkRasterPipelineContexts::UniformColorCtx* c) { r = F_(c->r); g = F_(c->g); b = F_(c->b); a = F_(c->a); } // load 4 floats from memory, and splat them into dr,dg,db,da -STAGE(uniform_color_dst, const SkRasterPipeline_UniformColorCtx* c) { +HIGHP_STAGE(uniform_color_dst, const SkRasterPipelineContexts::UniformColorCtx* c) { dr = F_(c->r); dg = F_(c->g); db = F_(c->b); @@ -2103,17 +2106,17 @@ STAGE(uniform_color_dst, const SkRasterPipeline_UniformColorCtx* c) { } // splats opaque-black into r,g,b,a -STAGE(black_color, NoCtx) { +HIGHP_STAGE(black_color, NoCtx) { r = g = b = F0; a = F1; } -STAGE(white_color, NoCtx) { +HIGHP_STAGE(white_color, NoCtx) { r = g = b = a = F1; } // load registers r,g,b,a from context (mirrors store_src) -STAGE(load_src, const float* ptr) { +HIGHP_STAGE(load_src, const float* ptr) { r = sk_unaligned_load(ptr + 0*N); g = sk_unaligned_load(ptr + 1*N); b = sk_unaligned_load(ptr + 2*N); @@ -2121,29 +2124,29 @@ STAGE(load_src, const float* ptr) { } // store registers r,g,b,a into context (mirrors load_src) -STAGE(store_src, float* ptr) { +HIGHP_STAGE(store_src, float* ptr) { sk_unaligned_store(ptr + 0*N, r); sk_unaligned_store(ptr + 1*N, g); sk_unaligned_store(ptr + 2*N, b); sk_unaligned_store(ptr + 3*N, a); } // store registers r,g into context -STAGE(store_src_rg, float* ptr) { +HIGHP_STAGE(store_src_rg, float* ptr) { sk_unaligned_store(ptr + 0*N, r); sk_unaligned_store(ptr + 1*N, g); } // load registers r,g from context -STAGE(load_src_rg, float* ptr) { +HIGHP_STAGE(load_src_rg, float* ptr) { r = sk_unaligned_load(ptr + 0*N); g = sk_unaligned_load(ptr + 1*N); } // store register a into context -STAGE(store_src_a, float* ptr) { +HIGHP_STAGE(store_src_a, float* ptr) { sk_unaligned_store(ptr, a); } // load registers dr,dg,db,da from context (mirrors store_dst) -STAGE(load_dst, const float* ptr) { +HIGHP_STAGE(load_dst, const float* ptr) { dr = sk_unaligned_load(ptr + 0*N); dg = sk_unaligned_load(ptr + 1*N); db = sk_unaligned_load(ptr + 2*N); @@ -2151,7 +2154,7 @@ STAGE(load_dst, const float* ptr) { } // store registers dr,dg,db,da into context (mirrors load_dst) -STAGE(store_dst, float* ptr) { +HIGHP_STAGE(store_dst, float* ptr) { sk_unaligned_store(ptr + 0*N, dr); sk_unaligned_store(ptr + 1*N, dg); sk_unaligned_store(ptr + 2*N, db); @@ -2161,7 +2164,7 @@ STAGE(store_dst, float* ptr) { // Most blend modes apply the same logic to each channel. #define BLEND_MODE(name) \ SI F name##_channel(F s, F d, F sa, F da); \ - STAGE(name, NoCtx) { \ + HIGHP_STAGE(name, NoCtx) { \ r = name##_channel(r,dr,a,da); \ g = name##_channel(g,dg,a,da); \ b = name##_channel(b,db,a,da); \ @@ -2192,7 +2195,7 @@ BLEND_MODE(xor_) { return mad(s, inv(da), d*inv(sa)); } // Most other blend modes apply the same logic to colors, and srcover to alpha. #define BLEND_MODE(name) \ SI F name##_channel(F s, F d, F sa, F da); \ - STAGE(name, NoCtx) { \ + HIGHP_STAGE(name, NoCtx) { \ r = name##_channel(r,dr,a,da); \ g = name##_channel(g,dg,a,da); \ b = name##_channel(b,db,a,da); \ @@ -2289,7 +2292,7 @@ SI void clip_color(F* r, F* g, F* b, F a) { *b = clip_channel(*b, l, clip_low, clip_high, mn_scale, mx_scale); } -STAGE(hue, NoCtx) { +HIGHP_STAGE(hue, NoCtx) { F R = r*a, G = g*a, B = b*a; @@ -2303,7 +2306,7 @@ STAGE(hue, NoCtx) { b = mad(b, inv(da), mad(db, inv(a), B)); a = a + nmad(a, da, da); } -STAGE(saturation, NoCtx) { +HIGHP_STAGE(saturation, NoCtx) { F R = dr*a, G = dg*a, B = db*a; @@ -2317,7 +2320,7 @@ STAGE(saturation, NoCtx) { b = mad(b, inv(da), mad(db, inv(a), B)); a = a + nmad(a, da, da); } -STAGE(color, NoCtx) { +HIGHP_STAGE(color, NoCtx) { F R = r*da, G = g*da, B = b*da; @@ -2330,7 +2333,7 @@ STAGE(color, NoCtx) { b = mad(b, inv(da), mad(db, inv(a), B)); a = a + nmad(a, da, da); } -STAGE(luminosity, NoCtx) { +HIGHP_STAGE(luminosity, NoCtx) { F R = dr*a, G = dg*a, B = db*a; @@ -2344,7 +2347,7 @@ STAGE(luminosity, NoCtx) { a = a + nmad(a, da, da); } -STAGE(srcover_rgba_8888, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(srcover_rgba_8888, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); U32 dst = load(ptr); @@ -2361,104 +2364,104 @@ STAGE(srcover_rgba_8888, const SkRasterPipeline_MemoryCtx* ctx) { a = mad(da, inv(a), a*255.0f); // { r, g, b, a} are now in [0,255] (but may be out of gamut) - // to_unorm() clamps back to gamut. Scaling by 1 since we're already 255-biased. - dst = to_unorm(r, 1, 255) - | to_unorm(g, 1, 255) << 8 - | to_unorm(b, 1, 255) << 16 - | to_unorm(a, 1, 255) << 24; + // to_unorm() clamps back to gamut. Scaling by 1 since we're already 255-based. + dst = to_unorm(r, /*scale=*/1, /*bias=*/0.f, /*maxI=*/255) + | to_unorm(g, /*scale=*/1, /*bias=*/0.f, /*maxI=*/255) << 8 + | to_unorm(b, /*scale=*/1, /*bias=*/0.f, /*maxI=*/255) << 16 + | to_unorm(a, /*scale=*/1, /*bias=*/0.f, /*maxI=*/255) << 24; store(ptr, dst); } SI F clamp_01_(F v) { return min(max(0.0f, v), 1.0f); } -STAGE(clamp_01, NoCtx) { +HIGHP_STAGE(clamp_01, NoCtx) { r = clamp_01_(r); g = clamp_01_(g); b = clamp_01_(b); a = clamp_01_(a); } -STAGE(clamp_a_01, NoCtx) { +HIGHP_STAGE(clamp_a_01, NoCtx) { a = clamp_01_(a); } -STAGE(clamp_gamut, NoCtx) { +HIGHP_STAGE(clamp_gamut, NoCtx) { a = min(max(a, 0.0f), 1.0f); r = min(max(r, 0.0f), a); g = min(max(g, 0.0f), a); b = min(max(b, 0.0f), a); } -STAGE(set_rgb, const float* rgb) { +HIGHP_STAGE(set_rgb, const float* rgb) { r = F_(rgb[0]); g = F_(rgb[1]); b = F_(rgb[2]); } -STAGE(unbounded_set_rgb, const float* rgb) { +HIGHP_STAGE(unbounded_set_rgb, const float* rgb) { r = F_(rgb[0]); g = F_(rgb[1]); b = F_(rgb[2]); } -STAGE(swap_rb, NoCtx) { +HIGHP_STAGE(swap_rb, NoCtx) { auto tmp = r; r = b; b = tmp; } -STAGE(swap_rb_dst, NoCtx) { +HIGHP_STAGE(swap_rb_dst, NoCtx) { auto tmp = dr; dr = db; db = tmp; } -STAGE(move_src_dst, NoCtx) { +HIGHP_STAGE(move_src_dst, NoCtx) { dr = r; dg = g; db = b; da = a; } -STAGE(move_dst_src, NoCtx) { +HIGHP_STAGE(move_dst_src, NoCtx) { r = dr; g = dg; b = db; a = da; } -STAGE(swap_src_dst, NoCtx) { +HIGHP_STAGE(swap_src_dst, NoCtx) { std::swap(r, dr); std::swap(g, dg); std::swap(b, db); std::swap(a, da); } -STAGE(premul, NoCtx) { +HIGHP_STAGE(premul, NoCtx) { r = r * a; g = g * a; b = b * a; } -STAGE(premul_dst, NoCtx) { +HIGHP_STAGE(premul_dst, NoCtx) { dr = dr * da; dg = dg * da; db = db * da; } -STAGE(unpremul, NoCtx) { +HIGHP_STAGE(unpremul, NoCtx) { float inf = sk_bit_cast(0x7f800000); auto scale = if_then_else(1.0f/a < inf, 1.0f/a, 0.0f); r *= scale; g *= scale; b *= scale; } -STAGE(unpremul_polar, NoCtx) { +HIGHP_STAGE(unpremul_polar, NoCtx) { float inf = sk_bit_cast(0x7f800000); auto scale = if_then_else(1.0f/a < inf, 1.0f/a, 0.0f); g *= scale; b *= scale; } -STAGE(force_opaque , NoCtx) { a = F1; } -STAGE(force_opaque_dst, NoCtx) { da = F1; } +HIGHP_STAGE(force_opaque , NoCtx) { a = F1; } +HIGHP_STAGE(force_opaque_dst, NoCtx) { da = F1; } -STAGE(rgb_to_hsl, NoCtx) { +HIGHP_STAGE(rgb_to_hsl, NoCtx) { F mx = max(r, max(g,b)), mn = min(r, min(g,b)), d = mx - mn, @@ -2478,7 +2481,7 @@ STAGE(rgb_to_hsl, NoCtx) { g = s; b = l; } -STAGE(hsl_to_rgb, NoCtx) { +HIGHP_STAGE(hsl_to_rgb, NoCtx) { // See GrRGBToHSLFilterEffect.fp F h = r, @@ -2498,7 +2501,7 @@ STAGE(hsl_to_rgb, NoCtx) { // Color conversion functions used in gradient interpolation, based on // https://www.w3.org/TR/css-color-4/#color-conversion-code -STAGE(css_lab_to_xyz, NoCtx) { +HIGHP_STAGE(css_lab_to_xyz, NoCtx) { constexpr float k = 24389 / 27.0f; constexpr float e = 216 / 24389.0f; @@ -2521,7 +2524,7 @@ STAGE(css_lab_to_xyz, NoCtx) { b = xyz[2]*D50[2]; } -STAGE(css_oklab_to_linear_srgb, NoCtx) { +HIGHP_STAGE(css_oklab_to_linear_srgb, NoCtx) { F l_ = r + 0.3963377774f * g + 0.2158037573f * b, m_ = r - 0.1055613458f * g - 0.0638541728f * b, s_ = r - 0.0894841775f * g - 1.2914855480f * b; @@ -2535,7 +2538,7 @@ STAGE(css_oklab_to_linear_srgb, NoCtx) { b = -0.0041960863f * l - 0.7034186147f * m + 1.7076147010f * s; } -STAGE(css_oklab_gamut_map_to_linear_srgb, NoCtx) { +HIGHP_STAGE(css_oklab_gamut_map_to_linear_srgb, NoCtx) { // TODO(https://crbug.com/1508329): Add support for gamut mapping. // Return a greyscale value, so that accidental use is obvious. F l_ = r, @@ -2554,7 +2557,7 @@ STAGE(css_oklab_gamut_map_to_linear_srgb, NoCtx) { // Skia stores all polar colors with hue in the first component, so this "LCH -> Lab" transform // actually takes "HCL". This is also used to do the same polar transform for OkHCL to OkLAB. // See similar comments & logic in SkGradientBaseShader.cpp. -STAGE(css_hcl_to_lab, NoCtx) { +HIGHP_STAGE(css_hcl_to_lab, NoCtx) { F H = r, C = g, L = b; @@ -2591,14 +2594,14 @@ SI RGB css_hsl_to_srgb_(F h, F s, F l) { }; } -STAGE(css_hsl_to_srgb, NoCtx) { +HIGHP_STAGE(css_hsl_to_srgb, NoCtx) { RGB rgb = css_hsl_to_srgb_(r, g, b); r = rgb.r; g = rgb.g; b = rgb.b; } -STAGE(css_hwb_to_srgb, NoCtx) { +HIGHP_STAGE(css_hwb_to_srgb, NoCtx) { g *= 0.01f; b *= 0.01f; @@ -2622,13 +2625,13 @@ SI F alpha_coverage_from_rgb_coverage(F a, F da, F cr, F cg, F cb) { , max(cr, max(cg,cb))); } -STAGE(scale_1_float, const float* c) { +HIGHP_STAGE(scale_1_float, const float* c) { r = r * *c; g = g * *c; b = b * *c; a = a * *c; } -STAGE(scale_u8, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(scale_u8, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); auto scales = load(ptr); @@ -2639,7 +2642,7 @@ STAGE(scale_u8, const SkRasterPipeline_MemoryCtx* ctx) { b = b * c; a = a * c; } -STAGE(scale_565, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(scale_565, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); F cr,cg,cb; @@ -2657,27 +2660,27 @@ SI F lerp(F from, F to, F t) { return mad(to-from, t, from); } -STAGE(lerp_1_float, const float* c) { +HIGHP_STAGE(lerp_1_float, const float* c) { r = lerp(dr, r, F_(*c)); g = lerp(dg, g, F_(*c)); b = lerp(db, b, F_(*c)); a = lerp(da, a, F_(*c)); } -STAGE(scale_native, const float scales[]) { +HIGHP_STAGE(scale_native, const float scales[]) { auto c = sk_unaligned_load(scales); r = r * c; g = g * c; b = b * c; a = a * c; } -STAGE(lerp_native, const float scales[]) { +HIGHP_STAGE(lerp_native, const float scales[]) { auto c = sk_unaligned_load(scales); r = lerp(dr, r, c); g = lerp(dg, g, c); b = lerp(db, b, c); a = lerp(da, a, c); } -STAGE(lerp_u8, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(lerp_u8, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); auto scales = load(ptr); @@ -2688,7 +2691,7 @@ STAGE(lerp_u8, const SkRasterPipeline_MemoryCtx* ctx) { b = lerp(db, b, c); a = lerp(da, a, c); } -STAGE(lerp_565, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(lerp_565, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); F cr,cg,cb; @@ -2702,7 +2705,7 @@ STAGE(lerp_565, const SkRasterPipeline_MemoryCtx* ctx) { a = lerp(da, a, ca); } -STAGE(emboss, const SkRasterPipeline_EmbossCtx* ctx) { +HIGHP_STAGE(emboss, const SkRasterPipelineContexts::EmbossCtx* ctx) { auto mptr = ptr_at_xy(&ctx->mul, dx,dy), aptr = ptr_at_xy(&ctx->add, dx,dy); @@ -2714,7 +2717,7 @@ STAGE(emboss, const SkRasterPipeline_EmbossCtx* ctx) { b = mad(b, mul, add); } -STAGE(byte_tables, const SkRasterPipeline_TablesCtx* tables) { +HIGHP_STAGE(byte_tables, const SkRasterPipelineContexts::TablesCtx* tables) { r = from_byte(gather(tables->r, to_unorm(r, 255))); g = from_byte(gather(tables->g, to_unorm(g, 255))); b = from_byte(gather(tables->b, to_unorm(b, 255))); @@ -2731,7 +2734,7 @@ SI F apply_sign(F x, U32 sign) { return sk_bit_cast(sign | sk_bit_cast(x)); } -STAGE(parametric, const skcms_TransferFunction* ctx) { +HIGHP_STAGE(parametric, const skcms_TransferFunction* ctx) { auto fn = [&](F v) { U32 sign; v = strip_sign(v, &sign); @@ -2745,7 +2748,7 @@ STAGE(parametric, const skcms_TransferFunction* ctx) { b = fn(b); } -STAGE(gamma_, const float* G) { +HIGHP_STAGE(gamma_, const float* G) { auto fn = [&](F v) { U32 sign; v = strip_sign(v, &sign); @@ -2756,7 +2759,7 @@ STAGE(gamma_, const float* G) { b = fn(b); } -STAGE(PQish, const skcms_TransferFunction* ctx) { +HIGHP_STAGE(PQish, const skcms_TransferFunction* ctx) { auto fn = [&](F v) { U32 sign; v = strip_sign(v, &sign); @@ -2772,7 +2775,7 @@ STAGE(PQish, const skcms_TransferFunction* ctx) { b = fn(b); } -STAGE(HLGish, const skcms_TransferFunction* ctx) { +HIGHP_STAGE(HLGish, const skcms_TransferFunction* ctx) { auto fn = [&](F v) { U32 sign; v = strip_sign(v, &sign); @@ -2791,7 +2794,7 @@ STAGE(HLGish, const skcms_TransferFunction* ctx) { b = fn(b); } -STAGE(HLGinvish, const skcms_TransferFunction* ctx) { +HIGHP_STAGE(HLGinvish, const skcms_TransferFunction* ctx) { auto fn = [&](F v) { U32 sign; v = strip_sign(v, &sign); @@ -2811,56 +2814,56 @@ STAGE(HLGinvish, const skcms_TransferFunction* ctx) { b = fn(b); } -STAGE(load_a8, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_a8, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); r = g = b = F0; a = from_byte(load(ptr)); } -STAGE(load_a8_dst, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_a8_dst, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); dr = dg = db = F0; da = from_byte(load(ptr)); } -STAGE(gather_a8, const SkRasterPipeline_GatherCtx* ctx) { +HIGHP_STAGE(gather_a8, const SkRasterPipelineContexts::GatherCtx* ctx) { const uint8_t* ptr; U32 ix = ix_and_ptr(&ptr, ctx, r,g); r = g = b = F0; a = from_byte(gather(ptr, ix)); } -STAGE(store_a8, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(store_a8, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); U8 packed = pack(pack(to_unorm(a, 255))); store(ptr, packed); } -STAGE(store_r8, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(store_r8, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); U8 packed = pack(pack(to_unorm(r, 255))); store(ptr, packed); } -STAGE(load_565, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_565, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); from_565(load(ptr), &r,&g,&b); a = F1; } -STAGE(load_565_dst, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_565_dst, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); from_565(load(ptr), &dr,&dg,&db); da = F1; } -STAGE(gather_565, const SkRasterPipeline_GatherCtx* ctx) { +HIGHP_STAGE(gather_565, const SkRasterPipelineContexts::GatherCtx* ctx) { const uint16_t* ptr; U32 ix = ix_and_ptr(&ptr, ctx, r,g); from_565(gather(ptr, ix), &r,&g,&b); a = F1; } -STAGE(store_565, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(store_565, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); U16 px = pack( to_unorm(r, 31) << 11 @@ -2869,20 +2872,20 @@ STAGE(store_565, const SkRasterPipeline_MemoryCtx* ctx) { store(ptr, px); } -STAGE(load_4444, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_4444, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); from_4444(load(ptr), &r,&g,&b,&a); } -STAGE(load_4444_dst, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_4444_dst, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); from_4444(load(ptr), &dr,&dg,&db,&da); } -STAGE(gather_4444, const SkRasterPipeline_GatherCtx* ctx) { +HIGHP_STAGE(gather_4444, const SkRasterPipelineContexts::GatherCtx* ctx) { const uint16_t* ptr; U32 ix = ix_and_ptr(&ptr, ctx, r,g); from_4444(gather(ptr, ix), &r,&g,&b,&a); } -STAGE(store_4444, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(store_4444, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); U16 px = pack( to_unorm(r, 15) << 12 | to_unorm(g, 15) << 8 @@ -2891,20 +2894,20 @@ STAGE(store_4444, const SkRasterPipeline_MemoryCtx* ctx) { store(ptr, px); } -STAGE(load_8888, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_8888, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); from_8888(load(ptr), &r,&g,&b,&a); } -STAGE(load_8888_dst, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_8888_dst, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); from_8888(load(ptr), &dr,&dg,&db,&da); } -STAGE(gather_8888, const SkRasterPipeline_GatherCtx* ctx) { +HIGHP_STAGE(gather_8888, const SkRasterPipelineContexts::GatherCtx* ctx) { const uint32_t* ptr; U32 ix = ix_and_ptr(&ptr, ctx, r,g); from_8888(gather(ptr, ix), &r,&g,&b,&a); } -STAGE(store_8888, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(store_8888, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); U32 px = to_unorm(r, 255) @@ -2914,74 +2917,74 @@ STAGE(store_8888, const SkRasterPipeline_MemoryCtx* ctx) { store(ptr, px); } -STAGE(load_rg88, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_rg88, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx, dy); from_88(load(ptr), &r, &g); b = F0; a = F1; } -STAGE(load_rg88_dst, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_rg88_dst, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx, dy); from_88(load(ptr), &dr, &dg); db = F0; da = F1; } -STAGE(gather_rg88, const SkRasterPipeline_GatherCtx* ctx) { +HIGHP_STAGE(gather_rg88, const SkRasterPipelineContexts::GatherCtx* ctx) { const uint16_t* ptr; U32 ix = ix_and_ptr(&ptr, ctx, r, g); from_88(gather(ptr, ix), &r, &g); b = F0; a = F1; } -STAGE(store_rg88, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(store_rg88, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx, dy); U16 px = pack( to_unorm(r, 255) | to_unorm(g, 255) << 8 ); store(ptr, px); } -STAGE(load_a16, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_a16, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); r = g = b = F0; a = from_short(load(ptr)); } -STAGE(load_a16_dst, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_a16_dst, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx, dy); dr = dg = db = F0; da = from_short(load(ptr)); } -STAGE(gather_a16, const SkRasterPipeline_GatherCtx* ctx) { +HIGHP_STAGE(gather_a16, const SkRasterPipelineContexts::GatherCtx* ctx) { const uint16_t* ptr; U32 ix = ix_and_ptr(&ptr, ctx, r, g); r = g = b = F0; a = from_short(gather(ptr, ix)); } -STAGE(store_a16, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(store_a16, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); U16 px = pack(to_unorm(a, 65535)); store(ptr, px); } -STAGE(load_rg1616, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_rg1616, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx, dy); b = F0; a = F1; from_1616(load(ptr), &r,&g); } -STAGE(load_rg1616_dst, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_rg1616_dst, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx, dy); from_1616(load(ptr), &dr, &dg); db = F0; da = F1; } -STAGE(gather_rg1616, const SkRasterPipeline_GatherCtx* ctx) { +HIGHP_STAGE(gather_rg1616, const SkRasterPipelineContexts::GatherCtx* ctx) { const uint32_t* ptr; U32 ix = ix_and_ptr(&ptr, ctx, r, g); from_1616(gather(ptr, ix), &r, &g); b = F0; a = F1; } -STAGE(store_rg1616, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(store_rg1616, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); U32 px = to_unorm(r, 65535) @@ -2989,20 +2992,20 @@ STAGE(store_rg1616, const SkRasterPipeline_MemoryCtx* ctx) { store(ptr, px); } -STAGE(load_16161616, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_16161616, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx, dy); from_16161616(load(ptr), &r,&g, &b, &a); } -STAGE(load_16161616_dst, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_16161616_dst, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx, dy); from_16161616(load(ptr), &dr, &dg, &db, &da); } -STAGE(gather_16161616, const SkRasterPipeline_GatherCtx* ctx) { +HIGHP_STAGE(gather_16161616, const SkRasterPipelineContexts::GatherCtx* ctx) { const uint64_t* ptr; U32 ix = ix_and_ptr(&ptr, ctx, r, g); from_16161616(gather(ptr, ix), &r, &g, &b, &a); } -STAGE(store_16161616, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(store_16161616, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, 4*dx,4*dy); U16 R = pack(to_unorm(r, 65535)), @@ -3013,20 +3016,20 @@ STAGE(store_16161616, const SkRasterPipeline_MemoryCtx* ctx) { store4(ptr, R,G,B,A); } -STAGE(load_10x6, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_10x6, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx, dy); from_10x6(load(ptr), &r,&g, &b, &a); } -STAGE(load_10x6_dst, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_10x6_dst, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx, dy); from_10x6(load(ptr), &dr, &dg, &db, &da); } -STAGE(gather_10x6, const SkRasterPipeline_GatherCtx* ctx) { +HIGHP_STAGE(gather_10x6, const SkRasterPipelineContexts::GatherCtx* ctx) { const uint64_t* ptr; U32 ix = ix_and_ptr(&ptr, ctx, r, g); from_10x6(gather(ptr, ix), &r, &g, &b, &a); } -STAGE(store_10x6, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(store_10x6, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, 4*dx,4*dy); U16 R = pack(to_unorm(r, 1023)) << 6, @@ -3037,60 +3040,57 @@ STAGE(store_10x6, const SkRasterPipeline_MemoryCtx* ctx) { store4(ptr, R,G,B,A); } - -STAGE(load_1010102, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_1010102, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); from_1010102(load(ptr), &r,&g,&b,&a); } -STAGE(load_1010102_dst, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_1010102_dst, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); from_1010102(load(ptr), &dr,&dg,&db,&da); } -STAGE(load_1010102_xr, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_1010102_xr, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); from_1010102_xr(load(ptr), &r,&g,&b,&a); } -STAGE(load_1010102_xr_dst, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_1010102_xr_dst, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); from_1010102_xr(load(ptr), &dr,&dg,&db,&da); } -STAGE(gather_1010102, const SkRasterPipeline_GatherCtx* ctx) { +HIGHP_STAGE(gather_1010102, const SkRasterPipelineContexts::GatherCtx* ctx) { const uint32_t* ptr; U32 ix = ix_and_ptr(&ptr, ctx, r,g); from_1010102(gather(ptr, ix), &r,&g,&b,&a); } -STAGE(gather_1010102_xr, const SkRasterPipeline_GatherCtx* ctx) { +HIGHP_STAGE(gather_1010102_xr, const SkRasterPipelineContexts::GatherCtx* ctx) { const uint32_t* ptr; U32 ix = ix_and_ptr(&ptr, ctx, r, g); from_1010102_xr(gather(ptr, ix), &r,&g,&b,&a); } -STAGE(gather_10101010_xr, const SkRasterPipeline_GatherCtx* ctx) { +HIGHP_STAGE(gather_10101010_xr, const SkRasterPipelineContexts::GatherCtx* ctx) { const uint64_t* ptr; U32 ix = ix_and_ptr(&ptr, ctx, r, g); from_10101010_xr(gather(ptr, ix), &r, &g, &b, &a); } -STAGE(load_10101010_xr, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_10101010_xr, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx, dy); from_10101010_xr(load(ptr), &r,&g, &b, &a); } -STAGE(load_10101010_xr_dst, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_10101010_xr_dst, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx, dy); from_10101010_xr(load(ptr), &dr, &dg, &db, &da); } -STAGE(store_10101010_xr, const SkRasterPipeline_MemoryCtx* ctx) { - static constexpr float min = -0.752941f; - static constexpr float max = 1.25098f; - static constexpr float range = max - min; +HIGHP_STAGE(store_10101010_xr, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, 4*dx,4*dy); - U16 R = pack(to_unorm((r - min) / range, 1023)) << 6, - G = pack(to_unorm((g - min) / range, 1023)) << 6, - B = pack(to_unorm((b - min) / range, 1023)) << 6, - A = pack(to_unorm((a - min) / range, 1023)) << 6; + // This is the inverse of from_10101010_xr, e.g. (v * 510 + 384) + U16 R = pack(to_unorm(r, /*scale=*/510, /*bias=*/384, /*maxI=*/1023)) << 6, + G = pack(to_unorm(g, /*scale=*/510, /*bias=*/384, /*maxI=*/1023)) << 6, + B = pack(to_unorm(b, /*scale=*/510, /*bias=*/384, /*maxI=*/1023)) << 6, + A = pack(to_unorm(a, /*scale=*/510, /*bias=*/384, /*maxI=*/1023)) << 6; store4(ptr, R,G,B,A); } -STAGE(store_1010102, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(store_1010102, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); U32 px = to_unorm(r, 1023) @@ -3099,19 +3099,18 @@ STAGE(store_1010102, const SkRasterPipeline_MemoryCtx* ctx) { | to_unorm(a, 3) << 30; store(ptr, px); } -STAGE(store_1010102_xr, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(store_1010102_xr, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); - static constexpr float min = -0.752941f; - static constexpr float max = 1.25098f; - static constexpr float range = max - min; - U32 px = to_unorm((r - min) / range, 1023) - | to_unorm((g - min) / range, 1023) << 10 - | to_unorm((b - min) / range, 1023) << 20 - | to_unorm(a, 3) << 30; + + // This is the inverse of from_1010102_xr, e.g. (v * 510 + 384) + U32 px = to_unorm(r, /*scale=*/510, /*bias=*/384, /*maxI=*/1023) + | to_unorm(g, /*scale=*/510, /*bias=*/384, /*maxI=*/1023) << 10 + | to_unorm(b, /*scale=*/510, /*bias=*/384, /*maxI=*/1023) << 10 + | to_unorm(a, /*scale=*/3) << 30; store(ptr, px); } -STAGE(load_f16, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_f16, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); U16 R,G,B,A; @@ -3121,7 +3120,7 @@ STAGE(load_f16, const SkRasterPipeline_MemoryCtx* ctx) { b = from_half(B); a = from_half(A); } -STAGE(load_f16_dst, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_f16_dst, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); U16 R,G,B,A; @@ -3131,7 +3130,7 @@ STAGE(load_f16_dst, const SkRasterPipeline_MemoryCtx* ctx) { db = from_half(B); da = from_half(A); } -STAGE(gather_f16, const SkRasterPipeline_GatherCtx* ctx) { +HIGHP_STAGE(gather_f16, const SkRasterPipelineContexts::GatherCtx* ctx) { const uint64_t* ptr; U32 ix = ix_and_ptr(&ptr, ctx, r,g); auto px = gather(ptr, ix); @@ -3143,7 +3142,7 @@ STAGE(gather_f16, const SkRasterPipeline_GatherCtx* ctx) { b = from_half(B); a = from_half(A); } -STAGE(store_f16, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(store_f16, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); store4((uint16_t*)ptr, to_half(r) , to_half(g) @@ -3151,7 +3150,7 @@ STAGE(store_f16, const SkRasterPipeline_MemoryCtx* ctx) { , to_half(a)); } -STAGE(load_af16, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_af16, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); U16 A = load((const uint16_t*)ptr); @@ -3160,25 +3159,25 @@ STAGE(load_af16, const SkRasterPipeline_MemoryCtx* ctx) { b = F0; a = from_half(A); } -STAGE(load_af16_dst, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_af16_dst, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx, dy); U16 A = load((const uint16_t*)ptr); dr = dg = db = F0; da = from_half(A); } -STAGE(gather_af16, const SkRasterPipeline_GatherCtx* ctx) { +HIGHP_STAGE(gather_af16, const SkRasterPipelineContexts::GatherCtx* ctx) { const uint16_t* ptr; U32 ix = ix_and_ptr(&ptr, ctx, r, g); r = g = b = F0; a = from_half(gather(ptr, ix)); } -STAGE(store_af16, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(store_af16, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); store(ptr, to_half(a)); } -STAGE(load_rgf16, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_rgf16, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx, dy); U16 R,G; @@ -3188,7 +3187,7 @@ STAGE(load_rgf16, const SkRasterPipeline_MemoryCtx* ctx) { b = F0; a = F1; } -STAGE(load_rgf16_dst, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_rgf16_dst, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx, dy); U16 R,G; @@ -3198,7 +3197,7 @@ STAGE(load_rgf16_dst, const SkRasterPipeline_MemoryCtx* ctx) { db = F0; da = F1; } -STAGE(gather_rgf16, const SkRasterPipeline_GatherCtx* ctx) { +HIGHP_STAGE(gather_rgf16, const SkRasterPipelineContexts::GatherCtx* ctx) { const uint32_t* ptr; U32 ix = ix_and_ptr(&ptr, ctx, r, g); auto px = gather(ptr, ix); @@ -3210,21 +3209,21 @@ STAGE(gather_rgf16, const SkRasterPipeline_GatherCtx* ctx) { b = F0; a = F1; } -STAGE(store_rgf16, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(store_rgf16, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx, dy); store2((uint16_t*)ptr, to_half(r) , to_half(g)); } -STAGE(load_f32, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_f32, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, 4*dx,4*dy); load4(ptr, &r,&g,&b,&a); } -STAGE(load_f32_dst, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(load_f32_dst, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, 4*dx,4*dy); load4(ptr, &dr,&dg,&db,&da); } -STAGE(gather_f32, const SkRasterPipeline_GatherCtx* ctx) { +HIGHP_STAGE(gather_f32, const SkRasterPipelineContexts::GatherCtx* ctx) { const float* ptr; U32 ix = ix_and_ptr(&ptr, ctx, r,g); r = gather(ptr, 4*ix + 0); @@ -3232,15 +3231,15 @@ STAGE(gather_f32, const SkRasterPipeline_GatherCtx* ctx) { b = gather(ptr, 4*ix + 2); a = gather(ptr, 4*ix + 3); } -STAGE(store_f32, const SkRasterPipeline_MemoryCtx* ctx) { +HIGHP_STAGE(store_f32, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, 4*dx,4*dy); store4(ptr, r,g,b,a); } -SI F exclusive_repeat(F v, const SkRasterPipeline_TileCtx* ctx) { +SI F exclusive_repeat(F v, const SkRasterPipelineContexts::TileCtx* ctx) { return v - floor_(v*ctx->invScale)*ctx->scale; } -SI F exclusive_mirror(F v, const SkRasterPipeline_TileCtx* ctx) { +SI F exclusive_mirror(F v, const SkRasterPipelineContexts::TileCtx* ctx) { auto limit = ctx->scale; auto invLimit = ctx->invScale; @@ -3260,16 +3259,24 @@ SI F exclusive_mirror(F v, const SkRasterPipeline_TileCtx* ctx) { // Tile x or y to [0,limit) == [0,limit - 1 ulp] (think, sampling from images). // The gather stages will hard clamp the output of these stages to [0,limit)... // we just need to do the basic repeat or mirroring. -STAGE(repeat_x, const SkRasterPipeline_TileCtx* ctx) { r = exclusive_repeat(r, ctx); } -STAGE(repeat_y, const SkRasterPipeline_TileCtx* ctx) { g = exclusive_repeat(g, ctx); } -STAGE(mirror_x, const SkRasterPipeline_TileCtx* ctx) { r = exclusive_mirror(r, ctx); } -STAGE(mirror_y, const SkRasterPipeline_TileCtx* ctx) { g = exclusive_mirror(g, ctx); } +HIGHP_STAGE(repeat_x, const SkRasterPipelineContexts::TileCtx* ctx) { + r = exclusive_repeat(r, ctx); +} +HIGHP_STAGE(repeat_y, const SkRasterPipelineContexts::TileCtx* ctx) { + g = exclusive_repeat(g, ctx); +} +HIGHP_STAGE(mirror_x, const SkRasterPipelineContexts::TileCtx* ctx) { + r = exclusive_mirror(r, ctx); +} +HIGHP_STAGE(mirror_y, const SkRasterPipelineContexts::TileCtx* ctx) { + g = exclusive_mirror(g, ctx); +} -STAGE( clamp_x_1, NoCtx) { r = clamp_01_(r); } -STAGE(repeat_x_1, NoCtx) { r = clamp_01_(r - floor_(r)); } -STAGE(mirror_x_1, NoCtx) { r = clamp_01_(abs_( (r-1.0f) - two(floor_((r-1.0f)*0.5f)) - 1.0f )); } +HIGHP_STAGE( clamp_x_1, NoCtx) { r = clamp_01_(r); } +HIGHP_STAGE(repeat_x_1, NoCtx) { r = clamp_01_(r - floor_(r)); } +HIGHP_STAGE(mirror_x_1, NoCtx) { r = clamp_01_(abs_( (r-1.0f) - two(floor_((r-1.0f)*0.5f)) - 1.0f )); } -STAGE(clamp_x_and_y, const SkRasterPipeline_CoordClampCtx* ctx) { +HIGHP_STAGE(clamp_x_and_y, const SkRasterPipelineContexts::CoordClampCtx* ctx) { r = min(ctx->max_x, max(ctx->min_x, r)); g = min(ctx->max_y, max(ctx->min_y, g)); } @@ -3280,19 +3287,19 @@ STAGE(clamp_x_and_y, const SkRasterPipeline_CoordClampCtx* ctx) { // After the gather stage, the r,g,b,a values are AND'd with this mask, setting them to 0 // if either of the coordinates were out of bounds. -STAGE(decal_x, SkRasterPipeline_DecalTileCtx* ctx) { +HIGHP_STAGE(decal_x, SkRasterPipelineContexts::DecalTileCtx* ctx) { auto w = ctx->limit_x; auto e = ctx->inclusiveEdge_x; auto cond = ((0 < r) & (r < w)) | (r == e); sk_unaligned_store(ctx->mask, cond_to_mask(cond)); } -STAGE(decal_y, SkRasterPipeline_DecalTileCtx* ctx) { +HIGHP_STAGE(decal_y, SkRasterPipelineContexts::DecalTileCtx* ctx) { auto h = ctx->limit_y; auto e = ctx->inclusiveEdge_y; auto cond = ((0 < g) & (g < h)) | (g == e); sk_unaligned_store(ctx->mask, cond_to_mask(cond)); } -STAGE(decal_x_and_y, SkRasterPipeline_DecalTileCtx* ctx) { +HIGHP_STAGE(decal_x_and_y, SkRasterPipelineContexts::DecalTileCtx* ctx) { auto w = ctx->limit_x; auto h = ctx->limit_y; auto ex = ctx->inclusiveEdge_x; @@ -3301,7 +3308,7 @@ STAGE(decal_x_and_y, SkRasterPipeline_DecalTileCtx* ctx) { & (((0 < g) & (g < h)) | (g == ey)); sk_unaligned_store(ctx->mask, cond_to_mask(cond)); } -STAGE(check_decal_mask, SkRasterPipeline_DecalTileCtx* ctx) { +HIGHP_STAGE(check_decal_mask, SkRasterPipelineContexts::DecalTileCtx* ctx) { auto mask = sk_unaligned_load(ctx->mask); r = sk_bit_cast(sk_bit_cast(r) & mask); g = sk_bit_cast(sk_bit_cast(g) & mask); @@ -3309,46 +3316,46 @@ STAGE(check_decal_mask, SkRasterPipeline_DecalTileCtx* ctx) { a = sk_bit_cast(sk_bit_cast(a) & mask); } -STAGE(alpha_to_gray, NoCtx) { +HIGHP_STAGE(alpha_to_gray, NoCtx) { r = g = b = a; a = F1; } -STAGE(alpha_to_gray_dst, NoCtx) { +HIGHP_STAGE(alpha_to_gray_dst, NoCtx) { dr = dg = db = da; da = F1; } -STAGE(alpha_to_red, NoCtx) { +HIGHP_STAGE(alpha_to_red, NoCtx) { r = a; a = F1; } -STAGE(alpha_to_red_dst, NoCtx) { +HIGHP_STAGE(alpha_to_red_dst, NoCtx) { dr = da; da = F1; } -STAGE(bt709_luminance_or_luma_to_alpha, NoCtx) { +HIGHP_STAGE(bt709_luminance_or_luma_to_alpha, NoCtx) { a = r*0.2126f + g*0.7152f + b*0.0722f; r = g = b = F0; } -STAGE(bt709_luminance_or_luma_to_rgb, NoCtx) { +HIGHP_STAGE(bt709_luminance_or_luma_to_rgb, NoCtx) { r = g = b = r*0.2126f + g*0.7152f + b*0.0722f; } -STAGE(matrix_translate, const float* m) { +HIGHP_STAGE(matrix_translate, const float* m) { r += m[0]; g += m[1]; } -STAGE(matrix_scale_translate, const float* m) { +HIGHP_STAGE(matrix_scale_translate, const float* m) { r = mad(r,m[0], m[2]); g = mad(g,m[1], m[3]); } -STAGE(matrix_2x3, const float* m) { +HIGHP_STAGE(matrix_2x3, const float* m) { auto R = mad(r,m[0], mad(g,m[1], m[2])), G = mad(r,m[3], mad(g,m[4], m[5])); r = R; g = G; } -STAGE(matrix_3x3, const float* m) { +HIGHP_STAGE(matrix_3x3, const float* m) { auto R = mad(r,m[0], mad(g,m[3], b*m[6])), G = mad(r,m[1], mad(g,m[4], b*m[7])), B = mad(r,m[2], mad(g,m[5], b*m[8])); @@ -3356,7 +3363,7 @@ STAGE(matrix_3x3, const float* m) { g = G; b = B; } -STAGE(matrix_3x4, const float* m) { +HIGHP_STAGE(matrix_3x4, const float* m) { auto R = mad(r,m[0], mad(g,m[3], mad(b,m[6], m[ 9]))), G = mad(r,m[1], mad(g,m[4], mad(b,m[7], m[10]))), B = mad(r,m[2], mad(g,m[5], mad(b,m[8], m[11]))); @@ -3364,7 +3371,7 @@ STAGE(matrix_3x4, const float* m) { g = G; b = B; } -STAGE(matrix_4x5, const float* m) { +HIGHP_STAGE(matrix_4x5, const float* m) { auto R = mad(r,m[ 0], mad(g,m[ 1], mad(b,m[ 2], mad(a,m[ 3], m[ 4])))), G = mad(r,m[ 5], mad(g,m[ 6], mad(b,m[ 7], mad(a,m[ 8], m[ 9])))), B = mad(r,m[10], mad(g,m[11], mad(b,m[12], mad(a,m[13], m[14])))), @@ -3374,7 +3381,7 @@ STAGE(matrix_4x5, const float* m) { b = B; a = A; } -STAGE(matrix_4x3, const float* m) { +HIGHP_STAGE(matrix_4x3, const float* m) { auto X = r, Y = g; @@ -3383,7 +3390,7 @@ STAGE(matrix_4x3, const float* m) { b = mad(X, m[2], mad(Y, m[6], m[10])); a = mad(X, m[3], mad(Y, m[7], m[11])); } -STAGE(matrix_perspective, const float* m) { +HIGHP_STAGE(matrix_perspective, const float* m) { // N.B. Unlike the other matrix_ stages, this matrix is row-major. auto R = mad(r,m[0], mad(g,m[1], m[2])), G = mad(r,m[3], mad(g,m[4], m[5])), @@ -3392,42 +3399,42 @@ STAGE(matrix_perspective, const float* m) { g = G * rcp_precise(Z); } -SI void gradient_lookup(const SkRasterPipeline_GradientCtx* c, U32 idx, F t, +SI void gradient_lookup(const SkRasterPipelineContexts::GradientCtx* c, U32 idx, F t, F* r, F* g, F* b, F* a) { F fr, br, fg, bg, fb, bb, fa, ba; #if defined(SKRP_CPU_HSW) if (c->stopCount <=8) { - fr = _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->fs[0]), (__m256i)idx); - br = _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->bs[0]), (__m256i)idx); - fg = _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->fs[1]), (__m256i)idx); - bg = _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->bs[1]), (__m256i)idx); - fb = _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->fs[2]), (__m256i)idx); - bb = _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->bs[2]), (__m256i)idx); - fa = _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->fs[3]), (__m256i)idx); - ba = _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->bs[3]), (__m256i)idx); + fr = _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->factors[0]), (__m256i)idx); + br = _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->biases[0]), (__m256i)idx); + fg = _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->factors[1]), (__m256i)idx); + bg = _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->biases[1]), (__m256i)idx); + fb = _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->factors[2]), (__m256i)idx); + bb = _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->biases[2]), (__m256i)idx); + fa = _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->factors[3]), (__m256i)idx); + ba = _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->biases[3]), (__m256i)idx); } else #elif defined(SKRP_CPU_LASX) if (c->stopCount <= 8) { - fr = (__m256)__lasx_xvperm_w(__lasx_xvld(c->fs[0], 0), idx); - br = (__m256)__lasx_xvperm_w(__lasx_xvld(c->bs[0], 0), idx); - fg = (__m256)__lasx_xvperm_w(__lasx_xvld(c->fs[1], 0), idx); - bg = (__m256)__lasx_xvperm_w(__lasx_xvld(c->bs[1], 0), idx); - fb = (__m256)__lasx_xvperm_w(__lasx_xvld(c->fs[2], 0), idx); - bb = (__m256)__lasx_xvperm_w(__lasx_xvld(c->bs[2], 0), idx); - fa = (__m256)__lasx_xvperm_w(__lasx_xvld(c->fs[3], 0), idx); - ba = (__m256)__lasx_xvperm_w(__lasx_xvld(c->bs[3], 0), idx); + fr = (__m256)__lasx_xvperm_w(__lasx_xvld(c->factors[0], 0), idx); + br = (__m256)__lasx_xvperm_w(__lasx_xvld(c->biases[0], 0), idx); + fg = (__m256)__lasx_xvperm_w(__lasx_xvld(c->factors[1], 0), idx); + bg = (__m256)__lasx_xvperm_w(__lasx_xvld(c->biases[1], 0), idx); + fb = (__m256)__lasx_xvperm_w(__lasx_xvld(c->factors[2], 0), idx); + bb = (__m256)__lasx_xvperm_w(__lasx_xvld(c->biases[2], 0), idx); + fa = (__m256)__lasx_xvperm_w(__lasx_xvld(c->factors[3], 0), idx); + ba = (__m256)__lasx_xvperm_w(__lasx_xvld(c->biases[3], 0), idx); } else #elif defined(SKRP_CPU_LSX) if (c->stopCount <= 4) { __m128i zero = __lsx_vldi(0); - fr = (__m128)__lsx_vshuf_w(idx, zero, __lsx_vld(c->fs[0], 0)); - br = (__m128)__lsx_vshuf_w(idx, zero, __lsx_vld(c->bs[0], 0)); - fg = (__m128)__lsx_vshuf_w(idx, zero, __lsx_vld(c->fs[1], 0)); - bg = (__m128)__lsx_vshuf_w(idx, zero, __lsx_vld(c->bs[1], 0)); - fb = (__m128)__lsx_vshuf_w(idx, zero, __lsx_vld(c->fs[2], 0)); - bb = (__m128)__lsx_vshuf_w(idx, zero, __lsx_vld(c->bs[2], 0)); - fa = (__m128)__lsx_vshuf_w(idx, zero, __lsx_vld(c->fs[3], 0)); - ba = (__m128)__lsx_vshuf_w(idx, zero, __lsx_vld(c->bs[3], 0)); + fr = (__m128)__lsx_vshuf_w(idx, zero, __lsx_vld(c->factors[0], 0)); + br = (__m128)__lsx_vshuf_w(idx, zero, __lsx_vld(c->biases[0], 0)); + fg = (__m128)__lsx_vshuf_w(idx, zero, __lsx_vld(c->factors[1], 0)); + bg = (__m128)__lsx_vshuf_w(idx, zero, __lsx_vld(c->biases[1], 0)); + fb = (__m128)__lsx_vshuf_w(idx, zero, __lsx_vld(c->factors[2], 0)); + bb = (__m128)__lsx_vshuf_w(idx, zero, __lsx_vld(c->biases[2], 0)); + fa = (__m128)__lsx_vshuf_w(idx, zero, __lsx_vld(c->factors[3], 0)); + ba = (__m128)__lsx_vshuf_w(idx, zero, __lsx_vld(c->biases[3], 0)); } else #endif { @@ -3437,23 +3444,23 @@ SI void gradient_lookup(const SkRasterPipeline_GradientCtx* c, U32 idx, F t, int i1 = __lsx_vpickve2gr_w(idx, 1); int i2 = __lsx_vpickve2gr_w(idx, 2); int i3 = __lsx_vpickve2gr_w(idx, 3); - fr = gather((int *)c->fs[0], i0, i1, i2, i3); - br = gather((int *)c->bs[0], i0, i1, i2, i3); - fg = gather((int *)c->fs[1], i0, i1, i2, i3); - bg = gather((int *)c->bs[1], i0, i1, i2, i3); - fb = gather((int *)c->fs[2], i0, i1, i2, i3); - bb = gather((int *)c->bs[2], i0, i1, i2, i3); - fa = gather((int *)c->fs[3], i0, i1, i2, i3); - ba = gather((int *)c->bs[3], i0, i1, i2, i3); + fr = gather((int *)c->factors[0], i0, i1, i2, i3); + br = gather((int *)c->biases[0], i0, i1, i2, i3); + fg = gather((int *)c->factors[1], i0, i1, i2, i3); + bg = gather((int *)c->biases[1], i0, i1, i2, i3); + fb = gather((int *)c->factors[2], i0, i1, i2, i3); + bb = gather((int *)c->biases[2], i0, i1, i2, i3); + fa = gather((int *)c->factors[3], i0, i1, i2, i3); + ba = gather((int *)c->biases[3], i0, i1, i2, i3); #else - fr = gather(c->fs[0], idx); - br = gather(c->bs[0], idx); - fg = gather(c->fs[1], idx); - bg = gather(c->bs[1], idx); - fb = gather(c->fs[2], idx); - bb = gather(c->bs[2], idx); - fa = gather(c->fs[3], idx); - ba = gather(c->bs[3], idx); + fr = gather(c->factors[0], idx); + br = gather(c->biases[0], idx); + fg = gather(c->factors[1], idx); + bg = gather(c->biases[1], idx); + fb = gather(c->factors[2], idx); + bb = gather(c->biases[2], idx); + fa = gather(c->factors[3], idx); + ba = gather(c->biases[3], idx); #endif } @@ -3463,13 +3470,13 @@ SI void gradient_lookup(const SkRasterPipeline_GradientCtx* c, U32 idx, F t, *a = mad(t, fa, ba); } -STAGE(evenly_spaced_gradient, const SkRasterPipeline_GradientCtx* c) { +HIGHP_STAGE(evenly_spaced_gradient, const SkRasterPipelineContexts::GradientCtx* c) { auto t = r; auto idx = trunc_(t * static_cast(c->stopCount-1)); gradient_lookup(c, idx, t, &r, &g, &b, &a); } -STAGE(gradient, const SkRasterPipeline_GradientCtx* c) { +HIGHP_STAGE(gradient, const SkRasterPipelineContexts::GradientCtx* c) { auto t = r; U32 idx = U32_(0); @@ -3481,15 +3488,16 @@ STAGE(gradient, const SkRasterPipeline_GradientCtx* c) { gradient_lookup(c, idx, t, &r, &g, &b, &a); } -STAGE(evenly_spaced_2_stop_gradient, const SkRasterPipeline_EvenlySpaced2StopGradientCtx* c) { +HIGHP_STAGE(evenly_spaced_2_stop_gradient, + const SkRasterPipelineContexts::EvenlySpaced2StopGradientCtx* c) { auto t = r; - r = mad(t, c->f[0], c->b[0]); - g = mad(t, c->f[1], c->b[1]); - b = mad(t, c->f[2], c->b[2]); - a = mad(t, c->f[3], c->b[3]); + r = mad(t, c->factor[0], c->bias[0]); + g = mad(t, c->factor[1], c->bias[1]); + b = mad(t, c->factor[2], c->bias[2]); + a = mad(t, c->factor[3], c->bias[3]); } -STAGE(xy_to_unit_angle, NoCtx) { +HIGHP_STAGE(xy_to_unit_angle, NoCtx) { F X = r, Y = g; F xabs = abs_(X), @@ -3515,7 +3523,7 @@ STAGE(xy_to_unit_angle, NoCtx) { r = phi; } -STAGE(xy_to_radius, NoCtx) { +HIGHP_STAGE(xy_to_radius, NoCtx) { F X2 = r * r, Y2 = g * g; r = sqrt_(X2 + Y2); @@ -3523,58 +3531,59 @@ STAGE(xy_to_radius, NoCtx) { // Please see https://skia.org/dev/design/conical for how our 2pt conical shader works. -STAGE(negate_x, NoCtx) { r = -r; } +HIGHP_STAGE(negate_x, NoCtx) { r = -r; } -STAGE(xy_to_2pt_conical_strip, const SkRasterPipeline_2PtConicalCtx* ctx) { +HIGHP_STAGE(xy_to_2pt_conical_strip, const SkRasterPipelineContexts::Conical2PtCtx* ctx) { F x = r, y = g, &t = r; t = x + sqrt_(ctx->fP0 - y*y); // ctx->fP0 = r0 * r0 } -STAGE(xy_to_2pt_conical_focal_on_circle, NoCtx) { +HIGHP_STAGE(xy_to_2pt_conical_focal_on_circle, NoCtx) { F x = r, y = g, &t = r; t = x + y*y / x; // (x^2 + y^2) / x } -STAGE(xy_to_2pt_conical_well_behaved, const SkRasterPipeline_2PtConicalCtx* ctx) { +HIGHP_STAGE(xy_to_2pt_conical_well_behaved, const SkRasterPipelineContexts::Conical2PtCtx* ctx) { F x = r, y = g, &t = r; t = sqrt_(x*x + y*y) - x * ctx->fP0; // ctx->fP0 = 1/r1 } -STAGE(xy_to_2pt_conical_greater, const SkRasterPipeline_2PtConicalCtx* ctx) { +HIGHP_STAGE(xy_to_2pt_conical_greater, const SkRasterPipelineContexts::Conical2PtCtx* ctx) { F x = r, y = g, &t = r; t = sqrt_(x*x - y*y) - x * ctx->fP0; // ctx->fP0 = 1/r1 } -STAGE(xy_to_2pt_conical_smaller, const SkRasterPipeline_2PtConicalCtx* ctx) { +HIGHP_STAGE(xy_to_2pt_conical_smaller, const SkRasterPipelineContexts::Conical2PtCtx* ctx) { F x = r, y = g, &t = r; t = -sqrt_(x*x - y*y) - x * ctx->fP0; // ctx->fP0 = 1/r1 } -STAGE(alter_2pt_conical_compensate_focal, const SkRasterPipeline_2PtConicalCtx* ctx) { +HIGHP_STAGE(alter_2pt_conical_compensate_focal, + const SkRasterPipelineContexts::Conical2PtCtx* ctx) { F& t = r; t = t + ctx->fP1; // ctx->fP1 = f } -STAGE(alter_2pt_conical_unswap, NoCtx) { +HIGHP_STAGE(alter_2pt_conical_unswap, NoCtx) { F& t = r; t = 1 - t; } -STAGE(mask_2pt_conical_nan, SkRasterPipeline_2PtConicalCtx* c) { +HIGHP_STAGE(mask_2pt_conical_nan, SkRasterPipelineContexts::Conical2PtCtx* c) { F& t = r; auto is_degenerate = (t != t); // NaN t = if_then_else(is_degenerate, F0, t); sk_unaligned_store(&c->fMask, cond_to_mask(!is_degenerate)); } -STAGE(mask_2pt_conical_degenerates, SkRasterPipeline_2PtConicalCtx* c) { +HIGHP_STAGE(mask_2pt_conical_degenerates, SkRasterPipelineContexts::Conical2PtCtx* c) { F& t = r; auto is_degenerate = (t <= 0) | (t != t); t = if_then_else(is_degenerate, F0, t); sk_unaligned_store(&c->fMask, cond_to_mask(!is_degenerate)); } -STAGE(apply_vector_mask, const uint32_t* ctx) { +HIGHP_STAGE(apply_vector_mask, const uint32_t* ctx) { const U32 mask = sk_unaligned_load(ctx); r = sk_bit_cast(sk_bit_cast(r) & mask); g = sk_bit_cast(sk_bit_cast(g) & mask); @@ -3582,7 +3591,7 @@ STAGE(apply_vector_mask, const uint32_t* ctx) { a = sk_bit_cast(sk_bit_cast(a) & mask); } -SI void save_xy(F* r, F* g, SkRasterPipeline_SamplerCtx* c) { +SI void save_xy(F* r, F* g, SkRasterPipelineContexts::SamplerCtx* c) { // Whether bilinear or bicubic, all sample points are at the same fractional offset (fx,fy). // They're either the 4 corners of a logical 1x1 pixel or the 16 corners of a 3x3 grid // surrounding (x,y) at (0.5,0.5) off-center. @@ -3596,7 +3605,7 @@ SI void save_xy(F* r, F* g, SkRasterPipeline_SamplerCtx* c) { sk_unaligned_store(c->fy, fy); } -STAGE(accumulate, const SkRasterPipeline_SamplerCtx* c) { +HIGHP_STAGE(accumulate, const SkRasterPipelineContexts::SamplerCtx* c) { // Bilinear and bicubic filters are both separable, so we produce independent contributions // from x and y, multiplying them together here to get each pixel's total scale factor. auto scale = sk_unaligned_load(c->scalex) @@ -3613,7 +3622,7 @@ STAGE(accumulate, const SkRasterPipeline_SamplerCtx* c) { // The y-axis is symmetric. template -SI void bilinear_x(SkRasterPipeline_SamplerCtx* ctx, F* x) { +SI void bilinear_x(SkRasterPipelineContexts::SamplerCtx* ctx, F* x) { *x = sk_unaligned_load(ctx->x) + (kScale * 0.5f); F fx = sk_unaligned_load(ctx->fx); @@ -3623,7 +3632,7 @@ SI void bilinear_x(SkRasterPipeline_SamplerCtx* ctx, F* x) { sk_unaligned_store(ctx->scalex, scalex); } template -SI void bilinear_y(SkRasterPipeline_SamplerCtx* ctx, F* y) { +SI void bilinear_y(SkRasterPipelineContexts::SamplerCtx* ctx, F* y) { *y = sk_unaligned_load(ctx->y) + (kScale * 0.5f); F fy = sk_unaligned_load(ctx->fy); @@ -3633,17 +3642,16 @@ SI void bilinear_y(SkRasterPipeline_SamplerCtx* ctx, F* y) { sk_unaligned_store(ctx->scaley, scaley); } -STAGE(bilinear_setup, SkRasterPipeline_SamplerCtx* ctx) { +HIGHP_STAGE(bilinear_setup, SkRasterPipelineContexts::SamplerCtx* ctx) { save_xy(&r, &g, ctx); // Init for accumulate dr = dg = db = da = F0; } -STAGE(bilinear_nx, SkRasterPipeline_SamplerCtx* ctx) { bilinear_x<-1>(ctx, &r); } -STAGE(bilinear_px, SkRasterPipeline_SamplerCtx* ctx) { bilinear_x<+1>(ctx, &r); } -STAGE(bilinear_ny, SkRasterPipeline_SamplerCtx* ctx) { bilinear_y<-1>(ctx, &g); } -STAGE(bilinear_py, SkRasterPipeline_SamplerCtx* ctx) { bilinear_y<+1>(ctx, &g); } - +HIGHP_STAGE(bilinear_nx, SkRasterPipelineContexts::SamplerCtx* ctx) { bilinear_x<-1>(ctx, &r); } +HIGHP_STAGE(bilinear_px, SkRasterPipelineContexts::SamplerCtx* ctx) { bilinear_x<+1>(ctx, &r); } +HIGHP_STAGE(bilinear_ny, SkRasterPipelineContexts::SamplerCtx* ctx) { bilinear_y<-1>(ctx, &g); } +HIGHP_STAGE(bilinear_py, SkRasterPipelineContexts::SamplerCtx* ctx) { bilinear_y<+1>(ctx, &g); } // In bicubic interpolation, the 16 pixels and +/- 0.5 and +/- 1.5 offsets from the sample // pixel center are combined with a non-uniform cubic filter, with higher values near the center. @@ -3656,7 +3664,7 @@ SI F bicubic_wts(F t, float A, float B, float C, float D) { } template -SI void bicubic_x(SkRasterPipeline_SamplerCtx* ctx, F* x) { +SI void bicubic_x(SkRasterPipelineContexts::SamplerCtx* ctx, F* x) { *x = sk_unaligned_load(ctx->x) + (kScale * 0.5f); F scalex; @@ -3667,7 +3675,7 @@ SI void bicubic_x(SkRasterPipeline_SamplerCtx* ctx, F* x) { sk_unaligned_store(ctx->scalex, scalex); } template -SI void bicubic_y(SkRasterPipeline_SamplerCtx* ctx, F* y) { +SI void bicubic_y(SkRasterPipelineContexts::SamplerCtx* ctx, F* y) { *y = sk_unaligned_load(ctx->y) + (kScale * 0.5f); F scaley; @@ -3678,7 +3686,7 @@ SI void bicubic_y(SkRasterPipeline_SamplerCtx* ctx, F* y) { sk_unaligned_store(ctx->scaley, scaley); } -STAGE(bicubic_setup, SkRasterPipeline_SamplerCtx* ctx) { +HIGHP_STAGE(bicubic_setup, SkRasterPipelineContexts::SamplerCtx* ctx) { save_xy(&r, &g, ctx); const float* w = ctx->weights; @@ -3699,15 +3707,15 @@ STAGE(bicubic_setup, SkRasterPipeline_SamplerCtx* ctx) { dr = dg = db = da = F0; } -STAGE(bicubic_n3x, SkRasterPipeline_SamplerCtx* ctx) { bicubic_x<-3>(ctx, &r); } -STAGE(bicubic_n1x, SkRasterPipeline_SamplerCtx* ctx) { bicubic_x<-1>(ctx, &r); } -STAGE(bicubic_p1x, SkRasterPipeline_SamplerCtx* ctx) { bicubic_x<+1>(ctx, &r); } -STAGE(bicubic_p3x, SkRasterPipeline_SamplerCtx* ctx) { bicubic_x<+3>(ctx, &r); } +HIGHP_STAGE(bicubic_n3x, SkRasterPipelineContexts::SamplerCtx* ctx) { bicubic_x<-3>(ctx, &r); } +HIGHP_STAGE(bicubic_n1x, SkRasterPipelineContexts::SamplerCtx* ctx) { bicubic_x<-1>(ctx, &r); } +HIGHP_STAGE(bicubic_p1x, SkRasterPipelineContexts::SamplerCtx* ctx) { bicubic_x<+1>(ctx, &r); } +HIGHP_STAGE(bicubic_p3x, SkRasterPipelineContexts::SamplerCtx* ctx) { bicubic_x<+3>(ctx, &r); } -STAGE(bicubic_n3y, SkRasterPipeline_SamplerCtx* ctx) { bicubic_y<-3>(ctx, &g); } -STAGE(bicubic_n1y, SkRasterPipeline_SamplerCtx* ctx) { bicubic_y<-1>(ctx, &g); } -STAGE(bicubic_p1y, SkRasterPipeline_SamplerCtx* ctx) { bicubic_y<+1>(ctx, &g); } -STAGE(bicubic_p3y, SkRasterPipeline_SamplerCtx* ctx) { bicubic_y<+3>(ctx, &g); } +HIGHP_STAGE(bicubic_n3y, SkRasterPipelineContexts::SamplerCtx* ctx) { bicubic_y<-3>(ctx, &g); } +HIGHP_STAGE(bicubic_n1y, SkRasterPipelineContexts::SamplerCtx* ctx) { bicubic_y<-1>(ctx, &g); } +HIGHP_STAGE(bicubic_p1y, SkRasterPipelineContexts::SamplerCtx* ctx) { bicubic_y<+1>(ctx, &g); } +HIGHP_STAGE(bicubic_p3y, SkRasterPipelineContexts::SamplerCtx* ctx) { bicubic_y<+3>(ctx, &g); } SI F compute_perlin_vector(U32 sample, F x, F y) { // We're relying on the packing of uint16s within a uint32, which will vary based on endianness. @@ -3728,7 +3736,7 @@ SI F compute_perlin_vector(U32 sample, F x, F y) { vecY * y); } -STAGE(perlin_noise, SkRasterPipeline_PerlinNoiseCtx* ctx) { +HIGHP_STAGE(perlin_noise, SkRasterPipelineContexts::PerlinNoiseCtx* ctx) { F noiseVecX = (r + 0.5) * ctx->baseFrequencyX; F noiseVecY = (g + 0.5) * ctx->baseFrequencyY; r = g = b = a = F0; @@ -3827,12 +3835,12 @@ STAGE(perlin_noise, SkRasterPipeline_PerlinNoiseCtx* ctx) { a = clamp_01_(a); } -STAGE(mipmap_linear_init, SkRasterPipeline_MipmapCtx* ctx) { +HIGHP_STAGE(mipmap_linear_init, SkRasterPipelineContexts::MipmapCtx* ctx) { sk_unaligned_store(ctx->x, r); sk_unaligned_store(ctx->y, g); } -STAGE(mipmap_linear_update, SkRasterPipeline_MipmapCtx* ctx) { +HIGHP_STAGE(mipmap_linear_update, SkRasterPipelineContexts::MipmapCtx* ctx) { sk_unaligned_store(ctx->r, r); sk_unaligned_store(ctx->g, g); sk_unaligned_store(ctx->b, b); @@ -3842,20 +3850,20 @@ STAGE(mipmap_linear_update, SkRasterPipeline_MipmapCtx* ctx) { g = sk_unaligned_load(ctx->y) * ctx->scaleY; } -STAGE(mipmap_linear_finish, SkRasterPipeline_MipmapCtx* ctx) { +HIGHP_STAGE(mipmap_linear_finish, SkRasterPipelineContexts::MipmapCtx* ctx) { r = lerp(sk_unaligned_load(ctx->r), r, F_(ctx->lowerWeight)); g = lerp(sk_unaligned_load(ctx->g), g, F_(ctx->lowerWeight)); b = lerp(sk_unaligned_load(ctx->b), b, F_(ctx->lowerWeight)); a = lerp(sk_unaligned_load(ctx->a), a, F_(ctx->lowerWeight)); } -STAGE(callback, SkRasterPipeline_CallbackCtx* c) { +HIGHP_STAGE(callback, SkRasterPipelineContexts::CallbackCtx* c) { store4(c->rgba, r,g,b,a); c->fn(c, N); load4(c->read_from, &r,&g,&b,&a); } -STAGE_TAIL(set_base_pointer, std::byte* p) { +HIGHP_TAIL_STAGE(set_base_pointer, std::byte* p) { base = p; } @@ -3870,22 +3878,22 @@ STAGE_TAIL(set_base_pointer, std::byte* p) { sk_bit_cast(g) & \ sk_bit_cast(b)) -STAGE_TAIL(init_lane_masks, SkRasterPipeline_InitLaneMasksCtx* ctx) { +HIGHP_TAIL_STAGE(init_lane_masks, SkRasterPipelineContexts::InitLaneMasksCtx* ctx) { uint32_t iota[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; - static_assert(std::size(iota) >= SkRasterPipeline_kMaxStride_highp); + static_assert(std::size(iota) >= SkRasterPipelineContexts::kMaxStride_highp); I32 mask = cond_to_mask(sk_unaligned_load(iota) < *ctx->tail); r = g = b = a = sk_bit_cast(mask); } -STAGE_TAIL(store_device_xy01, F* dst) { +HIGHP_TAIL_STAGE(store_device_xy01, F* dst) { // This is very similar to `seed_shader + store_src`, but b/a are backwards. // (sk_FragCoord actually puts w=1 in the w slot.) static constexpr float iota[] = { 0.5f, 1.5f, 2.5f, 3.5f, 4.5f, 5.5f, 6.5f, 7.5f, 8.5f, 9.5f,10.5f,11.5f,12.5f,13.5f,14.5f,15.5f, }; - static_assert(std::size(iota) >= SkRasterPipeline_kMaxStride_highp); + static_assert(std::size(iota) >= SkRasterPipelineContexts::kMaxStride_highp); dst[0] = cast(U32_(dx)) + sk_unaligned_load(iota); dst[1] = cast(U32_(dy)) + 0.5f; @@ -3893,7 +3901,7 @@ STAGE_TAIL(store_device_xy01, F* dst) { dst[3] = F1; } -STAGE_TAIL(exchange_src, F* rgba) { +HIGHP_TAIL_STAGE(exchange_src, F* rgba) { // Swaps r,g,b,a registers with the values at `rgba`. F temp[4] = {r, g, b, a}; r = rgba[0]; @@ -3906,57 +3914,57 @@ STAGE_TAIL(exchange_src, F* rgba) { rgba[3] = temp[3]; } -STAGE_TAIL(load_condition_mask, F* ctx) { +HIGHP_TAIL_STAGE(load_condition_mask, F* ctx) { r = sk_unaligned_load(ctx); update_execution_mask(); } -STAGE_TAIL(store_condition_mask, F* ctx) { +HIGHP_TAIL_STAGE(store_condition_mask, F* ctx) { sk_unaligned_store(ctx, r); } -STAGE_TAIL(merge_condition_mask, I32* ptr) { +HIGHP_TAIL_STAGE(merge_condition_mask, I32* ptr) { // Set the condition-mask to the intersection of two adjacent masks at the pointer. r = sk_bit_cast(ptr[0] & ptr[1]); update_execution_mask(); } -STAGE_TAIL(merge_inv_condition_mask, I32* ptr) { +HIGHP_TAIL_STAGE(merge_inv_condition_mask, I32* ptr) { // Set the condition-mask to the intersection of the first mask and the inverse of the second. r = sk_bit_cast(ptr[0] & ~ptr[1]); update_execution_mask(); } -STAGE_TAIL(load_loop_mask, F* ctx) { +HIGHP_TAIL_STAGE(load_loop_mask, F* ctx) { g = sk_unaligned_load(ctx); update_execution_mask(); } -STAGE_TAIL(store_loop_mask, F* ctx) { +HIGHP_TAIL_STAGE(store_loop_mask, F* ctx) { sk_unaligned_store(ctx, g); } -STAGE_TAIL(mask_off_loop_mask, NoCtx) { +HIGHP_TAIL_STAGE(mask_off_loop_mask, NoCtx) { // We encountered a break statement. If a lane was active, it should be masked off now, and stay // masked-off until the termination of the loop. g = sk_bit_cast(sk_bit_cast(g) & ~execution_mask()); update_execution_mask(); } -STAGE_TAIL(reenable_loop_mask, I32* ptr) { +HIGHP_TAIL_STAGE(reenable_loop_mask, I32* ptr) { // Set the loop-mask to the union of the current loop-mask with the mask at the pointer. g = sk_bit_cast(sk_bit_cast(g) | ptr[0]); update_execution_mask(); } -STAGE_TAIL(merge_loop_mask, I32* ptr) { +HIGHP_TAIL_STAGE(merge_loop_mask, I32* ptr) { // Set the loop-mask to the intersection of the current loop-mask with the mask at the pointer. // (Note: this behavior subtly differs from merge_condition_mask!) g = sk_bit_cast(sk_bit_cast(g) & ptr[0]); update_execution_mask(); } -STAGE_TAIL(continue_op, I32* continueMask) { +HIGHP_TAIL_STAGE(continue_op, I32* continueMask) { // Set any currently-executing lanes in the continue-mask to true. *continueMask |= execution_mask(); @@ -3965,7 +3973,7 @@ STAGE_TAIL(continue_op, I32* continueMask) { update_execution_mask(); } -STAGE_TAIL(case_op, SkRasterPipeline_CaseOpCtx* packed) { +HIGHP_TAIL_STAGE(case_op, SkRasterPipelineContexts::CaseOpCtx* packed) { auto ctx = SkRPCtxUtils::Unpack(packed); // Check each lane to see if the case value matches the expectation. @@ -3981,43 +3989,41 @@ STAGE_TAIL(case_op, SkRasterPipeline_CaseOpCtx* packed) { *defaultMask &= ~caseMatches; } -STAGE_TAIL(load_return_mask, F* ctx) { +HIGHP_TAIL_STAGE(load_return_mask, F* ctx) { b = sk_unaligned_load(ctx); update_execution_mask(); } -STAGE_TAIL(store_return_mask, F* ctx) { +HIGHP_TAIL_STAGE(store_return_mask, F* ctx) { sk_unaligned_store(ctx, b); } -STAGE_TAIL(mask_off_return_mask, NoCtx) { +HIGHP_TAIL_STAGE(mask_off_return_mask, NoCtx) { // We encountered a return statement. If a lane was active, it should be masked off now, and // stay masked-off until the end of the function. b = sk_bit_cast(sk_bit_cast(b) & ~execution_mask()); update_execution_mask(); } -STAGE_BRANCH(branch_if_all_lanes_active, SkRasterPipeline_BranchIfAllLanesActiveCtx* ctx) { +HIGHP_BRANCH_STAGE(branch_if_all_lanes_active, SkRasterPipelineContexts::BranchIfAllLanesActiveCtx* ctx) { uint32_t iota[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; - static_assert(std::size(iota) >= SkRasterPipeline_kMaxStride_highp); + static_assert(std::size(iota) >= SkRasterPipelineContexts::kMaxStride_highp); I32 tailLanes = cond_to_mask(*ctx->tail <= sk_unaligned_load(iota)); return all(execution_mask() | tailLanes) ? ctx->offset : 1; } -STAGE_BRANCH(branch_if_any_lanes_active, SkRasterPipeline_BranchCtx* ctx) { +HIGHP_BRANCH_STAGE(branch_if_any_lanes_active, SkRasterPipelineContexts::BranchCtx* ctx) { return any(execution_mask()) ? ctx->offset : 1; } -STAGE_BRANCH(branch_if_no_lanes_active, SkRasterPipeline_BranchCtx* ctx) { +HIGHP_BRANCH_STAGE(branch_if_no_lanes_active, SkRasterPipelineContexts::BranchCtx* ctx) { return any(execution_mask()) ? 1 : ctx->offset; } -STAGE_BRANCH(jump, SkRasterPipeline_BranchCtx* ctx) { - return ctx->offset; -} +HIGHP_BRANCH_STAGE(jump, SkRasterPipelineContexts::BranchCtx* ctx) { return ctx->offset; } -STAGE_BRANCH(branch_if_no_active_lanes_eq, SkRasterPipeline_BranchIfEqualCtx* ctx) { +HIGHP_BRANCH_STAGE(branch_if_no_active_lanes_eq, SkRasterPipelineContexts::BranchIfEqualCtx* ctx) { // Compare each lane against the expected value... I32 match = cond_to_mask(*(const I32*)ctx->ptr == ctx->value); // ... but mask off lanes that aren't executing. @@ -4026,28 +4032,28 @@ STAGE_BRANCH(branch_if_no_active_lanes_eq, SkRasterPipeline_BranchIfEqualCtx* ct return any(match) ? 1 : ctx->offset; } -STAGE_TAIL(trace_line, SkRasterPipeline_TraceLineCtx* ctx) { +HIGHP_TAIL_STAGE(trace_line, SkRasterPipelineContexts::TraceLineCtx* ctx) { const I32* traceMask = (const I32*)ctx->traceMask; if (any(execution_mask() & *traceMask)) { ctx->traceHook->line(ctx->lineNumber); } } -STAGE_TAIL(trace_enter, SkRasterPipeline_TraceFuncCtx* ctx) { +HIGHP_TAIL_STAGE(trace_enter, SkRasterPipelineContexts::TraceFuncCtx* ctx) { const I32* traceMask = (const I32*)ctx->traceMask; if (any(execution_mask() & *traceMask)) { ctx->traceHook->enter(ctx->funcIdx); } } -STAGE_TAIL(trace_exit, SkRasterPipeline_TraceFuncCtx* ctx) { +HIGHP_TAIL_STAGE(trace_exit, SkRasterPipelineContexts::TraceFuncCtx* ctx) { const I32* traceMask = (const I32*)ctx->traceMask; if (any(execution_mask() & *traceMask)) { ctx->traceHook->exit(ctx->funcIdx); } } -STAGE_TAIL(trace_scope, SkRasterPipeline_TraceScopeCtx* ctx) { +HIGHP_TAIL_STAGE(trace_scope, SkRasterPipelineContexts::TraceScopeCtx* ctx) { // Note that trace_scope intentionally does not incorporate the execution mask. Otherwise, the // scopes would become unbalanced if the execution mask changed in the middle of a block. The // caller is responsible for providing a combined trace- and execution-mask. @@ -4057,7 +4063,7 @@ STAGE_TAIL(trace_scope, SkRasterPipeline_TraceScopeCtx* ctx) { } } -STAGE_TAIL(trace_var, SkRasterPipeline_TraceVarCtx* ctx) { +HIGHP_TAIL_STAGE(trace_var, SkRasterPipelineContexts::TraceVarCtx* ctx) { const I32* traceMask = (const I32*)ctx->traceMask; I32 mask = execution_mask() & *traceMask; if (any(mask)) { @@ -4083,25 +4089,25 @@ STAGE_TAIL(trace_var, SkRasterPipeline_TraceVarCtx* ctx) { } } -STAGE_TAIL(copy_uniform, SkRasterPipeline_UniformCtx* ctx) { +HIGHP_TAIL_STAGE(copy_uniform, SkRasterPipelineContexts::UniformCtx* ctx) { const int* src = ctx->src; I32* dst = (I32*)ctx->dst; dst[0] = I32_(src[0]); } -STAGE_TAIL(copy_2_uniforms, SkRasterPipeline_UniformCtx* ctx) { +HIGHP_TAIL_STAGE(copy_2_uniforms, SkRasterPipelineContexts::UniformCtx* ctx) { const int* src = ctx->src; I32* dst = (I32*)ctx->dst; dst[0] = I32_(src[0]); dst[1] = I32_(src[1]); } -STAGE_TAIL(copy_3_uniforms, SkRasterPipeline_UniformCtx* ctx) { +HIGHP_TAIL_STAGE(copy_3_uniforms, SkRasterPipelineContexts::UniformCtx* ctx) { const int* src = ctx->src; I32* dst = (I32*)ctx->dst; dst[0] = I32_(src[0]); dst[1] = I32_(src[1]); dst[2] = I32_(src[2]); } -STAGE_TAIL(copy_4_uniforms, SkRasterPipeline_UniformCtx* ctx) { +HIGHP_TAIL_STAGE(copy_4_uniforms, SkRasterPipelineContexts::UniformCtx* ctx) { const int* src = ctx->src; I32* dst = (I32*)ctx->dst; dst[0] = I32_(src[0]); @@ -4110,25 +4116,25 @@ STAGE_TAIL(copy_4_uniforms, SkRasterPipeline_UniformCtx* ctx) { dst[3] = I32_(src[3]); } -STAGE_TAIL(copy_constant, SkRasterPipeline_ConstantCtx* packed) { +HIGHP_TAIL_STAGE(copy_constant, SkRasterPipelineContexts::ConstantCtx* packed) { auto ctx = SkRPCtxUtils::Unpack(packed); I32* dst = (I32*)(base + ctx.dst); I32 value = I32_(ctx.value); dst[0] = value; } -STAGE_TAIL(splat_2_constants, SkRasterPipeline_ConstantCtx* packed) { +HIGHP_TAIL_STAGE(splat_2_constants, SkRasterPipelineContexts::ConstantCtx* packed) { auto ctx = SkRPCtxUtils::Unpack(packed); I32* dst = (I32*)(base + ctx.dst); I32 value = I32_(ctx.value); dst[0] = dst[1] = value; } -STAGE_TAIL(splat_3_constants, SkRasterPipeline_ConstantCtx* packed) { +HIGHP_TAIL_STAGE(splat_3_constants, SkRasterPipelineContexts::ConstantCtx* packed) { auto ctx = SkRPCtxUtils::Unpack(packed); I32* dst = (I32*)(base + ctx.dst); I32 value = I32_(ctx.value); dst[0] = dst[1] = dst[2] = value; } -STAGE_TAIL(splat_4_constants, SkRasterPipeline_ConstantCtx* packed) { +HIGHP_TAIL_STAGE(splat_4_constants, SkRasterPipelineContexts::ConstantCtx* packed) { auto ctx = SkRPCtxUtils::Unpack(packed); I32* dst = (I32*)(base + ctx.dst); I32 value = I32_(ctx.value); @@ -4136,28 +4142,29 @@ STAGE_TAIL(splat_4_constants, SkRasterPipeline_ConstantCtx* packed) { } template -SI void copy_n_slots_unmasked_fn(SkRasterPipeline_BinaryOpCtx* packed, std::byte* base) { +SI void copy_n_slots_unmasked_fn(SkRasterPipelineContexts::BinaryOpCtx* packed, std::byte* base) { auto ctx = SkRPCtxUtils::Unpack(packed); F* dst = (F*)(base + ctx.dst); F* src = (F*)(base + ctx.src); memcpy(dst, src, sizeof(F) * NumSlots); } -STAGE_TAIL(copy_slot_unmasked, SkRasterPipeline_BinaryOpCtx* packed) { +HIGHP_TAIL_STAGE(copy_slot_unmasked, SkRasterPipelineContexts::BinaryOpCtx* packed) { copy_n_slots_unmasked_fn<1>(packed, base); } -STAGE_TAIL(copy_2_slots_unmasked, SkRasterPipeline_BinaryOpCtx* packed) { +HIGHP_TAIL_STAGE(copy_2_slots_unmasked, SkRasterPipelineContexts::BinaryOpCtx* packed) { copy_n_slots_unmasked_fn<2>(packed, base); } -STAGE_TAIL(copy_3_slots_unmasked, SkRasterPipeline_BinaryOpCtx* packed) { +HIGHP_TAIL_STAGE(copy_3_slots_unmasked, SkRasterPipelineContexts::BinaryOpCtx* packed) { copy_n_slots_unmasked_fn<3>(packed, base); } -STAGE_TAIL(copy_4_slots_unmasked, SkRasterPipeline_BinaryOpCtx* packed) { +HIGHP_TAIL_STAGE(copy_4_slots_unmasked, SkRasterPipelineContexts::BinaryOpCtx* packed) { copy_n_slots_unmasked_fn<4>(packed, base); } template -SI void copy_n_immutable_unmasked_fn(SkRasterPipeline_BinaryOpCtx* packed, std::byte* base) { +SI void copy_n_immutable_unmasked_fn(SkRasterPipelineContexts::BinaryOpCtx* packed, + std::byte* base) { auto ctx = SkRPCtxUtils::Unpack(packed); // Load the scalar values. @@ -4173,21 +4180,23 @@ SI void copy_n_immutable_unmasked_fn(SkRasterPipeline_BinaryOpCtx* packed, std:: } } -STAGE_TAIL(copy_immutable_unmasked, SkRasterPipeline_BinaryOpCtx* packed) { +HIGHP_TAIL_STAGE(copy_immutable_unmasked, SkRasterPipelineContexts::BinaryOpCtx* packed) { copy_n_immutable_unmasked_fn<1>(packed, base); } -STAGE_TAIL(copy_2_immutables_unmasked, SkRasterPipeline_BinaryOpCtx* packed) { +HIGHP_TAIL_STAGE(copy_2_immutables_unmasked, SkRasterPipelineContexts::BinaryOpCtx* packed) { copy_n_immutable_unmasked_fn<2>(packed, base); } -STAGE_TAIL(copy_3_immutables_unmasked, SkRasterPipeline_BinaryOpCtx* packed) { +HIGHP_TAIL_STAGE(copy_3_immutables_unmasked, SkRasterPipelineContexts::BinaryOpCtx* packed) { copy_n_immutable_unmasked_fn<3>(packed, base); } -STAGE_TAIL(copy_4_immutables_unmasked, SkRasterPipeline_BinaryOpCtx* packed) { +HIGHP_TAIL_STAGE(copy_4_immutables_unmasked, SkRasterPipelineContexts::BinaryOpCtx* packed) { copy_n_immutable_unmasked_fn<4>(packed, base); } template -SI void copy_n_slots_masked_fn(SkRasterPipeline_BinaryOpCtx* packed, std::byte* base, I32 mask) { +SI void copy_n_slots_masked_fn(SkRasterPipelineContexts::BinaryOpCtx* packed, + std::byte* base, + I32 mask) { auto ctx = SkRPCtxUtils::Unpack(packed); I32* dst = (I32*)(base + ctx.dst); I32* src = (I32*)(base + ctx.src); @@ -4198,16 +4207,16 @@ SI void copy_n_slots_masked_fn(SkRasterPipeline_BinaryOpCtx* packed, std::byte* } } -STAGE_TAIL(copy_slot_masked, SkRasterPipeline_BinaryOpCtx* packed) { +HIGHP_TAIL_STAGE(copy_slot_masked, SkRasterPipelineContexts::BinaryOpCtx* packed) { copy_n_slots_masked_fn<1>(packed, base, execution_mask()); } -STAGE_TAIL(copy_2_slots_masked, SkRasterPipeline_BinaryOpCtx* packed) { +HIGHP_TAIL_STAGE(copy_2_slots_masked, SkRasterPipelineContexts::BinaryOpCtx* packed) { copy_n_slots_masked_fn<2>(packed, base, execution_mask()); } -STAGE_TAIL(copy_3_slots_masked, SkRasterPipeline_BinaryOpCtx* packed) { +HIGHP_TAIL_STAGE(copy_3_slots_masked, SkRasterPipelineContexts::BinaryOpCtx* packed) { copy_n_slots_masked_fn<3>(packed, base, execution_mask()); } -STAGE_TAIL(copy_4_slots_masked, SkRasterPipeline_BinaryOpCtx* packed) { +HIGHP_TAIL_STAGE(copy_4_slots_masked, SkRasterPipelineContexts::BinaryOpCtx* packed) { copy_n_slots_masked_fn<4>(packed, base, execution_mask()); } @@ -4244,24 +4253,24 @@ SI void shuffle_fn(std::byte* ptr, OffsetType* offsets, int numSlots) { } template -SI void small_swizzle_fn(SkRasterPipeline_SwizzleCtx* packed, std::byte* base) { +SI void small_swizzle_fn(SkRasterPipelineContexts::SwizzleCtx* packed, std::byte* base) { auto ctx = SkRPCtxUtils::Unpack(packed); shuffle_fn(base + ctx.dst, ctx.offsets, N); } -STAGE_TAIL(swizzle_1, SkRasterPipeline_SwizzleCtx* packed) { +HIGHP_TAIL_STAGE(swizzle_1, SkRasterPipelineContexts::SwizzleCtx* packed) { small_swizzle_fn<1>(packed, base); } -STAGE_TAIL(swizzle_2, SkRasterPipeline_SwizzleCtx* packed) { +HIGHP_TAIL_STAGE(swizzle_2, SkRasterPipelineContexts::SwizzleCtx* packed) { small_swizzle_fn<2>(packed, base); } -STAGE_TAIL(swizzle_3, SkRasterPipeline_SwizzleCtx* packed) { +HIGHP_TAIL_STAGE(swizzle_3, SkRasterPipelineContexts::SwizzleCtx* packed) { small_swizzle_fn<3>(packed, base); } -STAGE_TAIL(swizzle_4, SkRasterPipeline_SwizzleCtx* packed) { +HIGHP_TAIL_STAGE(swizzle_4, SkRasterPipelineContexts::SwizzleCtx* packed) { small_swizzle_fn<4>(packed, base); } -STAGE_TAIL(shuffle, SkRasterPipeline_ShuffleCtx* ctx) { +HIGHP_TAIL_STAGE(shuffle, SkRasterPipelineContexts::ShuffleCtx* ctx) { shuffle_fn<16>((std::byte*)ctx->ptr, ctx->offsets, ctx->count); } @@ -4276,20 +4285,20 @@ SI void swizzle_copy_masked_fn(I32* dst, const I32* src, uint16_t* offsets, I32 } } -STAGE_TAIL(swizzle_copy_slot_masked, SkRasterPipeline_SwizzleCopyCtx* ctx) { +HIGHP_TAIL_STAGE(swizzle_copy_slot_masked, SkRasterPipelineContexts::SwizzleCopyCtx* ctx) { swizzle_copy_masked_fn<1>((I32*)ctx->dst, (const I32*)ctx->src, ctx->offsets, execution_mask()); } -STAGE_TAIL(swizzle_copy_2_slots_masked, SkRasterPipeline_SwizzleCopyCtx* ctx) { +HIGHP_TAIL_STAGE(swizzle_copy_2_slots_masked, SkRasterPipelineContexts::SwizzleCopyCtx* ctx) { swizzle_copy_masked_fn<2>((I32*)ctx->dst, (const I32*)ctx->src, ctx->offsets, execution_mask()); } -STAGE_TAIL(swizzle_copy_3_slots_masked, SkRasterPipeline_SwizzleCopyCtx* ctx) { +HIGHP_TAIL_STAGE(swizzle_copy_3_slots_masked, SkRasterPipelineContexts::SwizzleCopyCtx* ctx) { swizzle_copy_masked_fn<3>((I32*)ctx->dst, (const I32*)ctx->src, ctx->offsets, execution_mask()); } -STAGE_TAIL(swizzle_copy_4_slots_masked, SkRasterPipeline_SwizzleCopyCtx* ctx) { +HIGHP_TAIL_STAGE(swizzle_copy_4_slots_masked, SkRasterPipelineContexts::SwizzleCopyCtx* ctx) { swizzle_copy_masked_fn<4>((I32*)ctx->dst, (const I32*)ctx->src, ctx->offsets, execution_mask()); } -STAGE_TAIL(copy_from_indirect_unmasked, SkRasterPipeline_CopyIndirectCtx* ctx) { +HIGHP_TAIL_STAGE(copy_from_indirect_unmasked, SkRasterPipelineContexts::CopyIndirectCtx* ctx) { // Clamp the indirect offsets to stay within the limit. U32 offsets = *(const U32*)ctx->indirectOffset; offsets = min(offsets, U32_(ctx->indirectLimit)); @@ -4299,7 +4308,7 @@ STAGE_TAIL(copy_from_indirect_unmasked, SkRasterPipeline_CopyIndirectCtx* ctx) { // Adjust the offsets forward so that they fetch from the correct lane. static constexpr uint32_t iota[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; - static_assert(std::size(iota) >= SkRasterPipeline_kMaxStride_highp); + static_assert(std::size(iota) >= SkRasterPipelineContexts::kMaxStride_highp); offsets += sk_unaligned_load(iota); // Use gather to perform indirect lookups; write the results into `dst`. @@ -4313,7 +4322,8 @@ STAGE_TAIL(copy_from_indirect_unmasked, SkRasterPipeline_CopyIndirectCtx* ctx) { } while (dst != end); } -STAGE_TAIL(copy_from_indirect_uniform_unmasked, SkRasterPipeline_CopyIndirectCtx* ctx) { +HIGHP_TAIL_STAGE(copy_from_indirect_uniform_unmasked, + SkRasterPipelineContexts::CopyIndirectCtx* ctx) { // Clamp the indirect offsets to stay within the limit. U32 offsets = *(const U32*)ctx->indirectOffset; offsets = min(offsets, U32_(ctx->indirectLimit)); @@ -4329,7 +4339,7 @@ STAGE_TAIL(copy_from_indirect_uniform_unmasked, SkRasterPipeline_CopyIndirectCtx } while (dst != end); } -STAGE_TAIL(copy_to_indirect_masked, SkRasterPipeline_CopyIndirectCtx* ctx) { +HIGHP_TAIL_STAGE(copy_to_indirect_masked, SkRasterPipelineContexts::CopyIndirectCtx* ctx) { // Clamp the indirect offsets to stay within the limit. U32 offsets = *(const U32*)ctx->indirectOffset; offsets = min(offsets, U32_(ctx->indirectLimit)); @@ -4339,7 +4349,7 @@ STAGE_TAIL(copy_to_indirect_masked, SkRasterPipeline_CopyIndirectCtx* ctx) { // Adjust the offsets forward so that they store into the correct lane. static constexpr uint32_t iota[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; - static_assert(std::size(iota) >= SkRasterPipeline_kMaxStride_highp); + static_assert(std::size(iota) >= SkRasterPipelineContexts::kMaxStride_highp); offsets += sk_unaligned_load(iota); // Perform indirect, masked writes into `dst`. @@ -4354,7 +4364,8 @@ STAGE_TAIL(copy_to_indirect_masked, SkRasterPipeline_CopyIndirectCtx* ctx) { } while (src != end); } -STAGE_TAIL(swizzle_copy_to_indirect_masked, SkRasterPipeline_SwizzleCopyIndirectCtx* ctx) { +HIGHP_TAIL_STAGE(swizzle_copy_to_indirect_masked, + SkRasterPipelineContexts::SwizzleCopyIndirectCtx* ctx) { // Clamp the indirect offsets to stay within the limit. U32 offsets = *(const U32*)ctx->indirectOffset; offsets = min(offsets, U32_(ctx->indirectLimit)); @@ -4364,7 +4375,7 @@ STAGE_TAIL(swizzle_copy_to_indirect_masked, SkRasterPipeline_SwizzleCopyIndirect // Adjust the offsets forward so that they store into the correct lane. static constexpr uint32_t iota[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; - static_assert(std::size(iota) >= SkRasterPipeline_kMaxStride_highp); + static_assert(std::size(iota) >= SkRasterPipelineContexts::kMaxStride_highp); offsets += sk_unaligned_load(iota); // Perform indirect, masked, swizzled writes into `dst`. @@ -4433,22 +4444,22 @@ SI void invsqrt_fn(F* dst) { } #define DECLARE_UNARY_FLOAT(name) \ - STAGE_TAIL(name##_float, F* dst) { apply_adjacent_unary(dst, dst + 1); } \ - STAGE_TAIL(name##_2_floats, F* dst) { apply_adjacent_unary(dst, dst + 2); } \ - STAGE_TAIL(name##_3_floats, F* dst) { apply_adjacent_unary(dst, dst + 3); } \ - STAGE_TAIL(name##_4_floats, F* dst) { apply_adjacent_unary(dst, dst + 4); } + HIGHP_TAIL_STAGE(name##_float, F* dst) { apply_adjacent_unary(dst, dst + 1); } \ + HIGHP_TAIL_STAGE(name##_2_floats, F* dst) { apply_adjacent_unary(dst, dst + 2); } \ + HIGHP_TAIL_STAGE(name##_3_floats, F* dst) { apply_adjacent_unary(dst, dst + 3); } \ + HIGHP_TAIL_STAGE(name##_4_floats, F* dst) { apply_adjacent_unary(dst, dst + 4); } #define DECLARE_UNARY_INT(name) \ - STAGE_TAIL(name##_int, I32* dst) { apply_adjacent_unary(dst, dst + 1); } \ - STAGE_TAIL(name##_2_ints, I32* dst) { apply_adjacent_unary(dst, dst + 2); } \ - STAGE_TAIL(name##_3_ints, I32* dst) { apply_adjacent_unary(dst, dst + 3); } \ - STAGE_TAIL(name##_4_ints, I32* dst) { apply_adjacent_unary(dst, dst + 4); } + HIGHP_TAIL_STAGE(name##_int, I32* dst) { apply_adjacent_unary(dst, dst + 1); } \ + HIGHP_TAIL_STAGE(name##_2_ints, I32* dst) { apply_adjacent_unary(dst, dst + 2); } \ + HIGHP_TAIL_STAGE(name##_3_ints, I32* dst) { apply_adjacent_unary(dst, dst + 3); } \ + HIGHP_TAIL_STAGE(name##_4_ints, I32* dst) { apply_adjacent_unary(dst, dst + 4); } #define DECLARE_UNARY_UINT(name) \ - STAGE_TAIL(name##_uint, U32* dst) { apply_adjacent_unary(dst, dst + 1); } \ - STAGE_TAIL(name##_2_uints, U32* dst) { apply_adjacent_unary(dst, dst + 2); } \ - STAGE_TAIL(name##_3_uints, U32* dst) { apply_adjacent_unary(dst, dst + 3); } \ - STAGE_TAIL(name##_4_uints, U32* dst) { apply_adjacent_unary(dst, dst + 4); } + HIGHP_TAIL_STAGE(name##_uint, U32* dst) { apply_adjacent_unary(dst, dst + 1); } \ + HIGHP_TAIL_STAGE(name##_2_uints, U32* dst) { apply_adjacent_unary(dst, dst + 2); } \ + HIGHP_TAIL_STAGE(name##_3_uints, U32* dst) { apply_adjacent_unary(dst, dst + 3); } \ + HIGHP_TAIL_STAGE(name##_4_uints, U32* dst) { apply_adjacent_unary(dst, dst + 4); } DECLARE_UNARY_INT(cast_to_float_from) DECLARE_UNARY_UINT(cast_to_float_from) DECLARE_UNARY_FLOAT(cast_to_int_from) @@ -4463,19 +4474,19 @@ DECLARE_UNARY_INT(abs) #undef DECLARE_UNARY_UINT // For complex unary ops, we only provide a 1-slot version to reduce code bloat. -STAGE_TAIL(sin_float, F* dst) { *dst = sin_(*dst); } -STAGE_TAIL(cos_float, F* dst) { *dst = cos_(*dst); } -STAGE_TAIL(tan_float, F* dst) { *dst = tan_(*dst); } -STAGE_TAIL(asin_float, F* dst) { *dst = asin_(*dst); } -STAGE_TAIL(acos_float, F* dst) { *dst = acos_(*dst); } -STAGE_TAIL(atan_float, F* dst) { *dst = atan_(*dst); } -STAGE_TAIL(sqrt_float, F* dst) { *dst = sqrt_(*dst); } -STAGE_TAIL(exp_float, F* dst) { *dst = approx_exp(*dst); } -STAGE_TAIL(exp2_float, F* dst) { *dst = approx_pow2(*dst); } -STAGE_TAIL(log_float, F* dst) { *dst = approx_log(*dst); } -STAGE_TAIL(log2_float, F* dst) { *dst = approx_log2(*dst); } +HIGHP_TAIL_STAGE(sin_float, F* dst) { *dst = sin_(*dst); } +HIGHP_TAIL_STAGE(cos_float, F* dst) { *dst = cos_(*dst); } +HIGHP_TAIL_STAGE(tan_float, F* dst) { *dst = tan_(*dst); } +HIGHP_TAIL_STAGE(asin_float, F* dst) { *dst = asin_(*dst); } +HIGHP_TAIL_STAGE(acos_float, F* dst) { *dst = acos_(*dst); } +HIGHP_TAIL_STAGE(atan_float, F* dst) { *dst = atan_(*dst); } +HIGHP_TAIL_STAGE(sqrt_float, F* dst) { *dst = sqrt_(*dst); } +HIGHP_TAIL_STAGE(exp_float, F* dst) { *dst = approx_exp(*dst); } +HIGHP_TAIL_STAGE(exp2_float, F* dst) { *dst = approx_pow2(*dst); } +HIGHP_TAIL_STAGE(log_float, F* dst) { *dst = approx_log(*dst); } +HIGHP_TAIL_STAGE(log2_float, F* dst) { *dst = approx_log2(*dst); } -STAGE_TAIL(inverse_mat2, F* dst) { +HIGHP_TAIL_STAGE(inverse_mat2, F* dst) { F a00 = dst[0], a01 = dst[1], a10 = dst[2], a11 = dst[3]; F det = nmad(a01, a10, a00 * a11), @@ -4486,7 +4497,7 @@ STAGE_TAIL(inverse_mat2, F* dst) { dst[3] = invdet * a00; } -STAGE_TAIL(inverse_mat3, F* dst) { +HIGHP_TAIL_STAGE(inverse_mat3, F* dst) { F a00 = dst[0], a01 = dst[1], a02 = dst[2], a10 = dst[3], a11 = dst[4], a12 = dst[5], a20 = dst[6], a21 = dst[7], a22 = dst[8]; @@ -4506,7 +4517,7 @@ STAGE_TAIL(inverse_mat3, F* dst) { dst[8] = invdet * nmad(a01, a10, a11 * a00); } -STAGE_TAIL(inverse_mat4, F* dst) { +HIGHP_TAIL_STAGE(inverse_mat4, F* dst) { F a00 = dst[0], a01 = dst[1], a02 = dst[2], a03 = dst[3], a10 = dst[4], a11 = dst[5], a12 = dst[6], a13 = dst[7], a20 = dst[8], a21 = dst[9], a22 = dst[10], a23 = dst[11], @@ -4567,7 +4578,8 @@ SI void apply_adjacent_binary(T* dst, T* src) { } template -SI void apply_adjacent_binary_packed(SkRasterPipeline_BinaryOpCtx* packed, std::byte* base) { +SI void apply_adjacent_binary_packed(SkRasterPipelineContexts::BinaryOpCtx* packed, + std::byte* base) { auto ctx = SkRPCtxUtils::Unpack(packed); std::byte* dst = base + ctx.dst; std::byte* src = base + ctx.src; @@ -4575,7 +4587,7 @@ SI void apply_adjacent_binary_packed(SkRasterPipeline_BinaryOpCtx* packed, std:: } template -SI void apply_binary_immediate(SkRasterPipeline_ConstantCtx* packed, std::byte* base) { +SI void apply_binary_immediate(SkRasterPipelineContexts::ConstantCtx* packed, std::byte* base) { auto ctx = SkRPCtxUtils::Unpack(packed); V* dst = (V*)(base + ctx.dst); // get a pointer to the destination S scalar = sk_bit_cast(ctx.value); // bit-pun the constant value as desired @@ -4673,40 +4685,40 @@ SI void mod_fn(F* dst, F* src) { *dst = nmad(*src, floor_(*dst / *src), *dst); } -#define DECLARE_N_WAY_BINARY_FLOAT(name) \ - STAGE_TAIL(name##_n_floats, SkRasterPipeline_BinaryOpCtx* packed) { \ - apply_adjacent_binary_packed(packed, base); \ +#define DECLARE_N_WAY_BINARY_FLOAT(name) \ + HIGHP_TAIL_STAGE(name##_n_floats, SkRasterPipelineContexts::BinaryOpCtx* packed) { \ + apply_adjacent_binary_packed(packed, base); \ } #define DECLARE_BINARY_FLOAT(name) \ - STAGE_TAIL(name##_float, F* dst) { apply_adjacent_binary(dst, dst + 1); } \ - STAGE_TAIL(name##_2_floats, F* dst) { apply_adjacent_binary(dst, dst + 2); } \ - STAGE_TAIL(name##_3_floats, F* dst) { apply_adjacent_binary(dst, dst + 3); } \ - STAGE_TAIL(name##_4_floats, F* dst) { apply_adjacent_binary(dst, dst + 4); } \ + HIGHP_TAIL_STAGE(name##_float, F* dst) { apply_adjacent_binary(dst, dst + 1); } \ + HIGHP_TAIL_STAGE(name##_2_floats, F* dst) { apply_adjacent_binary(dst, dst + 2); } \ + HIGHP_TAIL_STAGE(name##_3_floats, F* dst) { apply_adjacent_binary(dst, dst + 3); } \ + HIGHP_TAIL_STAGE(name##_4_floats, F* dst) { apply_adjacent_binary(dst, dst + 4); } \ DECLARE_N_WAY_BINARY_FLOAT(name) -#define DECLARE_N_WAY_BINARY_INT(name) \ - STAGE_TAIL(name##_n_ints, SkRasterPipeline_BinaryOpCtx* packed) { \ - apply_adjacent_binary_packed(packed, base); \ +#define DECLARE_N_WAY_BINARY_INT(name) \ + HIGHP_TAIL_STAGE(name##_n_ints, SkRasterPipelineContexts::BinaryOpCtx* packed) { \ + apply_adjacent_binary_packed(packed, base); \ } #define DECLARE_BINARY_INT(name) \ - STAGE_TAIL(name##_int, I32* dst) { apply_adjacent_binary(dst, dst + 1); } \ - STAGE_TAIL(name##_2_ints, I32* dst) { apply_adjacent_binary(dst, dst + 2); } \ - STAGE_TAIL(name##_3_ints, I32* dst) { apply_adjacent_binary(dst, dst + 3); } \ - STAGE_TAIL(name##_4_ints, I32* dst) { apply_adjacent_binary(dst, dst + 4); } \ + HIGHP_TAIL_STAGE(name##_int, I32* dst) { apply_adjacent_binary(dst, dst + 1); } \ + HIGHP_TAIL_STAGE(name##_2_ints, I32* dst) { apply_adjacent_binary(dst, dst + 2); } \ + HIGHP_TAIL_STAGE(name##_3_ints, I32* dst) { apply_adjacent_binary(dst, dst + 3); } \ + HIGHP_TAIL_STAGE(name##_4_ints, I32* dst) { apply_adjacent_binary(dst, dst + 4); } \ DECLARE_N_WAY_BINARY_INT(name) -#define DECLARE_N_WAY_BINARY_UINT(name) \ - STAGE_TAIL(name##_n_uints, SkRasterPipeline_BinaryOpCtx* packed) { \ - apply_adjacent_binary_packed(packed, base); \ +#define DECLARE_N_WAY_BINARY_UINT(name) \ + HIGHP_TAIL_STAGE(name##_n_uints, SkRasterPipelineContexts::BinaryOpCtx* packed) { \ + apply_adjacent_binary_packed(packed, base); \ } #define DECLARE_BINARY_UINT(name) \ - STAGE_TAIL(name##_uint, U32* dst) { apply_adjacent_binary(dst, dst + 1); } \ - STAGE_TAIL(name##_2_uints, U32* dst) { apply_adjacent_binary(dst, dst + 2); } \ - STAGE_TAIL(name##_3_uints, U32* dst) { apply_adjacent_binary(dst, dst + 3); } \ - STAGE_TAIL(name##_4_uints, U32* dst) { apply_adjacent_binary(dst, dst + 4); } \ + HIGHP_TAIL_STAGE(name##_uint, U32* dst) { apply_adjacent_binary(dst, dst + 1); } \ + HIGHP_TAIL_STAGE(name##_2_uints, U32* dst) { apply_adjacent_binary(dst, dst + 2); } \ + HIGHP_TAIL_STAGE(name##_3_uints, U32* dst) { apply_adjacent_binary(dst, dst + 3); } \ + HIGHP_TAIL_STAGE(name##_4_uints, U32* dst) { apply_adjacent_binary(dst, dst + 4); } \ DECLARE_N_WAY_BINARY_UINT(name) // Many ops reuse the int stages when performing uint arithmetic, since they're equivalent on a @@ -4732,30 +4744,30 @@ DECLARE_N_WAY_BINARY_FLOAT(atan2) DECLARE_N_WAY_BINARY_FLOAT(pow) // Some ops have an optimized version when the right-side is an immediate value. -#define DECLARE_IMM_BINARY_FLOAT(name) \ - STAGE_TAIL(name##_imm_float, SkRasterPipeline_ConstantCtx* packed) { \ - apply_binary_immediate<1, F, float, &name##_fn>(packed, base); \ +#define DECLARE_IMM_BINARY_FLOAT(name) \ + HIGHP_TAIL_STAGE(name##_imm_float, SkRasterPipelineContexts::ConstantCtx* packed) { \ + apply_binary_immediate<1, F, float, &name##_fn>(packed, base); \ } -#define DECLARE_IMM_BINARY_INT(name) \ - STAGE_TAIL(name##_imm_int, SkRasterPipeline_ConstantCtx* packed) { \ - apply_binary_immediate<1, I32, int32_t, &name##_fn>(packed, base); \ +#define DECLARE_IMM_BINARY_INT(name) \ + HIGHP_TAIL_STAGE(name##_imm_int, SkRasterPipelineContexts::ConstantCtx* packed) { \ + apply_binary_immediate<1, I32, int32_t, &name##_fn>(packed, base); \ } -#define DECLARE_MULTI_IMM_BINARY_INT(name) \ - STAGE_TAIL(name##_imm_int, SkRasterPipeline_ConstantCtx* packed) { \ - apply_binary_immediate<1, I32, int32_t, &name##_fn>(packed, base); \ - } \ - STAGE_TAIL(name##_imm_2_ints, SkRasterPipeline_ConstantCtx* packed) { \ - apply_binary_immediate<2, I32, int32_t, &name##_fn>(packed, base); \ - } \ - STAGE_TAIL(name##_imm_3_ints, SkRasterPipeline_ConstantCtx* packed) { \ - apply_binary_immediate<3, I32, int32_t, &name##_fn>(packed, base); \ - } \ - STAGE_TAIL(name##_imm_4_ints, SkRasterPipeline_ConstantCtx* packed) { \ - apply_binary_immediate<4, I32, int32_t, &name##_fn>(packed, base); \ +#define DECLARE_MULTI_IMM_BINARY_INT(name) \ + HIGHP_TAIL_STAGE(name##_imm_int, SkRasterPipelineContexts::ConstantCtx* packed) { \ + apply_binary_immediate<1, I32, int32_t, &name##_fn>(packed, base); \ + } \ + HIGHP_TAIL_STAGE(name##_imm_2_ints, SkRasterPipelineContexts::ConstantCtx* packed) { \ + apply_binary_immediate<2, I32, int32_t, &name##_fn>(packed, base); \ + } \ + HIGHP_TAIL_STAGE(name##_imm_3_ints, SkRasterPipelineContexts::ConstantCtx* packed) { \ + apply_binary_immediate<3, I32, int32_t, &name##_fn>(packed, base); \ + } \ + HIGHP_TAIL_STAGE(name##_imm_4_ints, SkRasterPipelineContexts::ConstantCtx* packed) { \ + apply_binary_immediate<4, I32, int32_t, &name##_fn>(packed, base); \ } -#define DECLARE_IMM_BINARY_UINT(name) \ - STAGE_TAIL(name##_imm_uint, SkRasterPipeline_ConstantCtx* packed) { \ - apply_binary_immediate<1, U32, uint32_t, &name##_fn>(packed, base); \ +#define DECLARE_IMM_BINARY_UINT(name) \ + HIGHP_TAIL_STAGE(name##_imm_uint, SkRasterPipelineContexts::ConstantCtx* packed) { \ + apply_binary_immediate<1, U32, uint32_t, &name##_fn>(packed, base); \ } DECLARE_IMM_BINARY_FLOAT(add) DECLARE_IMM_BINARY_INT(add) @@ -4782,18 +4794,18 @@ DECLARE_IMM_BINARY_FLOAT(cmpne) DECLARE_IMM_BINARY_INT(cmpne) // Dots can be represented with multiply and add ops, but they are so foundational that it's worth // having dedicated ops. -STAGE_TAIL(dot_2_floats, F* dst) { +HIGHP_TAIL_STAGE(dot_2_floats, F* dst) { dst[0] = mad(dst[0], dst[2], dst[1] * dst[3]); } -STAGE_TAIL(dot_3_floats, F* dst) { +HIGHP_TAIL_STAGE(dot_3_floats, F* dst) { dst[0] = mad(dst[0], dst[3], mad(dst[1], dst[4], dst[2] * dst[5])); } -STAGE_TAIL(dot_4_floats, F* dst) { +HIGHP_TAIL_STAGE(dot_4_floats, F* dst) { dst[0] = mad(dst[0], dst[4], mad(dst[1], dst[5], mad(dst[2], dst[6], @@ -4803,7 +4815,7 @@ STAGE_TAIL(dot_4_floats, F* dst) { // MxM, VxM and MxV multiplication all use matrix_multiply. Vectors are treated like a matrix with a // single column or row. template -SI void matrix_multiply(SkRasterPipeline_MatrixMultiplyCtx* packed, std::byte* base) { +SI void matrix_multiply(SkRasterPipelineContexts::MatrixMultiplyCtx* packed, std::byte* base) { auto ctx = SkRPCtxUtils::Unpack(packed); int outColumns = ctx.rightColumns, @@ -4849,21 +4861,21 @@ SI void matrix_multiply(SkRasterPipeline_MatrixMultiplyCtx* packed, std::byte* b } } -STAGE_TAIL(matrix_multiply_2, SkRasterPipeline_MatrixMultiplyCtx* packed) { +HIGHP_TAIL_STAGE(matrix_multiply_2, SkRasterPipelineContexts::MatrixMultiplyCtx* packed) { matrix_multiply<2>(packed, base); } -STAGE_TAIL(matrix_multiply_3, SkRasterPipeline_MatrixMultiplyCtx* packed) { +HIGHP_TAIL_STAGE(matrix_multiply_3, SkRasterPipelineContexts::MatrixMultiplyCtx* packed) { matrix_multiply<3>(packed, base); } -STAGE_TAIL(matrix_multiply_4, SkRasterPipeline_MatrixMultiplyCtx* packed) { +HIGHP_TAIL_STAGE(matrix_multiply_4, SkRasterPipelineContexts::MatrixMultiplyCtx* packed) { matrix_multiply<4>(packed, base); } // Refract always operates on 4-wide incident and normal vectors; for narrower inputs, the code // generator fills in the input columns with zero, and discards the extra output columns. -STAGE_TAIL(refract_4_floats, F* dst) { +HIGHP_TAIL_STAGE(refract_4_floats, F* dst) { // Algorithm adapted from https://registry.khronos.org/OpenGL-Refpages/gl4/html/refract.xhtml F *incident = dst + 0; F *normal = dst + 4; @@ -4901,7 +4913,8 @@ SI void apply_adjacent_ternary(T* dst, T* src0, T* src1) { } template -SI void apply_adjacent_ternary_packed(SkRasterPipeline_TernaryOpCtx* packed, std::byte* base) { +SI void apply_adjacent_ternary_packed(SkRasterPipelineContexts::TernaryOpCtx* packed, + std::byte* base) { auto ctx = SkRPCtxUtils::Unpack(packed); std::byte* dst = base + ctx.dst; std::byte* src0 = dst + ctx.delta; @@ -4924,25 +4937,33 @@ SI void smoothstep_fn(F* edge0, F* edge1, F* x) { *edge0 = t * t * (3.0 - 2.0 * t); } -#define DECLARE_N_WAY_TERNARY_FLOAT(name) \ - STAGE_TAIL(name##_n_floats, SkRasterPipeline_TernaryOpCtx* packed) { \ - apply_adjacent_ternary_packed(packed, base); \ +#define DECLARE_N_WAY_TERNARY_FLOAT(name) \ + HIGHP_TAIL_STAGE(name##_n_floats, SkRasterPipelineContexts::TernaryOpCtx* packed) { \ + apply_adjacent_ternary_packed(packed, base); \ } #define DECLARE_TERNARY_FLOAT(name) \ - STAGE_TAIL(name##_float, F* p) { apply_adjacent_ternary(p, p+1, p+2); } \ - STAGE_TAIL(name##_2_floats, F* p) { apply_adjacent_ternary(p, p+2, p+4); } \ - STAGE_TAIL(name##_3_floats, F* p) { apply_adjacent_ternary(p, p+3, p+6); } \ - STAGE_TAIL(name##_4_floats, F* p) { apply_adjacent_ternary(p, p+4, p+8); } \ + HIGHP_TAIL_STAGE(name##_float, F* p) { apply_adjacent_ternary(p, p+1, p+2); } \ + HIGHP_TAIL_STAGE(name##_2_floats, F* p) { apply_adjacent_ternary(p, p+2, p+4); } \ + HIGHP_TAIL_STAGE(name##_3_floats, F* p) { apply_adjacent_ternary(p, p+3, p+6); } \ + HIGHP_TAIL_STAGE(name##_4_floats, F* p) { apply_adjacent_ternary(p, p+4, p+8); } \ DECLARE_N_WAY_TERNARY_FLOAT(name) -#define DECLARE_TERNARY_INT(name) \ - STAGE_TAIL(name##_int, I32* p) { apply_adjacent_ternary(p, p+1, p+2); } \ - STAGE_TAIL(name##_2_ints, I32* p) { apply_adjacent_ternary(p, p+2, p+4); } \ - STAGE_TAIL(name##_3_ints, I32* p) { apply_adjacent_ternary(p, p+3, p+6); } \ - STAGE_TAIL(name##_4_ints, I32* p) { apply_adjacent_ternary(p, p+4, p+8); } \ - STAGE_TAIL(name##_n_ints, SkRasterPipeline_TernaryOpCtx* packed) { \ - apply_adjacent_ternary_packed(packed, base); \ +#define DECLARE_TERNARY_INT(name) \ + HIGHP_TAIL_STAGE(name##_int, I32* p) { \ + apply_adjacent_ternary(p, p + 1, p + 2); \ + } \ + HIGHP_TAIL_STAGE(name##_2_ints, I32* p) { \ + apply_adjacent_ternary(p, p + 2, p + 4); \ + } \ + HIGHP_TAIL_STAGE(name##_3_ints, I32* p) { \ + apply_adjacent_ternary(p, p + 3, p + 6); \ + } \ + HIGHP_TAIL_STAGE(name##_4_ints, I32* p) { \ + apply_adjacent_ternary(p, p + 4, p + 8); \ + } \ + HIGHP_TAIL_STAGE(name##_n_ints, SkRasterPipelineContexts::TernaryOpCtx* packed) { \ + apply_adjacent_ternary_packed(packed, base); \ } DECLARE_N_WAY_TERNARY_FLOAT(smoothstep) @@ -4953,7 +4974,7 @@ DECLARE_TERNARY_INT(mix) #undef DECLARE_TERNARY_FLOAT #undef DECLARE_TERNARY_INT -STAGE(gauss_a_to_rgba, NoCtx) { +HIGHP_STAGE(gauss_a_to_rgba, NoCtx) { // x = 1 - x; // exp(-x * x * 4) - 0.018f; // ... now approximate with quartic @@ -4970,7 +4991,7 @@ STAGE(gauss_a_to_rgba, NoCtx) { } // A specialized fused image shader for clamp-x, clamp-y, non-sRGB sampling. -STAGE(bilerp_clamp_8888, const SkRasterPipeline_GatherCtx* ctx) { +HIGHP_STAGE(bilerp_clamp_8888, const SkRasterPipelineContexts::GatherCtx* ctx) { // (cx,cy) are the center of our sample. F cx = r, cy = g; @@ -5012,7 +5033,7 @@ STAGE(bilerp_clamp_8888, const SkRasterPipeline_GatherCtx* ctx) { } // A specialized fused image shader for clamp-x, clamp-y, non-sRGB sampling. -STAGE(bicubic_clamp_8888, const SkRasterPipeline_GatherCtx* ctx) { +HIGHP_STAGE(bicubic_clamp_8888, const SkRasterPipelineContexts::GatherCtx* ctx) { // (cx,cy) are the center of our sample. F cx = r, cy = g; @@ -5061,7 +5082,7 @@ STAGE(bicubic_clamp_8888, const SkRasterPipeline_GatherCtx* ctx) { // ~~~~~~ skgpu::Swizzle stage ~~~~~~ // -STAGE(swizzle, void* ctx) { +HIGHP_STAGE(swizzle, void* ctx) { auto ir = r, ig = g, ib = b, ia = a; F* o[] = {&r, &g, &b, &a}; char swiz[4]; @@ -5082,12 +5103,11 @@ STAGE(swizzle, void* ctx) { namespace lowp { #if defined(SKRP_CPU_SCALAR) || defined(SK_ENABLE_OPTIMIZE_SIZE) || \ - defined(SK_BUILD_FOR_GOOGLE3) || defined(SK_DISABLE_LOWP_RASTER_PIPELINE) + defined(SK_DISABLE_LOWP_RASTER_PIPELINE) // We don't bother generating the lowp stages if we are: // - ... in scalar mode (MSVC, old clang, etc...) // - ... trying to save code size - // - ... building for Google3. (No justification for this, but changing it would be painful). - // - ... explicitly disabling it. This is currently just used by Flutter. + // - ... explicitly disabling it. This is currently used by Flutter and Google3. // // Having nullptr for every stage will cause SkRasterPipeline to always use the highp stages. #define M(st) static void (*st)(void) = nullptr; @@ -5096,7 +5116,7 @@ namespace lowp { static void (*just_return)(void) = nullptr; static void start_pipeline(size_t,size_t,size_t,size_t, SkRasterPipelineStage*, - SkSpan, + SkSpan, uint8_t* tailPointer) {} #else // We are compiling vector code with Clang... let's make some lowp stages! @@ -5145,7 +5165,7 @@ static constexpr U16 U16_0 = U16_(0), using Stage = void (ABI*)(Params*, SkRasterPipelineStage* program, U16 r, U16 g, U16 b, U16 a); #else using Stage = void (ABI*)(SkRasterPipelineStage* program, - size_t dx, size_t dy, + const size_t dx, const size_t dy, U16 r, U16 g, U16 b, U16 a, U16 dr, U16 dg, U16 db, U16 da); #endif @@ -5153,7 +5173,7 @@ static constexpr U16 U16_0 = U16_(0), static void start_pipeline(size_t x0, size_t y0, size_t xlimit, size_t ylimit, SkRasterPipelineStage* program, - SkSpan memoryCtxPatches, + SkSpan memoryCtxPatches, uint8_t* tailPointer) { uint8_t unreferencedTail; if (!tailPointer) { @@ -5203,12 +5223,17 @@ static void start_pipeline(size_t x0, size_t y0, // // (Some stages ignore their inputs or produce no logical output. That's perfectly fine.) // -// These three STAGE_ macros let you define each type of stage, +// These three LOWP_STAGE_ macros let you define each type of stage, // and will have (x,y) geometry and/or (r,g,b,a, dr,dg,db,da) pixel arguments as appropriate. +// +// Why does the LOWP version have 3 versions of a stage while HIGHP only has 1? +// We don't want to lose precision on the x and y coordinates, so we fuse the rg and ba +// registers before passing them in (and need to know if we have to split that super +// register or not). #if SKRP_NARROW_STAGES - #define STAGE_GG(name, ARG) \ - SI void name##_k(ARG, size_t dx, size_t dy, F& x, F& y); \ + #define LOWP_STAGE_GG(name, ARG) \ + SI void name##_k(ARG, const size_t dx, const size_t dy, F& x, F& y); \ static void ABI name(Params* params, SkRasterPipelineStage* program, \ U16 r, U16 g, U16 b, U16 a) { \ auto x = join(r,g), \ @@ -5219,27 +5244,24 @@ static void start_pipeline(size_t x0, size_t y0, auto fn = (Stage)(++program)->fn; \ fn(params, program, r,g,b,a); \ } \ - SI void name##_k(ARG, size_t dx, size_t dy, F& x, F& y) + SI void name##_k(ARG, const size_t dx, const size_t dy, F& x, F& y) - #define STAGE_GP(name, ARG) \ - SI void name##_k(ARG, size_t dx, size_t dy, F x, F y, \ - U16& r, U16& g, U16& b, U16& a, \ - U16& dr, U16& dg, U16& db, U16& da); \ + #define LOWP_STAGE_GP(name, ARG) \ + SI void name##_k(ARG, const size_t dx, const size_t dy, const F x, const F y, \ + U16& r, U16& g, U16& b, U16& a); \ static void ABI name(Params* params, SkRasterPipelineStage* program, \ U16 r, U16 g, U16 b, U16 a) { \ auto x = join(r,g), \ y = join(b,a); \ - name##_k(Ctx{program}, params->dx,params->dy, x,y, r,g,b,a, \ - params->dr,params->dg,params->db,params->da); \ + name##_k(Ctx{program}, params->dx,params->dy, x,y, r,g,b,a); \ auto fn = (Stage)(++program)->fn; \ fn(params, program, r,g,b,a); \ } \ - SI void name##_k(ARG, size_t dx, size_t dy, F x, F y, \ - U16& r, U16& g, U16& b, U16& a, \ - U16& dr, U16& dg, U16& db, U16& da) + SI void name##_k(ARG, const size_t dx, const size_t dy, const F x, const F y, \ + U16& r, U16& g, U16& b, U16& a) - #define STAGE_PP(name, ARG) \ - SI void name##_k(ARG, size_t dx, size_t dy, \ + #define LOWP_STAGE_PP(name, ARG) \ + SI void name##_k(ARG, const size_t dx, const size_t dy, \ U16& r, U16& g, U16& b, U16& a, \ U16& dr, U16& dg, U16& db, U16& da); \ static void ABI name(Params* params, SkRasterPipelineStage* program, \ @@ -5249,14 +5271,14 @@ static void start_pipeline(size_t x0, size_t y0, auto fn = (Stage)(++program)->fn; \ fn(params, program, r,g,b,a); \ } \ - SI void name##_k(ARG, size_t dx, size_t dy, \ + SI void name##_k(ARG, const size_t dx, const size_t dy, \ U16& r, U16& g, U16& b, U16& a, \ U16& dr, U16& dg, U16& db, U16& da) #else - #define STAGE_GG(name, ARG) \ - SI void name##_k(ARG, size_t dx, size_t dy, F& x, F& y); \ + #define LOWP_STAGE_GG(name, ARG) \ + SI void name##_k(ARG, const size_t dx, const size_t dy, F& x, F& y); \ static void ABI name(SkRasterPipelineStage* program, \ - size_t dx, size_t dy, \ + const size_t dx, const size_t dy, \ U16 r, U16 g, U16 b, U16 a, \ U16 dr, U16 dg, U16 db, U16 da) { \ auto x = join(r,g), \ @@ -5267,39 +5289,37 @@ static void start_pipeline(size_t x0, size_t y0, auto fn = (Stage)(++program)->fn; \ fn(program, dx,dy, r,g,b,a, dr,dg,db,da); \ } \ - SI void name##_k(ARG, size_t dx, size_t dy, F& x, F& y) + SI void name##_k(ARG, const size_t dx, const size_t dy, F& x, F& y) - #define STAGE_GP(name, ARG) \ - SI void name##_k(ARG, size_t dx, size_t dy, F x, F y, \ - U16& r, U16& g, U16& b, U16& a, \ - U16& dr, U16& dg, U16& db, U16& da); \ + #define LOWP_STAGE_GP(name, ARG) \ + SI void name##_k(ARG, const size_t dx, const size_t dy, const F x, const F y, \ + U16& r, U16& g, U16& b, U16& a); \ static void ABI name(SkRasterPipelineStage* program, \ - size_t dx, size_t dy, \ + const size_t dx, const size_t dy, \ U16 r, U16 g, U16 b, U16 a, \ U16 dr, U16 dg, U16 db, U16 da) { \ auto x = join(r,g), \ y = join(b,a); \ - name##_k(Ctx{program}, dx,dy, x,y, r,g,b,a, dr,dg,db,da); \ + name##_k(Ctx{program}, dx,dy, x,y, r,g,b,a); \ auto fn = (Stage)(++program)->fn; \ fn(program, dx,dy, r,g,b,a, dr,dg,db,da); \ } \ - SI void name##_k(ARG, size_t dx, size_t dy, F x, F y, \ - U16& r, U16& g, U16& b, U16& a, \ - U16& dr, U16& dg, U16& db, U16& da) + SI void name##_k(ARG, const size_t dx, const size_t dy, const F x, const F y, \ + U16& r, U16& g, U16& b, U16& a) - #define STAGE_PP(name, ARG) \ - SI void name##_k(ARG, size_t dx, size_t dy, \ + #define LOWP_STAGE_PP(name, ARG) \ + SI void name##_k(ARG, const size_t dx, const size_t dy, \ U16& r, U16& g, U16& b, U16& a, \ U16& dr, U16& dg, U16& db, U16& da); \ static void ABI name(SkRasterPipelineStage* program, \ - size_t dx, size_t dy, \ + const size_t dx, const size_t dy, \ U16 r, U16 g, U16 b, U16 a, \ U16 dr, U16 dg, U16 db, U16 da) { \ name##_k(Ctx{program}, dx,dy, r,g,b,a, dr,dg,db,da); \ auto fn = (Stage)(++program)->fn; \ fn(program, dx,dy, r,g,b,a, dr,dg,db,da); \ } \ - SI void name##_k(ARG, size_t dx, size_t dy, \ + SI void name##_k(ARG, const size_t dx, const size_t dy, \ U16& r, U16& g, U16& b, U16& a, \ U16& dr, U16& dg, U16& db, U16& da) #endif @@ -5583,7 +5603,7 @@ SI F abs_(F x) { return sk_bit_cast( sk_bit_cast(x) & 0x7fffffff ); } // ~~~~~~ Basic / misc. stages ~~~~~~ // -STAGE_GG(seed_shader, NoCtx) { +LOWP_STAGE_GG(seed_shader, NoCtx) { #if defined(SKRP_CPU_LSX) __m128 val1 = {0.5f, 1.5f, 2.5f, 3.5f}; __m128 val2 = {4.5f, 5.5f, 6.5f, 7.5f}; @@ -5605,28 +5625,28 @@ STAGE_GG(seed_shader, NoCtx) { 0.5f, 1.5f, 2.5f, 3.5f, 4.5f, 5.5f, 6.5f, 7.5f, 8.5f, 9.5f,10.5f,11.5f,12.5f,13.5f,14.5f,15.5f, }; - static_assert(std::size(iota) >= SkRasterPipeline_kMaxStride); + static_assert(std::size(iota) >= SkRasterPipelineContexts::kMaxStride); x = cast(I32_(dx)) + sk_unaligned_load(iota); y = cast(I32_(dy)) + 0.5f; #endif } -STAGE_GG(matrix_translate, const float* m) { +LOWP_STAGE_GG(matrix_translate, const float* m) { x += m[0]; y += m[1]; } -STAGE_GG(matrix_scale_translate, const float* m) { +LOWP_STAGE_GG(matrix_scale_translate, const float* m) { x = mad(x,m[0], m[2]); y = mad(y,m[1], m[3]); } -STAGE_GG(matrix_2x3, const float* m) { +LOWP_STAGE_GG(matrix_2x3, const float* m) { auto X = mad(x,m[0], mad(y,m[1], m[2])), Y = mad(x,m[3], mad(y,m[4], m[5])); x = X; y = Y; } -STAGE_GG(matrix_perspective, const float* m) { +LOWP_STAGE_GG(matrix_perspective, const float* m) { // N.B. Unlike the other matrix_ stages, this matrix is row-major. auto X = mad(x,m[0], mad(y,m[1], m[2])), Y = mad(x,m[3], mad(y,m[4], m[5])), @@ -5635,86 +5655,86 @@ STAGE_GG(matrix_perspective, const float* m) { y = Y * rcp_precise(Z); } -STAGE_PP(uniform_color, const SkRasterPipeline_UniformColorCtx* c) { +LOWP_STAGE_PP(uniform_color, const SkRasterPipelineContexts::UniformColorCtx* c) { r = U16_(c->rgba[0]); g = U16_(c->rgba[1]); b = U16_(c->rgba[2]); a = U16_(c->rgba[3]); } -STAGE_PP(uniform_color_dst, const SkRasterPipeline_UniformColorCtx* c) { +LOWP_STAGE_PP(uniform_color_dst, const SkRasterPipelineContexts::UniformColorCtx* c) { dr = U16_(c->rgba[0]); dg = U16_(c->rgba[1]); db = U16_(c->rgba[2]); da = U16_(c->rgba[3]); } -STAGE_PP(black_color, NoCtx) { r = g = b = U16_0; a = U16_255; } -STAGE_PP(white_color, NoCtx) { r = g = b = U16_255; a = U16_255; } +LOWP_STAGE_PP(black_color, NoCtx) { r = g = b = U16_0; a = U16_255; } +LOWP_STAGE_PP(white_color, NoCtx) { r = g = b = U16_255; a = U16_255; } -STAGE_PP(set_rgb, const float rgb[3]) { +LOWP_STAGE_PP(set_rgb, const float rgb[3]) { r = from_float(rgb[0]); g = from_float(rgb[1]); b = from_float(rgb[2]); } // No need to clamp against 0 here (values are unsigned) -STAGE_PP(clamp_01, NoCtx) { +LOWP_STAGE_PP(clamp_01, NoCtx) { r = min(r, 255); g = min(g, 255); b = min(b, 255); a = min(a, 255); } -STAGE_PP(clamp_a_01, NoCtx) { +LOWP_STAGE_PP(clamp_a_01, NoCtx) { a = min(a, 255); } -STAGE_PP(clamp_gamut, NoCtx) { +LOWP_STAGE_PP(clamp_gamut, NoCtx) { a = min(a, 255); r = min(r, a); g = min(g, a); b = min(b, a); } -STAGE_PP(premul, NoCtx) { +LOWP_STAGE_PP(premul, NoCtx) { r = div255_accurate(r * a); g = div255_accurate(g * a); b = div255_accurate(b * a); } -STAGE_PP(premul_dst, NoCtx) { +LOWP_STAGE_PP(premul_dst, NoCtx) { dr = div255_accurate(dr * da); dg = div255_accurate(dg * da); db = div255_accurate(db * da); } -STAGE_PP(force_opaque , NoCtx) { a = U16_255; } -STAGE_PP(force_opaque_dst, NoCtx) { da = U16_255; } +LOWP_STAGE_PP(force_opaque , NoCtx) { a = U16_255; } +LOWP_STAGE_PP(force_opaque_dst, NoCtx) { da = U16_255; } -STAGE_PP(swap_rb, NoCtx) { +LOWP_STAGE_PP(swap_rb, NoCtx) { auto tmp = r; r = b; b = tmp; } -STAGE_PP(swap_rb_dst, NoCtx) { +LOWP_STAGE_PP(swap_rb_dst, NoCtx) { auto tmp = dr; dr = db; db = tmp; } -STAGE_PP(move_src_dst, NoCtx) { +LOWP_STAGE_PP(move_src_dst, NoCtx) { dr = r; dg = g; db = b; da = a; } -STAGE_PP(move_dst_src, NoCtx) { +LOWP_STAGE_PP(move_dst_src, NoCtx) { r = dr; g = dg; b = db; a = da; } -STAGE_PP(swap_src_dst, NoCtx) { +LOWP_STAGE_PP(swap_src_dst, NoCtx) { std::swap(r, dr); std::swap(g, dg); std::swap(b, db); @@ -5726,7 +5746,7 @@ STAGE_PP(swap_src_dst, NoCtx) { // The same logic applied to all 4 channels. #define BLEND_MODE(name) \ SI U16 name##_channel(U16 s, U16 d, U16 sa, U16 da); \ - STAGE_PP(name, NoCtx) { \ + LOWP_STAGE_PP(name, NoCtx) { \ r = name##_channel(r,dr,a,da); \ g = name##_channel(g,dg,a,da); \ b = name##_channel(b,db,a,da); \ @@ -5770,7 +5790,7 @@ STAGE_PP(swap_src_dst, NoCtx) { // The same logic applied to color, and srcover for alpha. #define BLEND_MODE(name) \ SI U16 name##_channel(U16 s, U16 d, U16 sa, U16 da); \ - STAGE_PP(name, NoCtx) { \ + LOWP_STAGE_PP(name, NoCtx) { \ r = name##_channel(r,dr,a,da); \ g = name##_channel(g,dg,a,da); \ b = name##_channel(b,db,a,da); \ @@ -5796,12 +5816,12 @@ STAGE_PP(swap_src_dst, NoCtx) { // ~~~~~~ Helpers for interacting with memory ~~~~~~ // template -SI T* ptr_at_xy(const SkRasterPipeline_MemoryCtx* ctx, size_t dx, size_t dy) { +SI T* ptr_at_xy(const SkRasterPipelineContexts::MemoryCtx* ctx, const size_t dx, const size_t dy) { return (T*)ctx->pixels + dy*ctx->stride + dx; } template -SI U32 ix_and_ptr(T** ptr, const SkRasterPipeline_GatherCtx* ctx, F x, F y) { +SI U32 ix_and_ptr(T** ptr, const SkRasterPipelineContexts::GatherCtx* ctx, F x, F y) { // Exclusive -> inclusive. const F w = F_(sk_bit_cast( sk_bit_cast(ctx->width ) - 1)), h = F_(sk_bit_cast( sk_bit_cast(ctx->height) - 1)); @@ -5819,7 +5839,7 @@ SI U32 ix_and_ptr(T** ptr, const SkRasterPipeline_GatherCtx* ctx, F x, F y) { } template -SI U32 ix_and_ptr(T** ptr, const SkRasterPipeline_GatherCtx* ctx, I32 x, I32 y) { +SI U32 ix_and_ptr(T** ptr, const SkRasterPipelineContexts::GatherCtx* ctx, I32 x, I32 y) { // This flag doesn't make sense when the coords are integers. SkASSERT(ctx->roundDownAtInteger == 0); // Exclusive -> inclusive. @@ -5968,7 +5988,7 @@ SI void from_8888(U32 rgba, U16* r, U16* g, U16* b, U16* a) { } SI void load_8888_(const uint32_t* ptr, U16* r, U16* g, U16* b, U16* a) { -#if 1 && defined(SKRP_CPU_NEON) +#if defined(SKRP_CPU_NEON) uint8x8x4_t rgba = vld4_u8((const uint8_t*)(ptr)); *r = cast(rgba.val[0]); *g = cast(rgba.val[1]); @@ -6008,7 +6028,7 @@ SI void store_8888_(uint32_t* ptr, U16 r, U16 g, U16 b, U16 a) { b = min(b, 255); a = min(a, 255); -#if 1 && defined(SKRP_CPU_NEON) +#if defined(SKRP_CPU_NEON) uint8x8x4_t rgba = {{ cast(r), cast(g), @@ -6023,16 +6043,16 @@ SI void store_8888_(uint32_t* ptr, U16 r, U16 g, U16 b, U16 a) { #endif } -STAGE_PP(load_8888, const SkRasterPipeline_MemoryCtx* ctx) { +LOWP_STAGE_PP(load_8888, const SkRasterPipelineContexts::MemoryCtx* ctx) { load_8888_(ptr_at_xy(ctx, dx,dy), &r,&g,&b,&a); } -STAGE_PP(load_8888_dst, const SkRasterPipeline_MemoryCtx* ctx) { +LOWP_STAGE_PP(load_8888_dst, const SkRasterPipelineContexts::MemoryCtx* ctx) { load_8888_(ptr_at_xy(ctx, dx,dy), &dr,&dg,&db,&da); } -STAGE_PP(store_8888, const SkRasterPipeline_MemoryCtx* ctx) { +LOWP_STAGE_PP(store_8888, const SkRasterPipelineContexts::MemoryCtx* ctx) { store_8888_(ptr_at_xy(ctx, dx,dy), r,g,b,a); } -STAGE_GP(gather_8888, const SkRasterPipeline_GatherCtx* ctx) { +LOWP_STAGE_GP(gather_8888, const SkRasterPipelineContexts::GatherCtx* ctx) { const uint32_t* ptr; U32 ix = ix_and_ptr(&ptr, ctx, x,y); from_8888(gather(ptr, ix), &r, &g, &b, &a); @@ -6071,18 +6091,18 @@ SI void store_565_(uint16_t* ptr, U16 r, U16 g, U16 b) { | B << 0); } -STAGE_PP(load_565, const SkRasterPipeline_MemoryCtx* ctx) { +LOWP_STAGE_PP(load_565, const SkRasterPipelineContexts::MemoryCtx* ctx) { load_565_(ptr_at_xy(ctx, dx,dy), &r,&g,&b); a = U16_255; } -STAGE_PP(load_565_dst, const SkRasterPipeline_MemoryCtx* ctx) { +LOWP_STAGE_PP(load_565_dst, const SkRasterPipelineContexts::MemoryCtx* ctx) { load_565_(ptr_at_xy(ctx, dx,dy), &dr,&dg,&db); da = U16_255; } -STAGE_PP(store_565, const SkRasterPipeline_MemoryCtx* ctx) { +LOWP_STAGE_PP(store_565, const SkRasterPipelineContexts::MemoryCtx* ctx) { store_565_(ptr_at_xy(ctx, dx,dy), r,g,b); } -STAGE_GP(gather_565, const SkRasterPipeline_GatherCtx* ctx) { +LOWP_STAGE_GP(gather_565, const SkRasterPipelineContexts::GatherCtx* ctx) { const uint16_t* ptr; U32 ix = ix_and_ptr(&ptr, ctx, x,y); from_565(gather(ptr, ix), &r, &g, &b); @@ -6123,16 +6143,16 @@ SI void store_4444_(uint16_t* ptr, U16 r, U16 g, U16 b, U16 a) { | A << 0); } -STAGE_PP(load_4444, const SkRasterPipeline_MemoryCtx* ctx) { +LOWP_STAGE_PP(load_4444, const SkRasterPipelineContexts::MemoryCtx* ctx) { load_4444_(ptr_at_xy(ctx, dx,dy), &r,&g,&b,&a); } -STAGE_PP(load_4444_dst, const SkRasterPipeline_MemoryCtx* ctx) { +LOWP_STAGE_PP(load_4444_dst, const SkRasterPipelineContexts::MemoryCtx* ctx) { load_4444_(ptr_at_xy(ctx, dx,dy), &dr,&dg,&db,&da); } -STAGE_PP(store_4444, const SkRasterPipeline_MemoryCtx* ctx) { +LOWP_STAGE_PP(store_4444, const SkRasterPipelineContexts::MemoryCtx* ctx) { store_4444_(ptr_at_xy(ctx, dx,dy), r,g,b,a); } -STAGE_GP(gather_4444, const SkRasterPipeline_GatherCtx* ctx) { +LOWP_STAGE_GP(gather_4444, const SkRasterPipelineContexts::GatherCtx* ctx) { const uint16_t* ptr; U32 ix = ix_and_ptr(&ptr, ctx, x,y); from_4444(gather(ptr, ix), &r,&g,&b,&a); @@ -6144,7 +6164,7 @@ SI void from_88(U16 rg, U16* r, U16* g) { } SI void load_88_(const uint16_t* ptr, U16* r, U16* g) { -#if 1 && defined(SKRP_CPU_NEON) +#if defined(SKRP_CPU_NEON) uint8x8x2_t rg = vld2_u8((const uint8_t*)(ptr)); *r = cast(rg.val[0]); *g = cast(rg.val[1]); @@ -6157,7 +6177,7 @@ SI void store_88_(uint16_t* ptr, U16 r, U16 g) { r = min(r, 255); g = min(g, 255); -#if 1 && defined(SKRP_CPU_NEON) +#if defined(SKRP_CPU_NEON) uint8x8x2_t rg = {{ cast(r), cast(g), @@ -6168,20 +6188,20 @@ SI void store_88_(uint16_t* ptr, U16 r, U16 g) { #endif } -STAGE_PP(load_rg88, const SkRasterPipeline_MemoryCtx* ctx) { +LOWP_STAGE_PP(load_rg88, const SkRasterPipelineContexts::MemoryCtx* ctx) { load_88_(ptr_at_xy(ctx, dx, dy), &r, &g); b = U16_0; a = U16_255; } -STAGE_PP(load_rg88_dst, const SkRasterPipeline_MemoryCtx* ctx) { +LOWP_STAGE_PP(load_rg88_dst, const SkRasterPipelineContexts::MemoryCtx* ctx) { load_88_(ptr_at_xy(ctx, dx, dy), &dr, &dg); db = U16_0; da = U16_255; } -STAGE_PP(store_rg88, const SkRasterPipeline_MemoryCtx* ctx) { +LOWP_STAGE_PP(store_rg88, const SkRasterPipelineContexts::MemoryCtx* ctx) { store_88_(ptr_at_xy(ctx, dx, dy), r, g); } -STAGE_GP(gather_rg88, const SkRasterPipeline_GatherCtx* ctx) { +LOWP_STAGE_GP(gather_rg88, const SkRasterPipelineContexts::GatherCtx* ctx) { const uint16_t* ptr; U32 ix = ix_and_ptr(&ptr, ctx, x, y); from_88(gather(ptr, ix), &r, &g); @@ -6199,76 +6219,76 @@ SI void store_8(uint8_t* ptr, U16 v) { store(ptr, cast(v)); } -STAGE_PP(load_a8, const SkRasterPipeline_MemoryCtx* ctx) { +LOWP_STAGE_PP(load_a8, const SkRasterPipelineContexts::MemoryCtx* ctx) { r = g = b = U16_0; a = load_8(ptr_at_xy(ctx, dx,dy)); } -STAGE_PP(load_a8_dst, const SkRasterPipeline_MemoryCtx* ctx) { +LOWP_STAGE_PP(load_a8_dst, const SkRasterPipelineContexts::MemoryCtx* ctx) { dr = dg = db = U16_0; da = load_8(ptr_at_xy(ctx, dx,dy)); } -STAGE_PP(store_a8, const SkRasterPipeline_MemoryCtx* ctx) { +LOWP_STAGE_PP(store_a8, const SkRasterPipelineContexts::MemoryCtx* ctx) { store_8(ptr_at_xy(ctx, dx,dy), a); } -STAGE_GP(gather_a8, const SkRasterPipeline_GatherCtx* ctx) { +LOWP_STAGE_GP(gather_a8, const SkRasterPipelineContexts::GatherCtx* ctx) { const uint8_t* ptr; U32 ix = ix_and_ptr(&ptr, ctx, x,y); r = g = b = U16_0; a = cast(gather(ptr, ix)); } -STAGE_PP(store_r8, const SkRasterPipeline_MemoryCtx* ctx) { +LOWP_STAGE_PP(store_r8, const SkRasterPipelineContexts::MemoryCtx* ctx) { store_8(ptr_at_xy(ctx, dx,dy), r); } -STAGE_PP(alpha_to_gray, NoCtx) { +LOWP_STAGE_PP(alpha_to_gray, NoCtx) { r = g = b = a; a = U16_255; } -STAGE_PP(alpha_to_gray_dst, NoCtx) { +LOWP_STAGE_PP(alpha_to_gray_dst, NoCtx) { dr = dg = db = da; da = U16_255; } -STAGE_PP(alpha_to_red, NoCtx) { +LOWP_STAGE_PP(alpha_to_red, NoCtx) { r = a; a = U16_255; } -STAGE_PP(alpha_to_red_dst, NoCtx) { +LOWP_STAGE_PP(alpha_to_red_dst, NoCtx) { dr = da; da = U16_255; } -STAGE_PP(bt709_luminance_or_luma_to_alpha, NoCtx) { +LOWP_STAGE_PP(bt709_luminance_or_luma_to_alpha, NoCtx) { a = (r*54 + g*183 + b*19)/256; // 0.2126, 0.7152, 0.0722 with 256 denominator. r = g = b = U16_0; } -STAGE_PP(bt709_luminance_or_luma_to_rgb, NoCtx) { +LOWP_STAGE_PP(bt709_luminance_or_luma_to_rgb, NoCtx) { r = g = b =(r*54 + g*183 + b*19)/256; // 0.2126, 0.7152, 0.0722 with 256 denominator. } // ~~~~~~ Coverage scales / lerps ~~~~~~ // -STAGE_PP(load_src, const uint16_t* ptr) { +LOWP_STAGE_PP(load_src, const uint16_t* ptr) { r = sk_unaligned_load(ptr + 0*N); g = sk_unaligned_load(ptr + 1*N); b = sk_unaligned_load(ptr + 2*N); a = sk_unaligned_load(ptr + 3*N); } -STAGE_PP(store_src, uint16_t* ptr) { +LOWP_STAGE_PP(store_src, uint16_t* ptr) { sk_unaligned_store(ptr + 0*N, r); sk_unaligned_store(ptr + 1*N, g); sk_unaligned_store(ptr + 2*N, b); sk_unaligned_store(ptr + 3*N, a); } -STAGE_PP(store_src_a, uint16_t* ptr) { +LOWP_STAGE_PP(store_src_a, uint16_t* ptr) { sk_unaligned_store(ptr, a); } -STAGE_PP(load_dst, const uint16_t* ptr) { +LOWP_STAGE_PP(load_dst, const uint16_t* ptr) { dr = sk_unaligned_load(ptr + 0*N); dg = sk_unaligned_load(ptr + 1*N); db = sk_unaligned_load(ptr + 2*N); da = sk_unaligned_load(ptr + 3*N); } -STAGE_PP(store_dst, uint16_t* ptr) { +LOWP_STAGE_PP(store_dst, uint16_t* ptr) { sk_unaligned_store(ptr + 0*N, dr); sk_unaligned_store(ptr + 1*N, dg); sk_unaligned_store(ptr + 2*N, db); @@ -6277,21 +6297,21 @@ STAGE_PP(store_dst, uint16_t* ptr) { // ~~~~~~ Coverage scales / lerps ~~~~~~ // -STAGE_PP(scale_1_float, const float* f) { +LOWP_STAGE_PP(scale_1_float, const float* f) { U16 c = from_float(*f); r = div255( r * c ); g = div255( g * c ); b = div255( b * c ); a = div255( a * c ); } -STAGE_PP(lerp_1_float, const float* f) { +LOWP_STAGE_PP(lerp_1_float, const float* f) { U16 c = from_float(*f); r = lerp(dr, r, c); g = lerp(dg, g, c); b = lerp(db, b, c); a = lerp(da, a, c); } -STAGE_PP(scale_native, const uint16_t scales[]) { +LOWP_STAGE_PP(scale_native, const uint16_t scales[]) { auto c = sk_unaligned_load(scales); r = div255( r * c ); g = div255( g * c ); @@ -6299,7 +6319,7 @@ STAGE_PP(scale_native, const uint16_t scales[]) { a = div255( a * c ); } -STAGE_PP(lerp_native, const uint16_t scales[]) { +LOWP_STAGE_PP(lerp_native, const uint16_t scales[]) { auto c = sk_unaligned_load(scales); r = lerp(dr, r, c); g = lerp(dg, g, c); @@ -6307,14 +6327,14 @@ STAGE_PP(lerp_native, const uint16_t scales[]) { a = lerp(da, a, c); } -STAGE_PP(scale_u8, const SkRasterPipeline_MemoryCtx* ctx) { +LOWP_STAGE_PP(scale_u8, const SkRasterPipelineContexts::MemoryCtx* ctx) { U16 c = load_8(ptr_at_xy(ctx, dx,dy)); r = div255( r * c ); g = div255( g * c ); b = div255( b * c ); a = div255( a * c ); } -STAGE_PP(lerp_u8, const SkRasterPipeline_MemoryCtx* ctx) { +LOWP_STAGE_PP(lerp_u8, const SkRasterPipelineContexts::MemoryCtx* ctx) { U16 c = load_8(ptr_at_xy(ctx, dx,dy)); r = lerp(dr, r, c); g = lerp(dg, g, c); @@ -6327,7 +6347,7 @@ SI U16 alpha_coverage_from_rgb_coverage(U16 a, U16 da, U16 cr, U16 cg, U16 cb) { return if_then_else(a < da, min(cr, min(cg,cb)) , max(cr, max(cg,cb))); } -STAGE_PP(scale_565, const SkRasterPipeline_MemoryCtx* ctx) { +LOWP_STAGE_PP(scale_565, const SkRasterPipelineContexts::MemoryCtx* ctx) { U16 cr,cg,cb; load_565_(ptr_at_xy(ctx, dx,dy), &cr,&cg,&cb); U16 ca = alpha_coverage_from_rgb_coverage(a,da, cr,cg,cb); @@ -6337,7 +6357,7 @@ STAGE_PP(scale_565, const SkRasterPipeline_MemoryCtx* ctx) { b = div255( b * cb ); a = div255( a * ca ); } -STAGE_PP(lerp_565, const SkRasterPipeline_MemoryCtx* ctx) { +LOWP_STAGE_PP(lerp_565, const SkRasterPipelineContexts::MemoryCtx* ctx) { U16 cr,cg,cb; load_565_(ptr_at_xy(ctx, dx,dy), &cr,&cg,&cb); U16 ca = alpha_coverage_from_rgb_coverage(a,da, cr,cg,cb); @@ -6348,7 +6368,7 @@ STAGE_PP(lerp_565, const SkRasterPipeline_MemoryCtx* ctx) { a = lerp(da, a, ca); } -STAGE_PP(emboss, const SkRasterPipeline_EmbossCtx* ctx) { +LOWP_STAGE_PP(emboss, const SkRasterPipelineContexts::EmbossCtx* ctx) { U16 mul = load_8(ptr_at_xy(&ctx->mul, dx,dy)), add = load_8(ptr_at_xy(&ctx->add, dx,dy)); @@ -6357,40 +6377,39 @@ STAGE_PP(emboss, const SkRasterPipeline_EmbossCtx* ctx) { b = min(div255(b*mul) + add, a); } - // ~~~~~~ Gradient stages ~~~~~~ // // Clamp x to [0,1], both sides inclusive (think, gradients). // Even repeat and mirror funnel through a clamp to handle bad inputs like +Inf, NaN. SI F clamp_01_(F v) { return min(max(0, v), 1); } -STAGE_GG(clamp_x_1 , NoCtx) { x = clamp_01_(x); } -STAGE_GG(repeat_x_1, NoCtx) { x = clamp_01_(x - floor_(x)); } -STAGE_GG(mirror_x_1, NoCtx) { +LOWP_STAGE_GG(clamp_x_1 , NoCtx) { x = clamp_01_(x); } +LOWP_STAGE_GG(repeat_x_1, NoCtx) { x = clamp_01_(x - floor_(x)); } +LOWP_STAGE_GG(mirror_x_1, NoCtx) { auto two = [](F x){ return x+x; }; x = clamp_01_(abs_( (x-1.0f) - two(floor_((x-1.0f)*0.5f)) - 1.0f )); } SI I16 cond_to_mask_16(I32 cond) { return cast(cond); } -STAGE_GG(decal_x, SkRasterPipeline_DecalTileCtx* ctx) { +LOWP_STAGE_GG(decal_x, SkRasterPipelineContexts::DecalTileCtx* ctx) { auto w = ctx->limit_x; sk_unaligned_store(ctx->mask, cond_to_mask_16((0 <= x) & (x < w))); } -STAGE_GG(decal_y, SkRasterPipeline_DecalTileCtx* ctx) { +LOWP_STAGE_GG(decal_y, SkRasterPipelineContexts::DecalTileCtx* ctx) { auto h = ctx->limit_y; sk_unaligned_store(ctx->mask, cond_to_mask_16((0 <= y) & (y < h))); } -STAGE_GG(decal_x_and_y, SkRasterPipeline_DecalTileCtx* ctx) { +LOWP_STAGE_GG(decal_x_and_y, SkRasterPipelineContexts::DecalTileCtx* ctx) { auto w = ctx->limit_x; auto h = ctx->limit_y; sk_unaligned_store(ctx->mask, cond_to_mask_16((0 <= x) & (x < w) & (0 <= y) & (y < h))); } -STAGE_GG(clamp_x_and_y, SkRasterPipeline_CoordClampCtx* ctx) { +LOWP_STAGE_GG(clamp_x_and_y, SkRasterPipelineContexts::CoordClampCtx* ctx) { x = min(ctx->max_x, max(ctx->min_x, x)); y = min(ctx->max_y, max(ctx->min_y, y)); } -STAGE_PP(check_decal_mask, SkRasterPipeline_DecalTileCtx* ctx) { +LOWP_STAGE_PP(check_decal_mask, SkRasterPipelineContexts::DecalTileCtx* ctx) { auto mask = sk_unaligned_load(ctx->mask); r = r & mask; g = g & mask; @@ -6407,86 +6426,90 @@ SI void round_F_to_U16(F R, F G, F B, F A, U16* r, U16* g, U16* b, U16* a) { *a = round_color(A); // we assume alpha is already in [0,1]. } -SI void gradient_lookup(const SkRasterPipeline_GradientCtx* c, U32 idx, F t, - U16* r, U16* g, U16* b, U16* a) { - +SI void gradient_lookup(const SkRasterPipelineContexts::GradientCtx* c, + U32 idx, + F t, + U16* r, + U16* g, + U16* b, + U16* a) { F fr, fg, fb, fa, br, bg, bb, ba; #if defined(SKRP_CPU_HSW) if (c->stopCount <=8) { __m256i lo, hi; split(idx, &lo, &hi); - fr = join(_mm256_permutevar8x32_ps(_mm256_loadu_ps(c->fs[0]), lo), - _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->fs[0]), hi)); - br = join(_mm256_permutevar8x32_ps(_mm256_loadu_ps(c->bs[0]), lo), - _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->bs[0]), hi)); - fg = join(_mm256_permutevar8x32_ps(_mm256_loadu_ps(c->fs[1]), lo), - _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->fs[1]), hi)); - bg = join(_mm256_permutevar8x32_ps(_mm256_loadu_ps(c->bs[1]), lo), - _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->bs[1]), hi)); - fb = join(_mm256_permutevar8x32_ps(_mm256_loadu_ps(c->fs[2]), lo), - _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->fs[2]), hi)); - bb = join(_mm256_permutevar8x32_ps(_mm256_loadu_ps(c->bs[2]), lo), - _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->bs[2]), hi)); - fa = join(_mm256_permutevar8x32_ps(_mm256_loadu_ps(c->fs[3]), lo), - _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->fs[3]), hi)); - ba = join(_mm256_permutevar8x32_ps(_mm256_loadu_ps(c->bs[3]), lo), - _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->bs[3]), hi)); + fr = join(_mm256_permutevar8x32_ps(_mm256_loadu_ps(c->factors[0]), lo), + _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->factors[0]), hi)); + br = join(_mm256_permutevar8x32_ps(_mm256_loadu_ps(c->biases[0]), lo), + _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->biases[0]), hi)); + fg = join(_mm256_permutevar8x32_ps(_mm256_loadu_ps(c->factors[1]), lo), + _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->factors[1]), hi)); + bg = join(_mm256_permutevar8x32_ps(_mm256_loadu_ps(c->biases[1]), lo), + _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->biases[1]), hi)); + fb = join(_mm256_permutevar8x32_ps(_mm256_loadu_ps(c->factors[2]), lo), + _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->factors[2]), hi)); + bb = join(_mm256_permutevar8x32_ps(_mm256_loadu_ps(c->biases[2]), lo), + _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->biases[2]), hi)); + fa = join(_mm256_permutevar8x32_ps(_mm256_loadu_ps(c->factors[3]), lo), + _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->factors[3]), hi)); + ba = join(_mm256_permutevar8x32_ps(_mm256_loadu_ps(c->biases[3]), lo), + _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->biases[3]), hi)); } else #elif defined(SKRP_CPU_LASX) if (c->stopCount <= 8) { __m256i lo, hi; split(idx, &lo, &hi); - fr = join((__m256)__lasx_xvperm_w(__lasx_xvld(c->fs[0], 0), lo), - (__m256)__lasx_xvperm_w(__lasx_xvld(c->fs[0], 0), hi)); - br = join((__m256)__lasx_xvperm_w(__lasx_xvld(c->bs[0], 0), lo), - (__m256)__lasx_xvperm_w(__lasx_xvld(c->bs[0], 0), hi)); - fg = join((__m256)__lasx_xvperm_w(__lasx_xvld(c->fs[1], 0), lo), - (__m256)__lasx_xvperm_w(__lasx_xvld(c->fs[1], 0), hi)); - bg = join((__m256)__lasx_xvperm_w(__lasx_xvld(c->bs[1], 0), lo), - (__m256)__lasx_xvperm_w(__lasx_xvld(c->bs[1], 0), hi)); - fb = join((__m256)__lasx_xvperm_w(__lasx_xvld(c->fs[2], 0), lo), - (__m256)__lasx_xvperm_w(__lasx_xvld(c->fs[2], 0), hi)); - bb = join((__m256)__lasx_xvperm_w(__lasx_xvld(c->bs[2], 0), lo), - (__m256)__lasx_xvperm_w(__lasx_xvld(c->bs[2], 0), hi)); - fa = join((__m256)__lasx_xvperm_w(__lasx_xvld(c->fs[3], 0), lo), - (__m256)__lasx_xvperm_w(__lasx_xvld(c->fs[3], 0), hi)); - ba = join((__m256)__lasx_xvperm_w(__lasx_xvld(c->bs[3], 0), lo), - (__m256)__lasx_xvperm_w(__lasx_xvld(c->bs[3], 0), hi)); + fr = join((__m256)__lasx_xvperm_w(__lasx_xvld(c->factors[0], 0), lo), + (__m256)__lasx_xvperm_w(__lasx_xvld(c->factors[0], 0), hi)); + br = join((__m256)__lasx_xvperm_w(__lasx_xvld(c->biases[0], 0), lo), + (__m256)__lasx_xvperm_w(__lasx_xvld(c->biases[0], 0), hi)); + fg = join((__m256)__lasx_xvperm_w(__lasx_xvld(c->factors[1], 0), lo), + (__m256)__lasx_xvperm_w(__lasx_xvld(c->factors[1], 0), hi)); + bg = join((__m256)__lasx_xvperm_w(__lasx_xvld(c->biases[1], 0), lo), + (__m256)__lasx_xvperm_w(__lasx_xvld(c->biases[1], 0), hi)); + fb = join((__m256)__lasx_xvperm_w(__lasx_xvld(c->factors[2], 0), lo), + (__m256)__lasx_xvperm_w(__lasx_xvld(c->factors[2], 0), hi)); + bb = join((__m256)__lasx_xvperm_w(__lasx_xvld(c->biases[2], 0), lo), + (__m256)__lasx_xvperm_w(__lasx_xvld(c->biases[2], 0), hi)); + fa = join((__m256)__lasx_xvperm_w(__lasx_xvld(c->factors[3], 0), lo), + (__m256)__lasx_xvperm_w(__lasx_xvld(c->factors[3], 0), hi)); + ba = join((__m256)__lasx_xvperm_w(__lasx_xvld(c->biases[3], 0), lo), + (__m256)__lasx_xvperm_w(__lasx_xvld(c->biases[3], 0), hi)); } else #elif defined(SKRP_CPU_LSX) if (c->stopCount <= 4) { __m128i lo, hi; split(idx, &lo, &hi); __m128i zero = __lsx_vldi(0); - fr = join((__m128)__lsx_vshuf_w(lo, zero, __lsx_vld(c->fs[0], 0)), - (__m128)__lsx_vshuf_w(hi, zero, __lsx_vld(c->fs[0], 0))); - br = join((__m128)__lsx_vshuf_w(lo, zero, __lsx_vld(c->bs[0], 0)), - (__m128)__lsx_vshuf_w(hi, zero, __lsx_vld(c->bs[0], 0))); - fg = join((__m128)__lsx_vshuf_w(lo, zero, __lsx_vld(c->fs[1], 0)), - (__m128)__lsx_vshuf_w(hi, zero, __lsx_vld(c->fs[1], 0))); - bg = join((__m128)__lsx_vshuf_w(lo, zero, __lsx_vld(c->bs[1], 0)), - (__m128)__lsx_vshuf_w(hi, zero, __lsx_vld(c->bs[1], 0))); - fb = join((__m128)__lsx_vshuf_w(lo, zero, __lsx_vld(c->fs[2], 0)), - (__m128)__lsx_vshuf_w(hi, zero, __lsx_vld(c->fs[2], 0))); - bb = join((__m128)__lsx_vshuf_w(lo, zero, __lsx_vld(c->bs[2], 0)), - (__m128)__lsx_vshuf_w(hi, zero, __lsx_vld(c->bs[2], 0))); - fa = join((__m128)__lsx_vshuf_w(lo, zero, __lsx_vld(c->fs[3], 0)), - (__m128)__lsx_vshuf_w(hi, zero, __lsx_vld(c->fs[3], 0))); - ba = join((__m128)__lsx_vshuf_w(lo, zero, __lsx_vld(c->bs[3], 0)), - (__m128)__lsx_vshuf_w(hi, zero, __lsx_vld(c->bs[3], 0))); + fr = join((__m128)__lsx_vshuf_w(lo, zero, __lsx_vld(c->factors[0], 0)), + (__m128)__lsx_vshuf_w(hi, zero, __lsx_vld(c->factors[0], 0))); + br = join((__m128)__lsx_vshuf_w(lo, zero, __lsx_vld(c->biases[0], 0)), + (__m128)__lsx_vshuf_w(hi, zero, __lsx_vld(c->biases[0], 0))); + fg = join((__m128)__lsx_vshuf_w(lo, zero, __lsx_vld(c->factors[1], 0)), + (__m128)__lsx_vshuf_w(hi, zero, __lsx_vld(c->factors[1], 0))); + bg = join((__m128)__lsx_vshuf_w(lo, zero, __lsx_vld(c->biases[1], 0)), + (__m128)__lsx_vshuf_w(hi, zero, __lsx_vld(c->biases[1], 0))); + fb = join((__m128)__lsx_vshuf_w(lo, zero, __lsx_vld(c->factors[2], 0)), + (__m128)__lsx_vshuf_w(hi, zero, __lsx_vld(c->factors[2], 0))); + bb = join((__m128)__lsx_vshuf_w(lo, zero, __lsx_vld(c->biases[2], 0)), + (__m128)__lsx_vshuf_w(hi, zero, __lsx_vld(c->biases[2], 0))); + fa = join((__m128)__lsx_vshuf_w(lo, zero, __lsx_vld(c->factors[3], 0)), + (__m128)__lsx_vshuf_w(hi, zero, __lsx_vld(c->factors[3], 0))); + ba = join((__m128)__lsx_vshuf_w(lo, zero, __lsx_vld(c->biases[3], 0)), + (__m128)__lsx_vshuf_w(hi, zero, __lsx_vld(c->biases[3], 0))); } else #endif { - fr = gather(c->fs[0], idx); - fg = gather(c->fs[1], idx); - fb = gather(c->fs[2], idx); - fa = gather(c->fs[3], idx); - br = gather(c->bs[0], idx); - bg = gather(c->bs[1], idx); - bb = gather(c->bs[2], idx); - ba = gather(c->bs[3], idx); + fr = gather(c->factors[0], idx); + fg = gather(c->factors[1], idx); + fb = gather(c->factors[2], idx); + fa = gather(c->factors[3], idx); + br = gather(c->biases[0], idx); + bg = gather(c->biases[1], idx); + bb = gather(c->biases[2], idx); + ba = gather(c->biases[3], idx); } round_F_to_U16(mad(t, fr, br), mad(t, fg, bg), @@ -6495,7 +6518,7 @@ SI void gradient_lookup(const SkRasterPipeline_GradientCtx* c, U32 idx, F t, r,g,b,a); } -STAGE_GP(gradient, const SkRasterPipeline_GradientCtx* c) { +LOWP_STAGE_GP(gradient, const SkRasterPipelineContexts::GradientCtx* c) { auto t = x; U32 idx = U32_(0); @@ -6507,22 +6530,23 @@ STAGE_GP(gradient, const SkRasterPipeline_GradientCtx* c) { gradient_lookup(c, idx, t, &r, &g, &b, &a); } -STAGE_GP(evenly_spaced_gradient, const SkRasterPipeline_GradientCtx* c) { +LOWP_STAGE_GP(evenly_spaced_gradient, const SkRasterPipelineContexts::GradientCtx* c) { auto t = x; auto idx = trunc_(t * static_cast(c->stopCount-1)); gradient_lookup(c, idx, t, &r, &g, &b, &a); } -STAGE_GP(evenly_spaced_2_stop_gradient, const SkRasterPipeline_EvenlySpaced2StopGradientCtx* c) { +LOWP_STAGE_GP(evenly_spaced_2_stop_gradient, + const SkRasterPipelineContexts::EvenlySpaced2StopGradientCtx* c) { auto t = x; - round_F_to_U16(mad(t, c->f[0], c->b[0]), - mad(t, c->f[1], c->b[1]), - mad(t, c->f[2], c->b[2]), - mad(t, c->f[3], c->b[3]), + round_F_to_U16(mad(t, c->factor[0], c->bias[0]), + mad(t, c->factor[1], c->bias[1]), + mad(t, c->factor[2], c->bias[2]), + mad(t, c->factor[3], c->bias[3]), &r,&g,&b,&a); } -STAGE_GP(bilerp_clamp_8888, const SkRasterPipeline_GatherCtx* ctx) { +LOWP_STAGE_GP(bilerp_clamp_8888, const SkRasterPipelineContexts::GatherCtx* ctx) { // Quantize sample point and transform into lerp coordinates converting them to 16.16 fixed // point number. #if defined(SKRP_CPU_LSX) @@ -6646,7 +6670,7 @@ STAGE_GP(bilerp_clamp_8888, const SkRasterPipeline_GatherCtx* ctx) { a = lerpY(topA, bottomA); } -STAGE_GG(xy_to_unit_angle, NoCtx) { +LOWP_STAGE_GG(xy_to_unit_angle, NoCtx) { F xabs = abs_(x), yabs = abs_(y); @@ -6669,13 +6693,13 @@ STAGE_GG(xy_to_unit_angle, NoCtx) { phi = if_then_else(phi != phi , 0 , phi); // Check for NaN. x = phi; } -STAGE_GG(xy_to_radius, NoCtx) { +LOWP_STAGE_GG(xy_to_radius, NoCtx) { x = sqrt_(x*x + y*y); } // ~~~~~~ Compound stages ~~~~~~ // -STAGE_PP(srcover_rgba_8888, const SkRasterPipeline_MemoryCtx* ctx) { +LOWP_STAGE_PP(srcover_rgba_8888, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy(ctx, dx,dy); load_8888_(ptr, &dr,&dg,&db,&da); @@ -6688,7 +6712,7 @@ STAGE_PP(srcover_rgba_8888, const SkRasterPipeline_MemoryCtx* ctx) { // ~~~~~~ skgpu::Swizzle stage ~~~~~~ // -STAGE_PP(swizzle, void* ctx) { +LOWP_STAGE_PP(swizzle, void* ctx) { auto ir = r, ig = g, ib = b, ia = a; U16* o[] = {&r, &g, &b, &a}; char swiz[4]; @@ -6707,9 +6731,158 @@ STAGE_PP(swizzle, void* ctx) { } } +// These debug stages are meant to be used with SkRasterPipelineVisualizer functions +// to look at certain lanes in a pipeline at various times. +// There are 3 flavors of functions for lowp, 2 for highp +// debug_L_255: clamps the value of the lane to [0, 255] and puts it into +// a channel of an appropriate color to visualize that component. +// debug_L: Converts the value of the lane to 12.8 fixed point such that it is +// easier to understand when using Viewer's debugger. A number like +// 532.5 will be displayed as 0x02 0x14 0x80 0xFF. 532 -> hex is 214 +// 0.5 is 0x80 / 0x100 and 0xFF indicates positive (0x00 is negative). +// Note on lowp there will be no fractional components nor negative numbers. +// debug_x/y: (lowp only) visualizes the lane, which is a 32 bit float (combined from +// rg or ba) as 12.8 fixed point (outlined above). + +LOWP_STAGE_PP(debug_r_255, const SkRasterPipelineContexts::MemoryCtx* ctx) { + auto ptr = ptr_at_xy(ctx, dx,dy); + + auto px = cast(min(r, 255)); + px |= 0xFF000000; // make opaque + store(ptr, px); +} +LOWP_STAGE_PP(debug_g_255, const SkRasterPipelineContexts::MemoryCtx* ctx) { + auto ptr = ptr_at_xy(ctx, dx,dy); + + auto px = cast(min(g, 255)) << 8; + px |= 0xFF000000; // make opaque + store(ptr, px); +} +LOWP_STAGE_PP(debug_b_255, const SkRasterPipelineContexts::MemoryCtx* ctx) { + auto ptr = ptr_at_xy(ctx, dx,dy); + + auto px = cast(min(b, 255)) << 16; + px |= 0xFF000000; // make opaque + store(ptr, px); +} +LOWP_STAGE_PP(debug_a_255, const SkRasterPipelineContexts::MemoryCtx* ctx) { + auto ptr = ptr_at_xy(ctx, dx,dy); + + auto px = cast(min(a, 255)); + // Render alpha as greyscale + store(ptr, px | px << 8 | px << 16 | px << 24); +} + +SI void lowp_fixed_point(const SkRasterPipelineContexts::MemoryCtx* ctx, + const size_t dx, const size_t dy, + const F lane) { + auto ptr = ptr_at_xy(ctx, dx,dy); + + auto r2 = cast(abs_(lane) / 256) & 0xFF; + auto g2 = cast(abs_(lane)) & 0xFF; + auto b2 = cast(abs_(lane) * 256) & 0xFF; + auto a2 = cast(min(max(lane * -256 * 256, 0), 0xFF)); + store(ptr, r2 | (g2 << 8) | (b2 << 16) | (a2 << 24)); +} + +LOWP_STAGE_GG(debug_x, const SkRasterPipelineContexts::MemoryCtx* ctx) { + lowp_fixed_point(ctx, dx, dy, x); +} +LOWP_STAGE_GG(debug_y, const SkRasterPipelineContexts::MemoryCtx* ctx) { + lowp_fixed_point(ctx, dx, dy, y); +} + +// Note that there won't be negative numbers nor fractional points. +SI void lowp_fixed_point(const SkRasterPipelineContexts::MemoryCtx* ctx, + const size_t dx, const size_t dy, + const U16 lane) { + auto ptr = ptr_at_xy(ctx, dx,dy); + // Flip the byte ordering so it aligns with the fixed point used above + auto px = cast((lane & 0xFF00) >> 8 | + (lane & 0x00FF) << 8); + store(ptr, px); +} + +LOWP_STAGE_PP(debug_r, const SkRasterPipelineContexts::MemoryCtx* ctx) { + lowp_fixed_point(ctx, dx, dy, r); +} +LOWP_STAGE_PP(debug_g, const SkRasterPipelineContexts::MemoryCtx* ctx) { + lowp_fixed_point(ctx, dx, dy, g); +} +LOWP_STAGE_PP(debug_b, const SkRasterPipelineContexts::MemoryCtx* ctx) { + lowp_fixed_point(ctx, dx, dy, b); +} +LOWP_STAGE_PP(debug_a, const SkRasterPipelineContexts::MemoryCtx* ctx) { + lowp_fixed_point(ctx, dx, dy, a); +} + #endif//defined(SKRP_CPU_SCALAR) controlling whether we build lowp stages } // namespace lowp +HIGHP_STAGE(debug_r_255, const SkRasterPipelineContexts::MemoryCtx* ctx) { + auto ptr = ptr_at_xy(ctx, dx,dy); + + auto px = to_unorm(r, 255); + px |= 0xFF000000; // make opaque + store(ptr, px); +} +HIGHP_STAGE(debug_g_255, const SkRasterPipelineContexts::MemoryCtx* ctx) { + auto ptr = ptr_at_xy(ctx, dx,dy); + + auto px = to_unorm(g, 255) << 8; + px |= 0xFF000000; // make opaque + store(ptr, px); +} +HIGHP_STAGE(debug_b_255, const SkRasterPipelineContexts::MemoryCtx* ctx) { + auto ptr = ptr_at_xy(ctx, dx,dy); + + auto px = to_unorm(b, 255) << 16; + px |= 0xFF000000; // make opaque + store(ptr, px); +} +HIGHP_STAGE(debug_a_255, const SkRasterPipelineContexts::MemoryCtx* ctx) { + auto ptr = ptr_at_xy(ctx, dx,dy); + + auto px = to_unorm(a, 255); + // Render alpha as greyscale + store(ptr, px | px << 8 | px << 16 | px << 24); +} + +SI void highp_fixed_point(const SkRasterPipelineContexts::MemoryCtx* ctx, + const size_t dx, const size_t dy, + const F lane) { + auto ptr = ptr_at_xy(ctx, dx,dy); + + auto r2 = trunc_(abs_(lane) / 256) & 0xFF; + auto g2 = trunc_(abs_(lane)) & 0xFF; + auto b2 = trunc_(abs_(lane) * 256) & 0xFF; + auto a2 = to_unorm(lane * -256 * 256, 255); + store(ptr, r2 | (g2 << 8) | (b2 << 16) | (a2 << 24)); +} + +HIGHP_STAGE(debug_r, const SkRasterPipelineContexts::MemoryCtx* ctx) { + highp_fixed_point(ctx, dx, dy, r); +} +HIGHP_STAGE(debug_g, const SkRasterPipelineContexts::MemoryCtx* ctx) { + highp_fixed_point(ctx, dx, dy, g); +} +HIGHP_STAGE(debug_b, const SkRasterPipelineContexts::MemoryCtx* ctx) { + highp_fixed_point(ctx, dx, dy, b); +} +HIGHP_STAGE(debug_a, const SkRasterPipelineContexts::MemoryCtx* ctx) { + highp_fixed_point(ctx, dx, dy, a); +} +// The special handling of x and y only make sense in lowp mode. If they are +// called in highp mode (e.g. someone made a lowp pipelin and then added something +// which required highp), we'll just treat x and y as r and g respectively (we have +// to define *some* behavior or this won't link). +HIGHP_STAGE(debug_x, const SkRasterPipelineContexts::MemoryCtx* ctx) { + highp_fixed_point(ctx, dx, dy, r); +} +HIGHP_STAGE(debug_y, const SkRasterPipelineContexts::MemoryCtx* ctx) { + highp_fixed_point(ctx, dx, dy, g); +} + /* This gives us SK_OPTS::lowp::N if lowp::N has been set, or SK_OPTS::N if it hasn't. */ namespace lowp { static constexpr size_t lowp_N = N; } diff --git a/gfx/skia/skia/src/opts/SkSwizzler_opts.inc b/gfx/skia/skia/src/opts/SkSwizzler_opts.inc index ff80497c5dde..671db3f05f61 100644 --- a/gfx/skia/skia/src/opts/SkSwizzler_opts.inc +++ b/gfx/skia/skia/src/opts/SkSwizzler_opts.inc @@ -5,9 +5,9 @@ * found in the LICENSE file. */ -#include "include/private/SkColorData.h" #include "src/base/SkUtils.h" #include "src/base/SkVx.h" +#include "src/core/SkColorData.h" #include "src/core/SkSwizzlePriv.h" #include diff --git a/gfx/skia/skia/src/pathops/SkOpSegment.cpp b/gfx/skia/skia/src/pathops/SkOpSegment.cpp index 6a1d406c07cf..777b53e07e19 100644 --- a/gfx/skia/skia/src/pathops/SkOpSegment.cpp +++ b/gfx/skia/skia/src/pathops/SkOpSegment.cpp @@ -31,13 +31,13 @@ intersection tests cannot. #define F (false) // discard the edge #define T (true) // keep the edge -static const bool gUnaryActiveEdge[2][2] = { +static constexpr bool kUnaryActiveEdge[2][2] = { // from=0 from=1 // to=0,1 to=0,1 {F, T}, {T, F}, }; -static const bool gActiveEdge[kXOR_SkPathOp + 1][2][2][2][2] = { +static constexpr bool kActiveEdge[kXOR_SkPathOp + 1][2][2][2][2] = { // miFrom=0 miFrom=1 // miTo=0 miTo=1 miTo=0 miTo=1 // suFrom=0 1 suFrom=0 1 suFrom=0 1 suFrom=0 1 @@ -146,7 +146,7 @@ bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, SkOpSpanBase* start, Sk suFrom = (oppMaxWinding & xorSuMask) != 0; suTo = (oppSumWinding & xorSuMask) != 0; } - bool result = gActiveEdge[op][miFrom][miTo][suFrom][suTo]; + bool result = kActiveEdge[op][miFrom][miTo][suFrom][suTo]; #if DEBUG_ACTIVE_OP SkDebugf("%s id=%d t=%1.9g tEnd=%1.9g op=%s miFrom=%d miTo=%d suFrom=%d suTo=%d result=%d\n", __FUNCTION__, debugID(), start->t(), end->t(), @@ -165,7 +165,7 @@ bool SkOpSegment::activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sum setUpWinding(start, end, &maxWinding, sumWinding); bool from = maxWinding != 0; bool to = *sumWinding != 0; - bool result = gUnaryActiveEdge[from][to]; + bool result = kUnaryActiveEdge[from][to]; return result; } @@ -870,7 +870,7 @@ void SkOpSegment::markAllDone() { SkOpSegment* other = this; SkOpSpan* priorDone = nullptr; SkOpSpan* lastDone = nullptr; - int safetyNet = 100000; + int safetyNet = 1000; while ((other = other->nextChase(&start, &step, &minSpan, &last))) { if (!--safetyNet) { return false; @@ -902,7 +902,7 @@ bool SkOpSegment::markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end, in bool success = markWinding(spanStart, winding); SkOpSpanBase* last = nullptr; SkOpSegment* other = this; - int safetyNet = 100000; + int safetyNet = 1000; while ((other = other->nextChase(&start, &step, &spanStart, &last))) { if (!--safetyNet) { return false; @@ -927,7 +927,7 @@ bool SkOpSegment::markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end, bool success = markWinding(spanStart, winding, oppWinding); SkOpSpanBase* last = nullptr; SkOpSegment* other = this; - int safetyNet = 100000; + int safetyNet = 1000; while ((other = other->nextChase(&start, &step, &spanStart, &last))) { if (!--safetyNet) { return false; @@ -1165,7 +1165,7 @@ bool SkOpSegment::missingCoincidence() { SkOpSpan* prior = nullptr; SkOpSpanBase* spanBase = &fHead; bool result = false; - int safetyNet = 100000; + int safetyNet = 1000; do { SkOpPtT* ptT = spanBase->ptT(), * spanStopPtT = ptT; SkOPASSERT(ptT->span() == spanBase); @@ -1279,7 +1279,7 @@ bool SkOpSegment::moveMultiples() { } SkOpPtT* startPtT = test->ptT(); SkOpPtT* testPtT = startPtT; - int safetyHatch = 1000000; + int safetyHatch = 1000; do { // iterate through all spans associated with start if (!--safetyHatch) { return false; @@ -1405,7 +1405,7 @@ bool SkOpSegment::spansNearby(const SkOpSpanBase* refSpan, const SkOpSpanBase* c } const SkOpPtT* check = checkHead; const SkOpSegment* refSeg = ref->segment(); - int escapeHatch = 100000; // defend against infinite loops + int escapeHatch = 100; // defend against infinite loops do { if (check->deleted()) { continue; @@ -1569,7 +1569,7 @@ bool SkOpSegment::sortAngles() { baseAngle = toAngle; } SkOpPtT* ptT = span->ptT(), * stopPtT = ptT; - int safetyNet = 1000000; + int safetyNet = 1000; do { if (!--safetyNet) { return false; diff --git a/gfx/skia/skia/src/pathops/SkPathOpsCommon.h b/gfx/skia/skia/src/pathops/SkPathOpsCommon.h index cca3b40421ad..1a1ad77297db 100644 --- a/gfx/skia/skia/src/pathops/SkPathOpsCommon.h +++ b/gfx/skia/skia/src/pathops/SkPathOpsCommon.h @@ -17,6 +17,7 @@ class SkOpSegment; class SkOpSpan; class SkOpSpanBase; class SkPath; +struct SkRect; template class SkTDArray; @@ -33,4 +34,6 @@ bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result SkDEBUGPARAMS(bool skipAssert) SkDEBUGPARAMS(const char* testName)); +bool ComputeTightBounds(const SkPath&, SkRect*); + #endif diff --git a/gfx/skia/skia/src/pathops/SkPathOpsCubic.cpp b/gfx/skia/skia/src/pathops/SkPathOpsCubic.cpp index 64e0ad6f469a..a7651aeccac9 100644 --- a/gfx/skia/skia/src/pathops/SkPathOpsCubic.cpp +++ b/gfx/skia/skia/src/pathops/SkPathOpsCubic.cpp @@ -375,8 +375,6 @@ int SkDCubic::searchRoots(double extremeTs[6], int extrema, double axisIntercept // cubic roots -static const double PI = 3.141592653589793; - // from SkGeometry.cpp (and Numeric Solutions, 5.6) // // TODO(skbug.com/14063) Deduplicate with SkCubics::RootsValidT int SkDCubic::RootsValidT(double A, double B, double C, double D, double t[3]) { @@ -475,11 +473,11 @@ int SkDCubic::RootsReal(double A, double B, double C, double D, double s[3]) { r = neg2RootQ * cos(theta / 3) - adiv3; *roots++ = r; - r = neg2RootQ * cos((theta + 2 * PI) / 3) - adiv3; + r = neg2RootQ * cos((theta + 2 * SK_DoublePI) / 3) - adiv3; if (!AlmostDequalUlps(s[0], r)) { *roots++ = r; } - r = neg2RootQ * cos((theta - 2 * PI) / 3) - adiv3; + r = neg2RootQ * cos((theta - 2 * SK_DoublePI) / 3) - adiv3; if (!AlmostDequalUlps(s[0], r) && (roots - s == 1 || !AlmostDequalUlps(s[1], r))) { *roots++ = r; } diff --git a/gfx/skia/skia/src/pathops/SkPathOpsDebug.cpp b/gfx/skia/skia/src/pathops/SkPathOpsDebug.cpp index ef00cac1bd6f..92469068a15f 100644 --- a/gfx/skia/skia/src/pathops/SkPathOpsDebug.cpp +++ b/gfx/skia/skia/src/pathops/SkPathOpsDebug.cpp @@ -3016,7 +3016,7 @@ static int debug_paths_draw_the_same(const SkPath& one, const SkPath& two, SkBit } void ReportOpFail(const SkPath& one, const SkPath& two, SkPathOp op) { - SkDebugf("// Op did not expect failure\n"); + SkDEBUGF("// Op did not expect failure\n"); DumpOp(stderr, one, two, op, "opTest"); fflush(stderr); } @@ -3056,7 +3056,7 @@ void VerifyOp(const SkPath& one, const SkPath& two, SkPathOp op, } void ReportSimplifyFail(const SkPath& path) { - SkDebugf("// Simplify did not expect failure\n"); + SkDEBUGF("// Simplify did not expect failure\n"); DumpSimplify(stderr, path, "simplifyTest"); fflush(stderr); } diff --git a/gfx/skia/skia/src/pathops/SkPathOpsPoint.h b/gfx/skia/skia/src/pathops/SkPathOpsPoint.h index 053192bad629..249577214e57 100644 --- a/gfx/skia/skia/src/pathops/SkPathOpsPoint.h +++ b/gfx/skia/skia/src/pathops/SkPathOpsPoint.h @@ -268,7 +268,7 @@ struct SkDPoint { float largestNumber = std::max(SkTAbs(a.fX), std::max(SkTAbs(a.fY), std::max(SkTAbs(b.fX), SkTAbs(b.fY)))); SkVector diffs = a - b; - float largestDiff = std::max(diffs.fX, diffs.fY); + float largestDiff = std::max(SkTAbs(diffs.fX), SkTAbs(diffs.fY)); return roughly_zero_when_compared_to(largestDiff, largestNumber); } diff --git a/gfx/skia/skia/src/pathops/SkPathOpsTSect.cpp b/gfx/skia/skia/src/pathops/SkPathOpsTSect.cpp index ba5c41a2be14..adff55e0d705 100644 --- a/gfx/skia/skia/src/pathops/SkPathOpsTSect.cpp +++ b/gfx/skia/skia/src/pathops/SkPathOpsTSect.cpp @@ -7,6 +7,7 @@ #include "src/pathops/SkPathOpsTSect.h" +#include "include/private/base/SkFloatingPoint.h" #include "include/private/base/SkMacros.h" #include "include/private/base/SkTArray.h" #include "src/base/SkTSort.h" @@ -234,7 +235,7 @@ void SkTSpan::init(const SkTCurve& c) { } bool SkTSpan::initBounds(const SkTCurve& c) { - if (SkDoubleIsNaN(fStartT) || SkDoubleIsNaN(fEndT)) { + if (SkIsNaN(fStartT) || SkIsNaN(fEndT)) { return false; } c.subDivide(fStartT, fEndT, fPart); diff --git a/gfx/skia/skia/src/pathops/SkPathOpsTSect.h b/gfx/skia/skia/src/pathops/SkPathOpsTSect.h index 012392f11122..8159539c3561 100644 --- a/gfx/skia/skia/src/pathops/SkPathOpsTSect.h +++ b/gfx/skia/skia/src/pathops/SkPathOpsTSect.h @@ -30,8 +30,6 @@ typedef uint8_t SkOpDebugBool; typedef bool SkOpDebugBool; #endif -#define SkDoubleIsNaN std::isnan - class SkTCoincident { public: SkTCoincident() { diff --git a/gfx/skia/skia/src/pathops/SkPathOpsTightBounds.cpp b/gfx/skia/skia/src/pathops/SkPathOpsTightBounds.cpp index a079512ddfb7..0b146b1fca57 100644 --- a/gfx/skia/skia/src/pathops/SkPathOpsTightBounds.cpp +++ b/gfx/skia/skia/src/pathops/SkPathOpsTightBounds.cpp @@ -4,12 +4,12 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ + #include "include/core/SkPath.h" #include "include/core/SkPathTypes.h" #include "include/core/SkPoint.h" #include "include/core/SkRect.h" #include "include/core/SkScalar.h" -#include "include/pathops/SkPathOps.h" #include "src/base/SkArenaAlloc.h" #include "src/core/SkPathPriv.h" #include "src/pathops/SkOpContour.h" @@ -20,7 +20,7 @@ #include -bool TightBounds(const SkPath& path, SkRect* result) { +bool ComputeTightBounds(const SkPath& path, SkRect* result) { SkRect moveBounds = { SK_ScalarMax, SK_ScalarMax, SK_ScalarMin, SK_ScalarMin }; bool wellBehaved = true; for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) { diff --git a/gfx/skia/skia/src/pdf/SkClusterator.h b/gfx/skia/skia/src/pdf/SkClusterator.h index 2ea3fbb127d6..850111240a71 100644 --- a/gfx/skia/skia/src/pdf/SkClusterator.h +++ b/gfx/skia/skia/src/pdf/SkClusterator.h @@ -26,7 +26,7 @@ public: uint32_t fGlyphIndex; uint32_t fGlyphCount; explicit operator bool() const { return fGlyphCount != 0; } - bool operator==(const SkClusterator::Cluster& o) { + bool operator==(const SkClusterator::Cluster& o) const { return fUtf8Text == o.fUtf8Text && fTextByteLength == o.fTextByteLength && fGlyphIndex == o.fGlyphIndex diff --git a/gfx/skia/skia/src/pdf/SkJpegInfo.h b/gfx/skia/skia/src/pdf/SkJpegInfo.h deleted file mode 100644 index 17155e4830b3..000000000000 --- a/gfx/skia/skia/src/pdf/SkJpegInfo.h +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#ifndef SkJpegInfo_DEFINED -#define SkJpegInfo_DEFINED - -#endif // SkJpegInfo_DEFINED diff --git a/gfx/skia/skia/src/pdf/SkJpegInfo_libjpegturbo.cpp b/gfx/skia/skia/src/pdf/SkJpegInfo_libjpegturbo.cpp deleted file mode 100644 index 020c041a5a51..000000000000 --- a/gfx/skia/skia/src/pdf/SkJpegInfo_libjpegturbo.cpp +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - * - * This file can parse information from a JPEG using libjpeg (which must be compiled in). - */ - diff --git a/gfx/skia/skia/src/pdf/SkJpegInfo_none.cpp b/gfx/skia/skia/src/pdf/SkJpegInfo_none.cpp deleted file mode 100644 index 78e44cd48353..000000000000 --- a/gfx/skia/skia/src/pdf/SkJpegInfo_none.cpp +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - * - * This file can parse information from a JPEG without being able to decode - * it. This does not need Skia to be built with a JPEG library. - */ - diff --git a/gfx/skia/skia/src/pdf/SkPDFBitmap.cpp b/gfx/skia/skia/src/pdf/SkPDFBitmap.cpp index ecf93e0d8f4a..998dc2fe991b 100644 --- a/gfx/skia/skia/src/pdf/SkPDFBitmap.cpp +++ b/gfx/skia/skia/src/pdf/SkPDFBitmap.cpp @@ -9,11 +9,9 @@ #include "include/codec/SkCodec.h" #include "include/codec/SkEncodedOrigin.h" -#include "include/codec/SkJpegDecoder.h" #include "include/core/SkAlphaType.h" #include "include/core/SkBitmap.h" #include "include/core/SkColor.h" -#include "include/core/SkColorPriv.h" #include "include/core/SkColorSpace.h" #include "include/core/SkColorType.h" #include "include/core/SkData.h" @@ -25,12 +23,13 @@ #include "include/core/SkStream.h" #include "include/docs/SkPDFDocument.h" #include "include/encode/SkICC.h" -#include "include/encode/SkJpegEncoder.h" #include "include/private/SkEncodedInfo.h" #include "include/private/base/SkAssert.h" #include "include/private/base/SkMutex.h" #include "include/private/base/SkTo.h" #include "modules/skcms/skcms.h" +#include "src/codec/SkCodecPriv.h" +#include "src/core/SkColorPriv.h" #include "src/core/SkTHash.h" #include "src/pdf/SkDeflate.h" #include "src/pdf/SkPDFDocumentPriv.h" @@ -45,10 +44,6 @@ #include #include -/*static*/ const SkEncodedInfo& SkPDFBitmap::GetEncodedInfo(SkCodec& codec) { - return codec.getEncodedInfo(); -} - namespace { // write a single byte to a stream n times. @@ -114,22 +109,11 @@ void emit_image_stream(SkPDFDocument* doc, pdfDict.insertRef("SMask", sMask); } pdfDict.insertInt("BitsPerComponent", 8); - #ifdef SK_PDF_BASE85_BINARY - auto filters = SkPDFMakeArray(); - filters->appendName("ASCII85Decode"); - switch (format) { - case SkPDFStreamFormat::DCT: filters->appendName("DCTDecode"); break; - case SkPDFStreamFormat::Flate: filters->appendName("FlateDecode"); break; - case SkPDFStreamFormat::Uncompressed: break; - } - pdfDict.insertObject("Filter", std::move(filters)); - #else switch (format) { case SkPDFStreamFormat::DCT: pdfDict.insertName("Filter", "DCTDecode"); break; case SkPDFStreamFormat::Flate: pdfDict.insertName("Filter", "FlateDecode"); break; case SkPDFStreamFormat::Uncompressed: break; } - #endif if (format == SkPDFStreamFormat::DCT) { pdfDict.insertInt("ColorTransform", 0); } @@ -175,9 +159,6 @@ void do_deflated_alpha(const SkPixmap& pm, SkPDFDocument* doc, SkPDFIndirectRefe deflateWStream->finalize(); } - #ifdef SK_PDF_BASE85_BINARY - SkPDFUtils::Base85Encode(buffer.detachAsStream(), &buffer); - #endif int length = SkToInt(buffer.bytesWritten()); emit_image_stream(doc, ref, [&buffer](SkWStream* stream) { buffer.writeToAndReset(stream); }, pm.info().dimensions(), SkPDFUnion::Name("DeviceGray"), @@ -288,9 +269,6 @@ void do_deflated_image(const SkPixmap& pm, } } - #ifdef SK_PDF_BASE85_BINARY - SkPDFUtils::Base85Encode(buffer.detachAsStream(), &buffer); - #endif int length = SkToInt(buffer.bytesWritten()); emit_image_stream(doc, ref, [&buffer](SkWStream* stream) { buffer.writeToAndReset(stream); }, pm.info().dimensions(), std::move(colorSpace), sMask, length, format); @@ -301,19 +279,17 @@ void do_deflated_image(const SkPixmap& pm, bool do_jpeg(sk_sp data, SkColorSpace* imageColorSpace, SkPDFDocument* doc, SkISize size, SkPDFIndirectReference ref) { -#ifdef MOZ_SKIA - return false; -#else - static constexpr const SkCodecs::Decoder decoders[] = { - SkJpegDecoder::Decoder(), - }; - std::unique_ptr codec = SkCodec::MakeFromData(data, decoders); + SkPDF::DecodeJpegCallback decodeJPEG = doc->metadata().jpegDecoder; + if (!decodeJPEG) { + return false; + } + std::unique_ptr codec = decodeJPEG(data); if (!codec) { return false; } SkISize jpegSize = codec->dimensions(); - const SkEncodedInfo& encodedInfo = SkPDFBitmap::GetEncodedInfo(*codec); + const SkEncodedInfo& encodedInfo = SkCodecPriv::GetEncodedInfo(codec.get()); SkEncodedInfo::Color jpegColorType = encodedInfo.color(); SkEncodedOrigin exifOrientation = codec->getOrigin(); @@ -324,11 +300,6 @@ bool do_jpeg(sk_sp data, SkColorSpace* imageColorSpace, SkPDFDocument* d || kTopLeft_SkEncodedOrigin != exifOrientation) { return false; } - #ifdef SK_PDF_BASE85_BINARY - SkDynamicMemoryWStream buffer; - SkPDFUtils::Base85Encode(SkMemoryStream::MakeDirect(data->data(), data->size()), &buffer); - data = buffer.detachAsData(); - #endif int channels = yuv ? 3 : 1; SkPDFUnion colorSpace = yuv ? SkPDFUnion::Name("DeviceRGB") : SkPDFUnion::Name("DeviceGray"); @@ -356,7 +327,6 @@ bool do_jpeg(sk_sp data, SkColorSpace* imageColorSpace, SkPDFDocument* d jpegSize, std::move(colorSpace), SkPDFIndirectReference(), SkToInt(data->size()), SkPDFStreamFormat::DCT); return true; -#endif } SkBitmap to_pixels(const SkImage* image) { @@ -400,12 +370,12 @@ void serialize_image(const SkImage* img, } SkBitmap bm = to_pixels(img); const SkPixmap& pm = bm.pixmap(); + SkPDF::EncodeJpegCallback encodeJPEG = doc->metadata().jpegEncoder; + bool isOpaque = pm.isOpaque() || pm.computeIsOpaque(); - if (encodingQuality <= 100 && isOpaque) { - SkJpegEncoder::Options jOpts; - jOpts.fQuality = encodingQuality; + if (encodeJPEG && encodingQuality <= 100 && isOpaque) { SkDynamicMemoryWStream stream; - if (SkJpegEncoder::Encode(&stream, pm, jOpts)) { + if (encodeJPEG(&stream, pm, encodingQuality)) { if (do_jpeg(stream.detachAsData(), pm.colorSpace(), doc, dimensions, ref)) { return; } diff --git a/gfx/skia/skia/src/pdf/SkPDFBitmap.h b/gfx/skia/skia/src/pdf/SkPDFBitmap.h index 09ccaead0ec3..6853aeee0c82 100644 --- a/gfx/skia/skia/src/pdf/SkPDFBitmap.h +++ b/gfx/skia/skia/src/pdf/SkPDFBitmap.h @@ -13,10 +13,8 @@ #include -class SkCodec; class SkImage; class SkPDFDocument; -struct SkEncodedInfo; struct SkPDFIndirectReference; /** @@ -27,11 +25,6 @@ SkPDFIndirectReference SkPDFSerializeImage(const SkImage* img, SkPDFDocument* doc, int encodingQuality = 101); -class SkPDFBitmap { -public: - static const SkEncodedInfo& GetEncodedInfo(SkCodec&); -}; - struct SkPDFIccProfileKey { sk_sp fData; int fChannels; diff --git a/gfx/skia/skia/src/pdf/SkPDFDevice.cpp b/gfx/skia/skia/src/pdf/SkPDFDevice.cpp index 8367ed1cb520..61403ed1ab2b 100644 --- a/gfx/skia/skia/src/pdf/SkPDFDevice.cpp +++ b/gfx/skia/skia/src/pdf/SkPDFDevice.cpp @@ -7,6 +7,7 @@ #include "src/pdf/SkPDFDevice.h" +#include "include/codec/SkCodec.h" #include "include/core/SkAlphaType.h" #include "include/core/SkBitmap.h" #include "include/core/SkBlendMode.h" @@ -40,7 +41,6 @@ #include "include/core/SkTypeface.h" #include "include/core/SkTypes.h" #include "include/docs/SkPDFDocument.h" -#include "include/encode/SkJpegEncoder.h" #include "include/pathops/SkPathOps.h" #include "include/private/base/SkDebug.h" #include "include/private/base/SkTemplates.h" @@ -98,6 +98,7 @@ SkPDFDevice::MarkedContentManager::MarkedContentManager(SkPDFDocument* document, , fOut(out) , fCurrentlyActiveMark() , fNextMarksElemId(0) + , fCurrentMarksElemId(0) , fMadeMarks(false) {} @@ -112,13 +113,14 @@ void SkPDFDevice::MarkedContentManager::setNextMarksElemId(int nextMarksElemId) int SkPDFDevice::MarkedContentManager::elemId() const { return fNextMarksElemId; } void SkPDFDevice::MarkedContentManager::beginMark() { - if (fNextMarksElemId == fCurrentlyActiveMark.elemId()) { + if (fNextMarksElemId == fCurrentMarksElemId) { return; } - if (fCurrentlyActiveMark) { + if (fCurrentMarksElemId) { // End this mark fOut->writeText("EMC\n"); fCurrentlyActiveMark = SkPDFStructTree::Mark(); + fCurrentMarksElemId = 0; } if (fNextMarksElemId) { fCurrentlyActiveMark = fDoc->createMarkForElemId(fNextMarksElemId); @@ -128,7 +130,37 @@ void SkPDFDevice::MarkedContentManager::beginMark() { fOut->writeText(" <writeDecAsText(fCurrentlyActiveMark.mcid()); fOut->writeText(" >>BDC\n"); + fCurrentMarksElemId = fCurrentlyActiveMark.elemId(); fMadeMarks = true; + } else if (SkPDF::NodeID::BackgroundArtifact <= fNextMarksElemId && + fNextMarksElemId <= SkPDF::NodeID::OtherArtifact && + fDoc->hasCurrentPage()) + { + fOut->writeText("/Artifact"); + if (fNextMarksElemId == SkPDF::NodeID::OtherArtifact) { + fOut->writeText(" BMC\n"); + } else if (fNextMarksElemId == SkPDF::NodeID::PaginationArtifact || + fNextMarksElemId == SkPDF::NodeID::PaginationHeaderArtifact || + fNextMarksElemId == SkPDF::NodeID::PaginationFooterArtifact || + fNextMarksElemId == SkPDF::NodeID::PaginationWatermarkArtifact) + { + fOut->writeText(" <writeText(" /Subtype Header"); + } else if (fNextMarksElemId == SkPDF::NodeID::PaginationFooterArtifact) { + fOut->writeText(" /Subtype Footer"); + } else if (fNextMarksElemId == SkPDF::NodeID::PaginationWatermarkArtifact) { + fOut->writeText(" /Subtype Watermark"); + } + fOut->writeText(" >>BDC\n"); + } else if (fNextMarksElemId == SkPDF::NodeID::LayoutArtifact) { + fOut->writeText(" <>BDC\n"); + } else if (fNextMarksElemId == SkPDF::NodeID::PageArtifact) { + fOut->writeText(" <>BDC\n"); + } else if (fNextMarksElemId == SkPDF::NodeID::BackgroundArtifact) { + fOut->writeText(" <>BDC\n"); + } + fCurrentMarksElemId = fNextMarksElemId; } } } @@ -140,38 +172,30 @@ void SkPDFDevice::MarkedContentManager::accumulate(const SkPoint& p) { fCurrentlyActiveMark.accumulate(p); } -#ifndef SK_PDF_MASK_QUALITY - // If MASK_QUALITY is in [0,100], will be used for JpegEncoder. - // Otherwise, just encode masks losslessly. - #define SK_PDF_MASK_QUALITY 50 - // Since these masks are used for blurry shadows, we shouldn't need - // high quality. Raise this value if your shadows have visible JPEG - // artifacts. - // If SkJpegEncoder::Encode fails, we will fall back to the lossless - // encoding. -#endif - // This function destroys the mask and either frees or takes the pixels. -sk_sp mask_to_greyscale_image(SkMaskBuilder* mask) { +sk_sp mask_to_greyscale_image(SkMaskBuilder* mask, SkPDFDocument* doc) { sk_sp img; SkPixmap pm(SkImageInfo::Make(mask->fBounds.width(), mask->fBounds.height(), kGray_8_SkColorType, kOpaque_SkAlphaType), mask->fImage, mask->fRowBytes); -#ifndef MOZ_SKIA - const int imgQuality = SK_PDF_MASK_QUALITY; - if (imgQuality <= 100 && imgQuality >= 0) { - SkDynamicMemoryWStream buffer; - SkJpegEncoder::Options jpegOptions; - jpegOptions.fQuality = imgQuality; - if (SkJpegEncoder::Encode(&buffer, pm, jpegOptions)) { - img = SkImages::DeferredFromEncodedData(buffer.detachAsData()); - SkASSERT(img); - if (img) { - SkMaskBuilder::FreeImage(mask->image()); + constexpr int imgQuality = SK_PDF_MASK_QUALITY; + if constexpr (imgQuality <= 100 && imgQuality >= 0) { + SkPDF::EncodeJpegCallback encodeJPEG = doc->metadata().jpegEncoder; + SkPDF::DecodeJpegCallback decodeJPEG = doc->metadata().jpegDecoder; + if (encodeJPEG && decodeJPEG) { + SkDynamicMemoryWStream buffer; + // By encoding this into jpeg, it be embedded efficiently during drawImage. + if (encodeJPEG(&buffer, pm, imgQuality)) { + std::unique_ptr codec = decodeJPEG(buffer.detachAsData()); + SkASSERT(codec); + img = SkCodecs::DeferredImage(std::move(codec)); + SkASSERT(img); + if (img) { + SkMaskBuilder::FreeImage(mask->image()); + } } } } -#endif if (!img) { img = SkImages::RasterFromPixmap( pm, [](const void* p, void*) { SkMaskBuilder::FreeImage(const_cast(p)); }, nullptr); @@ -588,7 +612,7 @@ void SkPDFDevice::internalDrawPathWithFilter(const SkClipStack& clipStack, return; } SkIRect dstMaskBounds = dstMask.fBounds; - sk_sp mask = mask_to_greyscale_image(&dstMask); + sk_sp mask = mask_to_greyscale_image(&dstMask, fDocument); // PDF doesn't seem to allow masking vector graphics with an Image XObject. // Must mask with a Form XObject. sk_sp maskDevice = this->makeCongruentDevice(); @@ -756,7 +780,7 @@ public: bool thousandEM = fPDFFont->strike().fPath.fUnitsPerEM == 1000; fViewersAgreeOnAdvancesInFont = thousandEM || !convertedToType3; } - void writeGlyph(uint16_t glyph, SkScalar advanceWidth, SkPoint xy) { + void writeGlyph(SkGlyphID glyph, SkScalar advanceWidth, SkPoint xy) { SkASSERT(fPDFFont); if (!fInitialized) { // Flip the text about the x-axis to account for origin swap and include @@ -864,7 +888,7 @@ void SkPDFDevice::drawGlyphRunAsPath( transparent.setColor(SK_ColorTRANSPARENT); if (this->localToDevice().hasPerspective()) { - SkAutoDeviceTransformRestore adr(this, SkMatrix::I()); + SkAutoDeviceTransformRestore adr(this, SkM44()); this->internalDrawGlyphRun(tmpGlyphRun, offset, transparent); } else { this->internalDrawGlyphRun(tmpGlyphRun, offset, transparent); @@ -1295,13 +1319,12 @@ static void populate_graphic_state_entry_from_paint( // PDF treats a shader as a color, so we only set one or the other. SkShader* shader = paint.getShader(); if (shader) { - // note: we always present the alpha as 1 for the shader, knowing that it will be - // accounted for when we create our newGraphicsState (below) if (as_SB(shader)->type() == SkShaderBase::ShaderType::kColor) { auto colorShader = static_cast(shader); // We don't have to set a shader just for a color. - color = SkColor4f::FromColor(colorShader->color()); - entry->fColor = {color.fR, color.fG, color.fB, 1}; + color = colorShader->color(); + color.fA *= paint.getAlphaf(); + entry->fColor = colorShader->color().makeOpaque(); } else { // PDF positions patterns relative to the initial transform, so // we need to apply the current transform to the shader parameters. @@ -1319,6 +1342,7 @@ static void populate_graphic_state_entry_from_paint( SkIRect bounds; clipStackBounds.roundOut(&bounds); + // Use alpha 1 for the shader, the paint alpha is applied with newGraphicsState (below) auto c = paint.getColor4f(); SkPDFIndirectReference pdfShader = SkPDFMakeShader(doc, shader, transform, bounds, {c.fR, c.fG, c.fB, 1.0f}); @@ -1828,7 +1852,7 @@ void SkPDFDevice::drawDevice(SkDevice* device, const SkSamplingOptions& sampling return; } - SkMatrix matrix = device->getRelativeTransform(*this); + SkMatrix matrix = device->getRelativeTransform(*this).asM33(); ScopedContentEntry content(this, &this->cs(), matrix, paint); if (!content) { return; @@ -1868,12 +1892,3 @@ void SkPDFDevice::drawSpecial(SkSpecialImage* srcImg, const SkMatrix& localToDev localToDevice); } } - -sk_sp SkPDFDevice::makeSpecial(const SkBitmap& bitmap) { - return SkSpecialImages::MakeFromRaster(bitmap.bounds(), bitmap, this->surfaceProps()); -} - -sk_sp SkPDFDevice::makeSpecial(const SkImage* image) { - return SkSpecialImages::MakeFromRaster( - image->bounds(), image->makeNonTextureImage(), this->surfaceProps()); -} diff --git a/gfx/skia/skia/src/pdf/SkPDFDevice.h b/gfx/skia/skia/src/pdf/SkPDFDevice.h index 3d3d5012ce8f..ec59e95211a1 100644 --- a/gfx/skia/skia/src/pdf/SkPDFDevice.h +++ b/gfx/skia/skia/src/pdf/SkPDFDevice.h @@ -124,10 +124,6 @@ public: const SkMatrix& initialTransform() const { return fInitialTransform; } -protected: - sk_sp makeSpecial(const SkBitmap&) override; - sk_sp makeSpecial(const SkImage*) override; - private: // TODO(vandebo): push most of SkPDFDevice's state into a core object in // order to get the right access levels without using friend. @@ -175,6 +171,7 @@ private: SkDynamicMemoryWStream* fOut; SkPDFStructTree::Mark fCurrentlyActiveMark; int fNextMarksElemId; + int fCurrentMarksElemId; bool fMadeMarks; } fMarkManager; diff --git a/gfx/skia/skia/src/pdf/SkPDFDocument.cpp b/gfx/skia/skia/src/pdf/SkPDFDocument.cpp index 6dc4d4fae7cc..156b81bfad9a 100644 --- a/gfx/skia/skia/src/pdf/SkPDFDocument.cpp +++ b/gfx/skia/skia/src/pdf/SkPDFDocument.cpp @@ -14,6 +14,7 @@ #include "include/core/SkRect.h" #include "include/core/SkSize.h" #include "include/core/SkStream.h" +#include "include/core/SkTypeface.h" #include "include/core/SkTypes.h" #include "include/private/base/SkMutex.h" #include "include/private/base/SkPoint_impl.h" @@ -45,6 +46,10 @@ #include #include +#if defined(SK_CODEC_ENCODES_JPEG) && defined(SK_CODEC_DECODES_JPEG) && !defined(SK_DISABLE_LEGACY_PDF_JPEG) +#include "include/docs/SkPDFJpegHelpers.h" +#endif + // For use in SkCanvas::drawAnnotation const char* SkPDFGetElemIdKey() { static constexpr char key[] = "PDF_Node_Key"; @@ -511,7 +516,7 @@ static sk_sp SkSrgbIcm() { "\214\363\31\363\247\3644\364\302\365P\365\336\366m\366\373\367\212" "\370\31\370\250\3718\371\307\372W\372\347\373w\374\7\374\230\375)\375" "\272\376K\376\334\377m\377\377"; - const size_t kProfileLength = 3212; + constexpr size_t kProfileLength = 3212; static_assert(kProfileLength == sizeof(kProfile) - 1, ""); return SkData::MakeWithoutCopy(kProfile, kProfileLength); } @@ -696,6 +701,20 @@ sk_sp SkPDF::MakeDocument(SkWStream* stream, const SkPDF::Metadata& if (meta.fEncodingQuality < 0) { meta.fEncodingQuality = 0; } +#if defined(SK_CODEC_ENCODES_JPEG) && defined(SK_CODEC_DECODES_JPEG) && !defined(SK_DISABLE_LEGACY_PDF_JPEG) + if (!meta.jpegDecoder) { + meta.jpegDecoder = SkPDF::JPEG::Decode; + } + if (!meta.jpegEncoder) { + meta.jpegEncoder = SkPDF::JPEG::Encode; + } +#else + if (!meta.jpegDecoder || !meta.jpegEncoder) { + if (!meta.allowNoJpegs) { + SK_ABORT("Must set both a jpegDecoder and jpegEncoder to create PDFs"); + } + } +#endif return stream ? sk_make_sp(stream, std::move(meta)) : nullptr; } diff --git a/gfx/skia/skia/src/pdf/SkPDFDocumentPriv.h b/gfx/skia/skia/src/pdf/SkPDFDocumentPriv.h index 26d58501cd3b..7094f69027c0 100644 --- a/gfx/skia/skia/src/pdf/SkPDFDocumentPriv.h +++ b/gfx/skia/skia/src/pdf/SkPDFDocumentPriv.h @@ -17,6 +17,7 @@ #include "include/core/SkSpan.h" // IWYU pragma: keep #include "include/core/SkStream.h" #include "include/core/SkString.h" +#include "include/core/SkTypeface.h" #include "include/core/SkTypes.h" #include "include/docs/SkPDFDocument.h" #include "include/private/base/SkMutex.h" @@ -172,12 +173,12 @@ public: skia_private::THashMap fICCProfileMap; - skia_private::THashMap> fTypefaceMetrics; - skia_private::THashMap> fType1GlyphNames; - skia_private::THashMap> fToUnicodeMap; - skia_private::THashMap> fToUnicodeMapEx; - skia_private::THashMap fFontDescriptors; - skia_private::THashMap fType3FontDescriptors; + skia_private::THashMap> fTypefaceMetrics; + skia_private::THashMap> fType1GlyphNames; + skia_private::THashMap> fToUnicodeMap; + skia_private::THashMap> fToUnicodeMapEx; + skia_private::THashMap fFontDescriptors; + skia_private::THashMap fType3FontDescriptors; skia_private::THashTable, const SkDescriptor&, SkPDFStrike::Traits> fStrikes; skia_private::THashMap 0) { - paint.setStrokeMiter(paint.getStrokeMiter() * fontToEMScale); paint.setStrokeWidth(paint.getStrokeWidth() * fontToEMScale); } @@ -280,7 +281,7 @@ const SkAdvancedTypefaceMetrics* SkPDFFont::GetMetrics(const SkTypeface& typefac // This probably isn't very good with an italic font. int16_t stemV = SHRT_MAX; for (char c : {'i', 'I', '!', '1'}) { - uint16_t g = font.unicharToGlyph(c); + SkGlyphID g = font.unicharToGlyph(c); SkRect bounds; font.getBounds(&g, 1, &bounds, nullptr); stemV = std::min(stemV, SkToS16(SkScalarRoundToInt(bounds.width()))); @@ -291,7 +292,7 @@ const SkAdvancedTypefaceMetrics* SkPDFFont::GetMetrics(const SkTypeface& typefac // Figure out a good guess for CapHeight: average the height of M and X. SkScalar capHeight = 0; for (char c : {'M', 'X'}) { - uint16_t g = font.unicharToGlyph(c); + SkGlyphID g = font.unicharToGlyph(c); SkRect bounds; font.getBounds(&g, 1, &bounds, nullptr); capHeight += bounds.height(); @@ -823,22 +824,27 @@ static void emit_subset_type3(const SkPDFFont& pdfFont, SkPDFDocument* doc) { AppendScalar(pimg.fOffset.y() * bitmapScale, &content); content.writeText(" cm\n"); - // Convert Gray image to jpeg if needed -#ifndef MOZ_SKIA + // Convert Grey image to deferred jpeg image to emit as jpeg if (pdfStrike.fHasMaskFilter) { - SkJpegEncoder::Options jpegOptions; - jpegOptions.fQuality = 50; // SK_PDF_MASK_QUALITY - SkImage* image = pimg.fImage.get(); - sk_sp jpegData = SkJpegEncoder::Encode(nullptr, image, jpegOptions); - if (jpegData) { - sk_sp jpegImage = SkImages::DeferredFromEncodedData(jpegData); - SkASSERT(jpegImage); - if (jpegImage) { - pimg.fImage = jpegImage; + SkPDF::EncodeJpegCallback encodeJPEG = doc->metadata().jpegEncoder; + SkPDF::DecodeJpegCallback decodeJPEG = doc->metadata().jpegDecoder; + if (encodeJPEG && decodeJPEG) { + SkImage* image = pimg.fImage.get(); + SkPixmap pm; + SkAssertResult(image->peekPixels(&pm)); + SkDynamicMemoryWStream buffer; + if (encodeJPEG(&buffer, pm, SK_PDF_MASK_QUALITY)) { + std::unique_ptr codec = + decodeJPEG(buffer.detachAsData()); + SkASSERT(codec); + sk_sp jpegImage = SkCodecs::DeferredImage(std::move(codec)); + SkASSERT(jpegImage); + if (jpegImage) { + pimg.fImage = jpegImage; + } } } } -#endif // Draw image into a Form XObject const SkISize imageSize = pimg.fImage->dimensions(); diff --git a/gfx/skia/skia/src/pdf/SkPDFGlyphUse.h b/gfx/skia/skia/src/pdf/SkPDFGlyphUse.h index fa7627ab5199..f43e65141130 100644 --- a/gfx/skia/skia/src/pdf/SkPDFGlyphUse.h +++ b/gfx/skia/skia/src/pdf/SkPDFGlyphUse.h @@ -27,8 +27,8 @@ public: if (fFirstNonZero == 1) { return fBitSet.forEachSetIndex(std::move(f)); } - uint16_t offset = fFirstNonZero - 1; - fBitSet.forEachSetIndex([&f, offset](unsigned v) { f(v == 0 ? v : v + offset); }); + size_t offset = fFirstNonZero - 1; + fBitSet.forEachSetIndex([&f, offset](size_t v) { f(v == 0 ? v : v + offset); }); } private: @@ -36,7 +36,7 @@ private: SkGlyphID fFirstNonZero = 0; SkGlyphID fLastGlyph = 0; - uint16_t toCode(SkGlyphID gid) const { + size_t toCode(SkGlyphID gid) const { if (gid == 0 || fFirstNonZero == 1) { return gid; } diff --git a/gfx/skia/skia/src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp b/gfx/skia/skia/src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp index 44c45b93774f..2e43baf443cd 100644 --- a/gfx/skia/skia/src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp +++ b/gfx/skia/skia/src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp @@ -98,7 +98,7 @@ std::unique_ptr SkPDFMakeCIDGlyphWidthsArray(const SkPDFStrikeSpec& std::vector glyphIDs; subset.getSetValues([&](unsigned index) { - glyphIDs.push_back(SkToU16(index)); + glyphIDs.push_back(SkTo(index)); }); auto glyphs = paths.glyphs(SkSpan(glyphIDs)); diff --git a/gfx/skia/skia/src/pdf/SkPDFTag.cpp b/gfx/skia/skia/src/pdf/SkPDFTag.cpp index 14f0a29607bf..3c7debae3f16 100644 --- a/gfx/skia/skia/src/pdf/SkPDFTag.cpp +++ b/gfx/skia/skia/src/pdf/SkPDFTag.cpp @@ -10,6 +10,7 @@ #include "include/core/SkPoint.h" #include "include/core/SkScalar.h" #include "include/private/base/SkAssert.h" +#include "include/private/base/SkDebug.h" #include "include/private/base/SkTo.h" #include "src/base/SkZip.h" #include "src/pdf/SkPDFDocumentPriv.h" @@ -204,6 +205,15 @@ SkPDFStructTree::~SkPDFStructTree() = default; void SkPDFStructTree::move(SkPDF::StructureElementNode& node, SkPDFStructElem* structElem, bool wantTitle) { + constexpr bool kDumpStructureTree = false; + if constexpr (kDumpStructureTree) { + int indent = 0; + for (SkPDFStructElem* parent = structElem->fParent; parent; parent = parent->fParent) { + ++indent; + } + SkDebugf("%.*s %d %s\n", indent, " ", node.fNodeId, node.fTypeString.c_str()); + } + structElem->fElemId = node.fNodeId; fStructElemForElemId.set(structElem->fElemId, structElem); @@ -315,7 +325,7 @@ SkPDFIndirectReference SkPDFStructElem::emitStructElem( dict.insertRef("P", parent); { // K - std::unique_ptr kids = SkPDFMakeArray(); + std::unique_ptr kids(new SkPDFOptionalArray()); for (auto&& child : fChildren) { if (child.fUsed) { kids->appendRef(child.emitStructElem(fRef, idTree, doc)); @@ -477,8 +487,9 @@ SkPDFIndirectReference SkPDFStructTree::emitStructTreeRoot(SkPDFDocument* doc) c return doc->emit(structTreeRoot, structTreeRootRef); } +namespace header_outline { namespace { -struct OutlineEntry { +struct Entry { struct Content { SkString fText; Location fLocation; @@ -492,7 +503,7 @@ struct OutlineEntry { int fHeaderLevel; SkPDFIndirectReference fRef; SkPDFIndirectReference fStructureRef; - std::vector fChildren = {}; + std::vector fChildren = {}; size_t fDescendentsEmitted = 0; void emitDescendents(SkPDFDocument* const doc) { @@ -513,7 +524,7 @@ struct OutlineEntry { destination->appendInt(0); entry.insertObject("Dest", std::move(destination)); - entry.insertRef("Parent", child.fRef); + entry.insertRef("Parent", fRef); if (child.fStructureRef) { entry.insertRef("SE", child.fStructureRef); } @@ -533,7 +544,7 @@ struct OutlineEntry { } }; -OutlineEntry::Content create_outline_entry_content(SkPDFStructElem* const structElem) { +Entry::Content create_header_content(SkPDFStructElem* const structElem) { SkString text; if (!structElem->fTitle.isEmpty()) { text = structElem->fTitle; @@ -547,27 +558,27 @@ OutlineEntry::Content create_outline_entry_content(SkPDFStructElem* const struct structElemLocation.accumulate(mark.fLocation); } - OutlineEntry::Content content{std::move(text), std::move(structElemLocation)}; + Entry::Content content{std::move(text), std::move(structElemLocation)}; // Accumulate children for (auto&& child : structElem->fChildren) { if (child.fUsed) { - content.accumulate(create_outline_entry_content(&child)); + content.accumulate(create_header_content(&child)); } } return content; } -void create_outline_from_headers(SkPDFDocument* const doc, SkPDFStructElem* const structElem, - STArray<7, OutlineEntry*>& stack) { + +void make(SkPDFDocument* const doc, SkPDFStructElem* const structElem, STArray<7, Entry*>& stack) { const SkString& type = structElem->fStructType; if (type.size() == 2 && type[0] == 'H' && '1' <= type[1] && type[1] <= '6') { int level = type[1] - '0'; while (level <= stack.back()->fHeaderLevel) { stack.pop_back(); } - OutlineEntry::Content content = create_outline_entry_content(structElem); + Entry::Content content = create_header_content(structElem); if (!content.fText.isEmpty()) { - OutlineEntry e{std::move(content), level, doc->reserveRef(), structElem->fRef}; + Entry e{std::move(content), level, doc->reserveRef(), structElem->fRef}; stack.push_back(&stack.back()->fChildren.emplace_back(std::move(e))); return; } @@ -575,33 +586,139 @@ void create_outline_from_headers(SkPDFDocument* const doc, SkPDFStructElem* cons for (auto&& child : structElem->fChildren) { if (child.fUsed) { - create_outline_from_headers(doc, &child, stack); + make(doc, &child, stack); } } } - } // namespace +} // namespace header_outline + +namespace structelem_outline { +namespace { +struct Entry { + size_t fDescendantCount = 0; + Location fLocation; + void accumulate(Entry const& child) { + fDescendantCount += child.fDescendantCount; + fLocation.accumulate(child.fLocation); + } +}; +Entry emit(SkPDFDocument* const doc, + SkPDFStructElem* const structElem, + SkPDFIndirectReference const parentRef, + SkPDFIndirectReference const prevSiblingRef, + SkPDFIndirectReference const selfRef, + SkPDFIndirectReference const nextSiblingRef) { + Entry self; + + // Emit any child entries. + STArray<20, SkPDFIndirectReference> childRefs; + for (auto&& child : structElem->fChildren) { + if (!child.fUsed) { + continue; + } + childRefs.emplace_back(doc->reserveRef()); + } + int childRefsIndex = 0; + SkPDFIndirectReference prevChildRef; // Starts out as "none". + childRefs.emplace_back(); // Put an extra "none" on the end for the last "next". + for (auto&& child : structElem->fChildren) { + if (!child.fUsed) { + continue; + } + SkPDFIndirectReference currChildRef = childRefs[childRefsIndex]; + SkPDFIndirectReference nextChildRef = childRefs[childRefsIndex+1]; + self.accumulate(emit(doc, &child, selfRef, prevChildRef, currChildRef, nextChildRef)); + prevChildRef = currChildRef; + ++childRefsIndex; + } + childRefs.pop_back(); // Remove the "none" on the end. + + // Emit self entry. + SkPDFDict entry; + if (!structElem->fTitle.isEmpty()) { + entry.insertTextString("Title", structElem->fTitle); + } else if (!structElem->fAlt.isEmpty()) { + entry.insertTextString("Title", structElem->fAlt); + } else { + entry.insertTextString("Title", structElem->fStructType); + } + + // The uppermost/leftmost point on the earliest page of this structure element's marks. + Location structElemLocation; + for (auto&& mark : structElem->fMarkedContent) { + structElemLocation.accumulate(mark.fLocation); + } + if (structElemLocation.fPoint.isFinite()) { + auto destination = SkPDFMakeArray(); + destination->appendRef(doc->getPage(structElemLocation.fPageIndex)); + destination->appendName("XYZ"); + destination->appendScalar(structElemLocation.fPoint.x()); + destination->appendScalar(structElemLocation.fPoint.y()); + destination->appendInt(0); + entry.insertObject("Dest", std::move(destination)); + + self.fLocation.accumulate(structElemLocation); + } else if (self.fLocation.fPoint.isFinite()) { + // The uppermost/leftmost point on the earliest page of any child. + auto destination = SkPDFMakeArray(); + destination->appendRef(doc->getPage(self.fLocation.fPageIndex)); + destination->appendName("XYZ"); + destination->appendScalar(self.fLocation.fPoint.x()); + destination->appendScalar(self.fLocation.fPoint.y()); + destination->appendInt(0); + entry.insertObject("Dest", std::move(destination)); + } + if (structElem->fRef) { + entry.insertRef("SE", structElem->fRef); + } + entry.insertRef("Parent", parentRef); + if (prevSiblingRef) { + entry.insertRef("Prev", prevSiblingRef); + } + if (nextSiblingRef) { + entry.insertRef("Next", nextSiblingRef); + } + if (!childRefs.empty()) { + entry.insertRef("First", childRefs.front()); + entry.insertRef("Last", childRefs.back()); + entry.insertInt("Count", self.fDescendantCount); + } + doc->emit(entry, selfRef); + ++self.fDescendantCount; + return self; +} +} // namespace +} // namespace structelem_outline SkPDFIndirectReference SkPDFStructTree::makeOutline(SkPDFDocument* doc) const { - if (!fRoot || !fRoot->fUsed || - fOutline != SkPDF::Metadata::Outline::StructureElementHeaders) - { + if (!fRoot || !fRoot->fUsed || fOutline == SkPDF::Metadata::Outline::None) { return SkPDFIndirectReference(); } - STArray<7, OutlineEntry*> stack; - OutlineEntry top{{SkString(), Location()}, 0, {}, {}}; - stack.push_back(&top); - create_outline_from_headers(doc, fRoot, stack); - if (top.fChildren.empty()) { - return SkPDFIndirectReference(); - } - top.emitDescendents(doc); SkPDFIndirectReference outlineRef = doc->reserveRef(); SkPDFDict outline("Outlines"); - outline.insertRef("First", top.fChildren.front().fRef); - outline.insertRef("Last", top.fChildren.back().fRef); - outline.insertInt("Count", top.fDescendentsEmitted); + if (fOutline == SkPDF::Metadata::Outline::StructureElements) { + SkPDFIndirectReference entryRef = doc->reserveRef(); + SkPDFIndirectReference none; + structelem_outline::Entry entry = structelem_outline::emit(doc, fRoot, outlineRef, + none, entryRef, none); + outline.insertRef("First", entryRef); + outline.insertRef("Last", entryRef); + outline.insertInt("Count", entry.fDescendantCount); + } else { + STArray<7, header_outline::Entry*> stack; + header_outline::Entry top{{SkString(), Location()}, 0, outlineRef, {}}; + stack.push_back(&top); + header_outline::make(doc, fRoot, stack); + if (top.fChildren.empty()) { + return SkPDFIndirectReference(); + } + top.emitDescendents(doc); + outline.insertRef("First", top.fChildren.front().fRef); + outline.insertRef("Last", top.fChildren.back().fRef); + outline.insertInt("Count", top.fDescendentsEmitted); + } return doc->emit(outline, outlineRef); } diff --git a/gfx/skia/skia/src/pdf/SkPDFTypes.cpp b/gfx/skia/skia/src/pdf/SkPDFTypes.cpp index c43955b047d3..ca81bdbf540e 100644 --- a/gfx/skia/skia/src/pdf/SkPDFTypes.cpp +++ b/gfx/skia/skia/src/pdf/SkPDFTypes.cpp @@ -391,6 +391,14 @@ void SkPDFArray::emitObject(SkWStream* stream) const { stream->writeText("]"); } +void SkPDFOptionalArray::emitObject(SkWStream* stream) const { + if (this->size() == 1) { + this->values()[0].emitObject(stream); + } else { + this->SkPDFArray::emitObject(stream); + } +} + void SkPDFArray::append(SkPDFUnion&& value) { fValues.emplace_back(std::move(value)); } @@ -561,17 +569,6 @@ static void serialize_stream(SkPDFDict* origDict, SkDeflateWStream deflateWStream(&compressedData,SkToInt(doc->metadata().fCompressionLevel)); SkStreamCopy(&deflateWStream, stream); deflateWStream.finalize(); - #ifdef SK_PDF_BASE85_BINARY - { - SkPDFUtils::Base85Encode(compressedData.detachAsStream(), &compressedData); - tmp = compressedData.detachAsStream(); - stream = tmp.get(); - auto filters = SkPDFMakeArray(); - filters->appendName("ASCII85Decode"); - filters->appendName("FlateDecode"); - dict.insertObject("Filter", std::move(filters)); - } - #else if (stream->getLength() > compressedData.bytesWritten() + kMinimumSavings) { tmp = compressedData.detachAsStream(); stream = tmp.get(); @@ -579,7 +576,6 @@ static void serialize_stream(SkPDFDict* origDict, } else { SkAssertResult(stream->rewind()); } - #endif } dict.insertInt("Length", stream->getLength()); diff --git a/gfx/skia/skia/src/pdf/SkPDFTypes.h b/gfx/skia/skia/src/pdf/SkPDFTypes.h index a0a3c80590dc..ac4537851878 100644 --- a/gfx/skia/skia/src/pdf/SkPDFTypes.h +++ b/gfx/skia/skia/src/pdf/SkPDFTypes.h @@ -9,6 +9,7 @@ #define SkPDFTypes_DEFINED #include "include/core/SkScalar.h" +#include "include/core/SkSpan.h" #include "include/core/SkTypes.h" #include "src/pdf/SkPDFUnion.h" @@ -23,6 +24,17 @@ class SkStreamAsset; class SkString; class SkWStream; +#ifndef SK_PDF_MASK_QUALITY + // If MASK_QUALITY is in [0,100], will be used for JpegEncoder. + // Otherwise, just encode masks losslessly. + #define SK_PDF_MASK_QUALITY 50 + // Since these masks are used for blurry shadows, we shouldn't need + // high quality. Raise this value if your shadows have visible JPEG + // artifacts. + // If SkJpegEncoder::Encode fails, we will fall back to the lossless + // encoding. +#endif + struct SkPDFIndirectReference { int fValue = -1; explicit operator bool() const { return fValue != -1; } @@ -69,7 +81,7 @@ private: An array object in a PDF. */ -class SkPDFArray final : public SkPDFObject { +class SkPDFArray : public SkPDFObject { public: /** Create a PDF array. Maximum length is 8191. */ @@ -104,6 +116,9 @@ public: void appendObject(std::unique_ptr&&); void appendRef(SkPDFIndirectReference); +protected: + SkSpan values() const { return SkSpan(fValues); } + private: std::vector fValues; void append(SkPDFUnion&& value); @@ -129,6 +144,15 @@ static inline std::unique_ptr SkPDFMakeArray(Args... args) { return ret; } +/** \class SkPDFOptionalArray + * + * An SkPDFArray which may be emitted as a non-array if it contains a single entry. + * Search the specification for "or an array" for where this can be used. + */ +class SkPDFOptionalArray final : public SkPDFArray { + void emitObject(SkWStream* stream) const override; +}; + /** \class SkPDFDict A dictionary object in a PDF. @@ -189,12 +213,6 @@ static inline std::unique_ptr SkPDFMakeDict(const char* type = nullpt enum class SkPDFSteamCompressionEnabled : bool { No = false, Yes = true, - Default = -#ifdef SK_PDF_LESS_COMPRESSION - No, -#else - Yes, -#endif }; // Exposed for unit testing. @@ -205,5 +223,5 @@ SkPDFIndirectReference SkPDFStreamOut( std::unique_ptr dict, std::unique_ptr stream, SkPDFDocument* doc, - SkPDFSteamCompressionEnabled compress = SkPDFSteamCompressionEnabled::Default); + SkPDFSteamCompressionEnabled compress = SkPDFSteamCompressionEnabled::Yes); #endif diff --git a/gfx/skia/skia/src/pdf/SkPDFUtils.cpp b/gfx/skia/skia/src/pdf/SkPDFUtils.cpp index 9d99885bc949..0447074f706a 100644 --- a/gfx/skia/skia/src/pdf/SkPDFUtils.cpp +++ b/gfx/skia/skia/src/pdf/SkPDFUtils.cpp @@ -359,43 +359,6 @@ bool SkPDFUtils::ToBitmap(const SkImage* img, SkBitmap* dst) { return false; } -#ifdef SK_PDF_BASE85_BINARY -void SkPDFUtils::Base85Encode(std::unique_ptr stream, SkDynamicMemoryWStream* dst) { - SkASSERT(dst); - SkASSERT(stream); - dst->writeText("\n"); - int column = 0; - while (true) { - uint8_t src[4] = {0, 0, 0, 0}; - size_t count = stream->read(src, 4); - SkASSERT(count < 5); - if (0 == count) { - dst->writeText("~>\n"); - return; - } - uint32_t v = ((uint32_t)src[0] << 24) | ((uint32_t)src[1] << 16) | - ((uint32_t)src[2] << 8) | src[3]; - if (v == 0 && count == 4) { - dst->writeText("z"); - column += 1; - } else { - char buffer[5]; - for (int n = 4; n > 0; --n) { - buffer[n] = (v % 85) + '!'; - v /= 85; - } - buffer[0] = v + '!'; - dst->write(buffer, count + 1); - column += count + 1; - } - if (column > 74) { - dst->writeText("\n"); - column = 0; - } - } -} -#endif // SK_PDF_BASE85_BINARY - void SkPDFUtils::AppendTransform(const SkMatrix& matrix, SkWStream* content) { SkScalar values[6]; if (!matrix.asAffine(values)) { diff --git a/gfx/skia/skia/src/pdf/SkPDFUtils.h b/gfx/skia/skia/src/pdf/SkPDFUtils.h index 58cc18fa9a25..1d7bef4dce2f 100644 --- a/gfx/skia/skia/src/pdf/SkPDFUtils.h +++ b/gfx/skia/skia/src/pdf/SkPDFUtils.h @@ -141,10 +141,6 @@ void PopulateTilingPatternDict(SkPDFDict* pattern, bool ToBitmap(const SkImage* img, SkBitmap* dst); -#ifdef SK_PDF_BASE85_BINARY -void Base85Encode(std::unique_ptr src, SkDynamicMemoryWStream* dst); -#endif // SK_PDF_BASE85_BINARY - void AppendTransform(const SkMatrix&, SkWStream*); // Takes SkTime::GetNSecs() [now] and puts it into the provided struct. diff --git a/gfx/skia/skia/src/ports/SkFontConfigTypeface.h b/gfx/skia/skia/src/ports/SkFontConfigTypeface.h index 86ae2ffb74d0..6ea63cba188b 100644 --- a/gfx/skia/skia/src/ports/SkFontConfigTypeface.h +++ b/gfx/skia/skia/src/ports/SkFontConfigTypeface.h @@ -13,22 +13,24 @@ #include "include/core/SkStream.h" #include "include/ports/SkFontConfigInterface.h" #include "src/core/SkFontDescriptor.h" -#include "src/ports/SkTypeface_FreeType.h" +#include "src/ports/SkTypeface_proxy.h" class SkFontDescriptor; -class SkTypeface_FCI : public SkTypeface_FreeType { - sk_sp fFCI; - SkFontConfigInterface::FontIdentity fIdentity; - SkString fFamilyName; - +class SkTypeface_FCI : public SkTypeface_proxy { public: - static SkTypeface_FCI* Create(sk_sp fci, + static SkTypeface_FCI* Create(sk_sp realTypeface, + sk_sp fci, const SkFontConfigInterface::FontIdentity& fi, SkString familyName, - const SkFontStyle& style) + const SkFontStyle& style, + const bool isFixedPitch) { - return new SkTypeface_FCI(std::move(fci), fi, std::move(familyName), style); + if (!realTypeface || !fci) { + return nullptr; + } + return new SkTypeface_FCI(std::move(realTypeface), std::move(fci), fi, + std::move(familyName), style, isFixedPitch); } const SkFontConfigInterface::FontIdentity& getIdentity() const { @@ -36,32 +38,50 @@ public: } sk_sp onMakeClone(const SkFontArguments& args) const override { - SkFontStyle style = this->fontStyle(); - std::unique_ptr data = this->cloneFontData(args, &style); - if (!data) { + sk_sp realTypeface = SkTypeface_proxy::onMakeClone(args); + if (!realTypeface) { return nullptr; } - return sk_sp(new SkTypeface_FreeTypeStream( - std::move(data), fFamilyName, style, this->isFixedPitch())); + return sk_sp(SkTypeface_FCI::Create( + std::move(realTypeface), + fFCI, + fIdentity, + fFamilyName, + this->fontStyle(), + this->isFixedPitch())); } protected: - SkTypeface_FCI(sk_sp fci, + SkTypeface_FCI(sk_sp realTypeface, + sk_sp fci, const SkFontConfigInterface::FontIdentity& fi, SkString familyName, - const SkFontStyle& style) - : INHERITED(style, false) + const SkFontStyle& style, + bool isFixedPitch) + : SkTypeface_proxy(std::move(realTypeface), style, isFixedPitch) , fFCI(std::move(fci)) , fIdentity(fi) - , fFamilyName(std::move(familyName)) {} + , fFamilyName(std::move(familyName)) { + } - void onGetFamilyName(SkString* familyName) const override { *familyName = fFamilyName; } void onGetFontDescriptor(SkFontDescriptor*, bool*) const override; std::unique_ptr onOpenStream(int* ttcIndex) const override; - std::unique_ptr onMakeFontData() const override; + void onGetFamilyName(SkString* familyName) const override { + *familyName = fFamilyName; + } + + SkFontStyle onGetFontStyle() const override { + return SkTypeface::onGetFontStyle(); + } + + bool onGetFixedPitch() const override { + return SkTypeface::onGetFixedPitch(); + } private: - using INHERITED = SkTypeface_FreeType; + sk_sp fFCI; + SkFontConfigInterface::FontIdentity fIdentity; + SkString fFamilyName; }; #endif // SkFontConfigTypeface_DEFINED diff --git a/gfx/skia/skia/src/ports/SkFontHost_FreeType.cpp b/gfx/skia/skia/src/ports/SkFontHost_FreeType.cpp index 7798b9132030..582efc07f711 100644 --- a/gfx/skia/skia/src/ports/SkFontHost_FreeType.cpp +++ b/gfx/skia/skia/src/ports/SkFontHost_FreeType.cpp @@ -17,7 +17,6 @@ #include "include/core/SkScalar.h" #include "include/core/SkStream.h" #include "include/core/SkString.h" -#include "include/private/SkColorData.h" #include "include/private/base/SkMalloc.h" #include "include/private/base/SkMutex.h" #include "include/private/base/SkTPin.h" @@ -25,6 +24,7 @@ #include "include/private/base/SkTo.h" #include "src/base/SkTSearch.h" #include "src/core/SkAdvancedTypefaceMetrics.h" +#include "src/core/SkColorData.h" #include "src/core/SkDescriptor.h" #include "src/core/SkFDot6.h" #include "src/core/SkFontDescriptor.h" @@ -137,10 +137,10 @@ static bool GetAxes(FT_Face face, SkFontScanner::AxisDefinitions* axes) { axes->reset(variations->num_axis); for (FT_UInt i = 0; i < variations->num_axis; ++i) { const FT_Var_Axis& ftAxis = variations->axis[i]; - (*axes)[i].fTag = ftAxis.tag; - (*axes)[i].fMinimum = SkFT_FixedToScalar(ftAxis.minimum); - (*axes)[i].fDefault = SkFT_FixedToScalar(ftAxis.def); - (*axes)[i].fMaximum = SkFT_FixedToScalar(ftAxis.maximum); + (*axes)[i].tag = ftAxis.tag; + (*axes)[i].min = SkFT_FixedToScalar(ftAxis.minimum); + (*axes)[i].def = SkFT_FixedToScalar(ftAxis.def); + (*axes)[i].max = SkFT_FixedToScalar(ftAxis.maximum); } } return true; @@ -314,11 +314,6 @@ void SkTypeface_FreeType::FaceRec::setupAxes(const SkFontData& data) { return; } - // If a named variation is requested, don't overwrite the named variation's position. - if (data.getIndex() > 0xFFFF) { - return; - } - SkDEBUGCODE( FT_MM_Var* variations = nullptr; if (FT_Get_MM_Var(fFace.get(), &variations)) { @@ -463,10 +458,10 @@ private: class SkScalerContext_FreeType : public SkScalerContext { public: - SkScalerContext_FreeType(sk_sp, + SkScalerContext_FreeType(const SkTypeface_FreeType& realTypeface, const SkScalerContextEffects&, const SkDescriptor* desc, - sk_sp); + SkTypeface& proxyTypeface); ~SkScalerContext_FreeType() override; bool success() const { @@ -722,17 +717,16 @@ std::unique_ptr SkTypeface_FreeType::onCreateScalerContext( std::unique_ptr SkTypeface_FreeType::onCreateScalerContextAsProxyTypeface( const SkScalerContextEffects& effects, const SkDescriptor* desc, - sk_sp realTypeface) const { - auto c = std::make_unique( - sk_ref_sp(const_cast(this)), + SkTypeface* proxyTypeface) const { + std::unique_ptr scalerContext(new SkScalerContext_FreeType( + *this, effects, desc, - realTypeface ? realTypeface : sk_ref_sp(const_cast(this))); - if (c->success()) { - return c; + proxyTypeface ? *proxyTypeface : *const_cast(this))); + if (scalerContext->success()) { + return scalerContext; } - return SkScalerContext::MakeEmpty( - sk_ref_sp(const_cast(this)), effects, desc); + return SkScalerContext::MakeEmpty(*const_cast(this), effects, desc); } /** Copy the design variation coordinates into 'coordinates'. @@ -746,14 +740,9 @@ std::unique_ptr SkTypeface_FreeType::onCreateScalerContextAsPro * variation space. It is possible the number of axes can be retrieved but actual position * cannot. */ -static int GetVariationDesignPosition(AutoFTAccess& fta, +static int GetVariationDesignPosition(FT_Face face, SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) { - FT_Face face = fta.face(); - if (!face) { - return -1; - } - if (!(face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS)) { return 0; } @@ -795,16 +784,17 @@ std::unique_ptr SkTypeface_FreeType::cloneFontData(const SkFontArgum int axisCount = axisDefinitions.size(); AutoSTMalloc<4, SkFontArguments::VariationPosition::Coordinate> currentPosition(axisCount); - int currentAxisCount = GetVariationDesignPosition(fta, currentPosition, axisCount); + int currentAxisCount = GetVariationDesignPosition(face, currentPosition, axisCount); SkString name; AutoSTMalloc<4, SkFixed> axisValues(axisCount); SkFontScanner_FreeType::computeAxisValues( axisDefinitions, + currentAxisCount == axisCount + ? SkFontArguments::VariationPosition{currentPosition.data(), currentAxisCount} + : SkFontArguments::VariationPosition{nullptr, 0}, args.getVariationDesignPosition(), - axisValues, - name, style, - currentAxisCount == axisCount ? currentPosition.get() : nullptr); + axisValues, name, style); int ttcIndex; std::unique_ptr stream = this->openStream(&ttcIndex); @@ -874,7 +864,7 @@ int SkTypeface_FreeType::onGetUPEM() const { return GetUnitsPerEm(face); } -bool SkTypeface_FreeType::onGetKerningPairAdjustments(const uint16_t glyphs[], +bool SkTypeface_FreeType::onGetKerningPairAdjustments(const SkGlyphID glyphs[], int count, int32_t adjustments[]) const { AutoFTAccess fta(this); FT_Face face = fta.face(); @@ -926,17 +916,17 @@ static FT_Int chooseBitmapStrike(FT_Face face, FT_F26Dot6 scaleY) { return chosenStrikeIndex; } -SkScalerContext_FreeType::SkScalerContext_FreeType(sk_sp proxyTypeface, +SkScalerContext_FreeType::SkScalerContext_FreeType(const SkTypeface_FreeType& realTypeface, const SkScalerContextEffects& effects, const SkDescriptor* desc, - sk_sp realTypeface) - : SkScalerContext(realTypeface, effects, desc) + SkTypeface& proxyTypeface) + : SkScalerContext(proxyTypeface, effects, desc) , fFace(nullptr) , fFTSize(nullptr) , fStrikeIndex(-1) { SkAutoMutexExclusive ac(f_t_mutex()); - fFaceRec = proxyTypeface->getFaceRec(); + fFaceRec = realTypeface.getFaceRec(); // The proxyTypeface owns the realTypeface. // load the font file if (nullptr == fFaceRec) { @@ -1735,8 +1725,6 @@ bool SkScalerContext_FreeType::emboldenIfNeeded(FT_Face face, FT_GlyphSlot glyph /////////////////////////////////////////////////////////////////////////////// -#include "src/base/SkUtils.h" - SkTypeface_FreeType::SkTypeface_FreeType(const SkFontStyle& style, bool isFixedPitch) : INHERITED(style, isFixedPitch) {} @@ -1766,7 +1754,7 @@ void SkTypeface_FreeType::onCharsToGlyphs(const SkUnichar uni[], int count, if (index < 0) { break; } - glyphs[i] = SkToU16(index); + glyphs[i] = SkTo(index); } if (i == count) { // we're done, no need to access the freetype objects @@ -1787,9 +1775,9 @@ void SkTypeface_FreeType::onCharsToGlyphs(const SkUnichar uni[], int count, SkUnichar c = uni[i]; int index = fC2GCache.findGlyphIndex(c); if (index >= 0) { - glyphs[i] = SkToU16(index); + glyphs[i] = SkTo(index); } else { - glyphs[i] = SkToU16(FT_Get_Char_Index(face, c)); + glyphs[i] = SkTo(FT_Get_Char_Index(face, c)); fC2GCache.insertCharAndGlyph(~index, c, glyphs[i]); } } @@ -1833,7 +1821,11 @@ int SkTypeface_FreeType::onGetVariationDesignPosition( SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const { AutoFTAccess fta(this); - return GetVariationDesignPosition(fta, coordinates, coordinateCount); + FT_Face face = fta.face(); + if (!face) { + return -1; + } + return GetVariationDesignPosition(face, coordinates, coordinateCount); } int SkTypeface_FreeType::onGetVariationDesignParameters( @@ -2037,14 +2029,18 @@ sk_sp SkTypeface_FreeType::MakeFromStream(std::unique_ptr axisValues(axisDefinitions.size()); - SkFontScanner_FreeType::computeAxisValues(axisDefinitions, position, axisValues, name, &style); + SkFontScanner_FreeType::computeAxisValues( + axisDefinitions, + SkFontArguments::VariationPosition{current.data(), current.size()}, + args.getVariationDesignPosition(), + axisValues, name, &style); auto data = std::make_unique( std::move(stream), args.getCollectionIndex(), args.getPalette().index, @@ -2109,8 +2105,9 @@ bool SkFontScanner_FreeType::scanFile(SkStreamAsset* stream, int* numFaces) cons if (!face) { return false; } - - *numFaces = face->num_faces; + if (numFaces) { + *numFaces = face->num_faces; + } return true; } @@ -2124,8 +2121,9 @@ bool SkFontScanner_FreeType::scanFace(SkStreamAsset* stream, if (!face) { return false; } - - *numInstances = face->style_flags >> 16; + if (numInstances) { + *numInstances = face->style_flags >> 16; + } return true; } @@ -2135,7 +2133,8 @@ bool SkFontScanner_FreeType::scanInstance(SkStreamAsset* stream, SkString* name, SkFontStyle* style, bool* isFixedPitch, - AxisDefinitions* axes) const { + AxisDefinitions* axes, + VariationPosition* position) const { SkAutoMutexExclusive libraryLock(fLibraryMutex); @@ -2183,21 +2182,21 @@ bool SkFontScanner_FreeType::scanInstance(SkStreamAsset* stream, std::optional wdthIndex; std::optional slntIndex; for(size_t i = 0; i < numAxes; ++i) { - if (axisDefinitions[i].fTag == wghtTag) { + if (axisDefinitions[i].tag == wghtTag) { // Rough validity check, sufficient spread and ranges within 0-1000. - SkScalar wghtRange = axisDefinitions[i].fMaximum - axisDefinitions[i].fMinimum; - if (wghtRange > 5 && wghtRange <= 1000 && axisDefinitions[i].fMaximum <= 1000) { + SkScalar wghtRange = axisDefinitions[i].max - axisDefinitions[i].min; + if (wghtRange > 5 && wghtRange <= 1000 && axisDefinitions[i].max <= 1000) { wghtIndex = i; } } - if (axisDefinitions[i].fTag == wdthTag) { + if (axisDefinitions[i].tag == wdthTag) { // Rough validity check, sufficient spread and are ranges within 0-500. - SkScalar wdthRange = axisDefinitions[i].fMaximum - axisDefinitions[i].fMinimum; - if (wdthRange > 0 && wdthRange <= 500 && axisDefinitions[i].fMaximum <= 500) { + SkScalar wdthRange = axisDefinitions[i].max - axisDefinitions[i].min; + if (wdthRange > 0 && wdthRange <= 500 && axisDefinitions[i].max <= 500) { wdthIndex = i; } } - if (axisDefinitions[i].fTag == slntTag) { + if (axisDefinitions[i].tag == slntTag) { slntIndex = i; } } @@ -2224,6 +2223,14 @@ bool SkFontScanner_FreeType::scanInstance(SkStreamAsset* stream, } } } + + if (position) { + position->reset(numAxes); + auto coordinates = position->data(); + if (GetVariationDesignPosition(face.get(), coordinates, numAxes) != (int)numAxes) { + return false; + } + } } } @@ -2281,6 +2288,7 @@ bool SkFontScanner_FreeType::scanInstance(SkStreamAsset* stream, if (axes != nullptr && !GetAxes(face.get(), axes)) { return false; } + return true; } @@ -2294,12 +2302,12 @@ SkTypeface::FactoryId SkFontScanner_FreeType::getFactoryId() const { } /*static*/ void SkFontScanner_FreeType::computeAxisValues( - AxisDefinitions axisDefinitions, + const AxisDefinitions& axisDefinitions, + const SkFontArguments::VariationPosition current, const SkFontArguments::VariationPosition position, SkFixed* axisValues, const SkString& name, - SkFontStyle* style, - const SkFontArguments::VariationPosition::Coordinate* current) + SkFontStyle* style) { static constexpr SkFourByteTag wghtTag = SkSetFourByteTag('w', 'g', 'h', 't'); static constexpr SkFourByteTag wdthTag = SkSetFourByteTag('w', 'd', 't', 'h'); @@ -2314,22 +2322,31 @@ SkTypeface::FactoryId SkFontScanner_FreeType::getFactoryId() const { } for (int i = 0; i < axisDefinitions.size(); ++i) { - const AxisDefinition& axisDefinition = axisDefinitions[i]; - const SkScalar axisMin = axisDefinition.fMinimum; - const SkScalar axisMax = axisDefinition.fMaximum; + const SkFontParameters::Variation::Axis& axisDefinition = axisDefinitions[i]; + const SkScalar axisMin = axisDefinition.min; + const SkScalar axisMax = axisDefinition.max; // Start with the default value. - axisValues[i] = SkScalarToFixed(axisDefinition.fDefault); + axisValues[i] = SkScalarToFixed(axisDefinition.def); // Then the current value. - if (current) { - for (int j = 0; j < axisDefinitions.size(); ++j) { - const auto& coordinate = current[j]; - if (axisDefinition.fTag == coordinate.axis) { - const SkScalar axisValue = SkTPin(coordinate.value, axisMin, axisMax); - axisValues[i] = SkScalarToFixed(axisValue); - break; + for (int j = current.coordinateCount; j --> 0;) { + const auto& coordinate = current.coordinates[j]; + if (axisDefinition.tag == coordinate.axis) { + const SkScalar axisValue = SkTPin(coordinate.value, axisMin, axisMax); + if (coordinate.value != axisValue) { + LOG_INFO("Current font axis value out of range: " + "%s '%c%c%c%c' %f; pinned to %f.\n", + name.c_str(), + (axisDefinition.tag >> 24) & 0xFF, + (axisDefinition.tag >> 16) & 0xFF, + (axisDefinition.tag >> 8) & 0xFF, + (axisDefinition.tag ) & 0xFF, + SkScalarToDouble(coordinate.value), + SkScalarToDouble(axisValue)); } + axisValues[i] = SkScalarToFixed(axisValue); + break; } } @@ -2338,16 +2355,16 @@ SkTypeface::FactoryId SkFontScanner_FreeType::getFactoryId() const { // use the last one since that's what css-fonts-4 requires. for (int j = position.coordinateCount; j --> 0;) { const auto& coordinate = position.coordinates[j]; - if (axisDefinition.fTag == coordinate.axis) { + if (axisDefinition.tag == coordinate.axis) { const SkScalar axisValue = SkTPin(coordinate.value, axisMin, axisMax); if (coordinate.value != axisValue) { LOG_INFO("Requested font axis value out of range: " "%s '%c%c%c%c' %f; pinned to %f.\n", name.c_str(), - (axisDefinition.fTag >> 24) & 0xFF, - (axisDefinition.fTag >> 16) & 0xFF, - (axisDefinition.fTag >> 8) & 0xFF, - (axisDefinition.fTag ) & 0xFF, + (axisDefinition.tag >> 24) & 0xFF, + (axisDefinition.tag >> 16) & 0xFF, + (axisDefinition.tag >> 8) & 0xFF, + (axisDefinition.tag ) & 0xFF, SkScalarToDouble(coordinate.value), SkScalarToDouble(axisValue)); } @@ -2357,14 +2374,14 @@ SkTypeface::FactoryId SkFontScanner_FreeType::getFactoryId() const { } if (style) { - if (axisDefinition.fTag == wghtTag) { + if (axisDefinition.tag == wghtTag) { // Rough validity check, is there sufficient spread and are ranges within 0-1000. SkScalar wghtRange = axisMax - axisMin; if (wghtRange > 5 && wghtRange <= 1000 && axisMax <= 1000) { weight = SkFixedRoundToInt(axisValues[i]); } } - if (axisDefinition.fTag == wdthTag) { + if (axisDefinition.tag == wdthTag) { // Rough validity check, is there a spread and are ranges within 0-500. SkScalar wdthRange = axisMax - axisMin; if (wdthRange > 0 && wdthRange <= 500 && axisMax <= 500) { @@ -2372,7 +2389,7 @@ SkTypeface::FactoryId SkFontScanner_FreeType::getFactoryId() const { width = SkFontDescriptor::SkFontStyleWidthForWidthAxisValue(wdthValue); } } - if (axisDefinition.fTag == slntTag && slant != SkFontStyle::kItalic_Slant) { + if (axisDefinition.tag == slntTag && slant != SkFontStyle::kItalic_Slant) { // https://docs.microsoft.com/en-us/typography/opentype/spec/dvaraxistag_slnt // "Scale interpretation: Values can be interpreted as the angle, // in counter-clockwise degrees, of oblique slant from whatever @@ -2397,7 +2414,7 @@ SkTypeface::FactoryId SkFontScanner_FreeType::getFactoryId() const { SkFourByteTag skTag = position.coordinates[i].axis; bool found = false; for (int j = 0; j < axisDefinitions.size(); ++j) { - if (skTag == axisDefinitions[j].fTag) { + if (skTag == axisDefinitions[j].tag) { found = true; break; } diff --git a/gfx/skia/skia/src/ports/SkFontHost_FreeType_common.cpp b/gfx/skia/skia/src/ports/SkFontHost_FreeType_common.cpp index 97b40549a5e4..7bad5ef0eca9 100644 --- a/gfx/skia/skia/src/ports/SkFontHost_FreeType_common.cpp +++ b/gfx/skia/skia/src/ports/SkFontHost_FreeType_common.cpp @@ -18,8 +18,8 @@ #include "include/core/SkPath.h" #include "include/effects/SkGradientShader.h" #include "include/pathops/SkPathOps.h" -#include "include/private/SkColorData.h" #include "include/private/base/SkTo.h" +#include "src/core/SkColorData.h" #include "src/core/SkFDot6.h" #include "src/core/SkSwizzlePriv.h" #include "src/core/SkTHash.h" @@ -1236,9 +1236,9 @@ void colrv1_transform(FT_Face face, bool colrv1_start_glyph(SkCanvas* canvas, const SkSpan& palette, - const SkColor foregroundColor, + SkColor foregroundColor, FT_Face face, - uint16_t glyphId, + SkGlyphID glyphId, FT_Color_Root_Transform rootTransform, VisitedSet* activePaints); @@ -1347,7 +1347,7 @@ bool colrv1_traverse_paint(SkCanvas* canvas, SkUNREACHABLE; } -SkPath GetClipBoxPath(FT_Face face, uint16_t glyphId, bool untransformed) { +SkPath GetClipBoxPath(FT_Face face, SkGlyphID glyphId, bool untransformed) { SkPath resultPath; SkUniqueFTSize unscaledFtSize = nullptr; FT_Size oldSize = face->size; @@ -1412,7 +1412,7 @@ SkPath GetClipBoxPath(FT_Face face, uint16_t glyphId, bool untransformed) { bool colrv1_start_glyph(SkCanvas* canvas, const SkSpan& palette, - const SkColor foregroundColor, + SkColor foregroundColor, FT_Face face, uint16_t glyphId, FT_Color_Root_Transform rootTransform, @@ -1439,7 +1439,7 @@ bool colrv1_start_glyph(SkCanvas* canvas, bool colrv1_start_glyph_bounds(SkMatrix *ctm, SkRect* bounds, FT_Face face, - uint16_t glyphId, + SkGlyphID glyphId, FT_Color_Root_Transform rootTransform, VisitedSet* activePaints); diff --git a/gfx/skia/skia/src/ports/SkFontHost_cairo.cpp b/gfx/skia/skia/src/ports/SkFontHost_cairo.cpp index c55147a54c14..d5a4a2c6eec8 100644 --- a/gfx/skia/skia/src/ports/SkFontHost_cairo.cpp +++ b/gfx/skia/skia/src/ports/SkFontHost_cairo.cpp @@ -100,7 +100,7 @@ void SkInitCairoFT(bool fontHintingEnabled) class SkScalerContext_CairoFT : public SkScalerContext { public: - SkScalerContext_CairoFT(sk_sp typeface, + SkScalerContext_CairoFT(SkTypeface& typeface, const SkScalerContextEffects& effects, const SkDescriptor* desc, FT_Face face, void* faceContext, SkPixelGeometry pixelGeometry, @@ -185,7 +185,7 @@ public: std::unique_ptr onCreateScalerContext(const SkScalerContextEffects& effects, const SkDescriptor* desc) const override { SkScalerContext_CairoFT* ctx = new SkScalerContext_CairoFT( - sk_ref_sp(const_cast(this)), effects, desc, + *const_cast(this), effects, desc, fFTFace, fFTFaceContext, fPixelGeometry, fLcdFilter); std::unique_ptr result(ctx); if (!ctx->isValid()) { @@ -332,10 +332,10 @@ SkTypeface* SkCreateTypefaceFromCairoFTFont(FT_Face face, void* faceContext, } SkScalerContext_CairoFT::SkScalerContext_CairoFT( - sk_sp typeface, const SkScalerContextEffects& effects, + SkTypeface& typeface, const SkScalerContextEffects& effects, const SkDescriptor* desc, FT_Face face, void* faceContext, SkPixelGeometry pixelGeometry, FT_LcdFilter lcdFilter) - : SkScalerContext(std::move(typeface), effects, desc) + : SkScalerContext(typeface, effects, desc) , fFTFace(face) , fFTFaceContext(faceContext) , fLcdFilter(lcdFilter) diff --git a/gfx/skia/skia/src/ports/SkFontHost_win.cpp b/gfx/skia/skia/src/ports/SkFontHost_win.cpp index 6d1f5ee3c0dc..308c5a05dbca 100644 --- a/gfx/skia/skia/src/ports/SkFontHost_win.cpp +++ b/gfx/skia/skia/src/ports/SkFontHost_win.cpp @@ -16,7 +16,6 @@ #include "include/core/SkString.h" #include "include/ports/SkTypeface_win.h" #include "src/ports/SkTypeface_win_dw.h" -#include "include/private/SkColorData.h" #include "include/private/base/SkMacros.h" #include "include/private/base/SkOnce.h" #include "include/private/base/SkTDArray.h" @@ -26,6 +25,7 @@ #include "src/base/SkLeanWindows.h" #include "src/base/SkUTF.h" #include "src/core/SkAdvancedTypefaceMetrics.h" +#include "src/core/SkColorData.h" #include "src/core/SkDescriptor.h" #include "src/core/SkFontDescriptor.h" #include "src/core/SkGlyph.h" @@ -579,7 +579,7 @@ void* HDCOffscreen::draw(const SkGlyph& glyph, bool isBW, size_t* srcRBPtr) { class SkScalerContext_GDI : public SkScalerContext { public: - SkScalerContext_GDI(sk_sp, + SkScalerContext_GDI(LogFontTypeface&, const SkScalerContextEffects&, const SkDescriptor* desc); ~SkScalerContext_GDI() override; @@ -655,10 +655,10 @@ static BYTE compute_quality(const SkScalerContextRec& rec) { } } -SkScalerContext_GDI::SkScalerContext_GDI(sk_sp rawTypeface, +SkScalerContext_GDI::SkScalerContext_GDI(LogFontTypeface& rawTypeface, const SkScalerContextEffects& effects, const SkDescriptor* desc) - : SkScalerContext(std::move(rawTypeface), effects, desc) + : SkScalerContext(rawTypeface, effects, desc) , fDDC(nullptr) , fSavefont(nullptr) , fFont(nullptr) @@ -1039,7 +1039,7 @@ static const uint8_t* getInverseGammaTableClearType() { return gTableClearType; } -#include "include/private/SkColorData.h" +#include "src/core/SkColorData.h" //Cannot assume that the input rgb is gray due to possible setting of kGenA8FromLCD_Flag. template @@ -2091,7 +2091,7 @@ std::unique_ptr LogFontTypeface::onCreateScalerContext( const SkScalerContextEffects& effects, const SkDescriptor* desc) const { auto ctx = std::make_unique( - sk_ref_sp(const_cast(this)), effects, desc); + *const_cast(this), effects, desc); if (ctx->isValid()) { return std::move(ctx); } @@ -2099,13 +2099,13 @@ std::unique_ptr LogFontTypeface::onCreateScalerContext( ctx.reset(); SkStrikeCache::PurgeAll(); ctx = std::make_unique( - sk_ref_sp(const_cast(this)), effects, desc); + *const_cast(this), effects, desc); if (ctx->isValid()) { return std::move(ctx); } return SkScalerContext::MakeEmpty( - sk_ref_sp(const_cast(this)), effects, desc); + *const_cast(this), effects, desc); } void LogFontTypeface::onFilterRec(SkScalerContextRec* rec) const { diff --git a/gfx/skia/skia/src/ports/SkFontMgr_FontConfigInterface.cpp b/gfx/skia/skia/src/ports/SkFontMgr_FontConfigInterface.cpp index 57592b27043d..2359f4e5626f 100644 --- a/gfx/skia/skia/src/ports/SkFontMgr_FontConfigInterface.cpp +++ b/gfx/skia/skia/src/ports/SkFontMgr_FontConfigInterface.cpp @@ -6,11 +6,13 @@ */ #include "include/core/SkFontMgr.h" +#include "include/core/SkFontScanner.h" #include "include/core/SkFontStyle.h" #include "include/core/SkString.h" #include "include/core/SkTypeface.h" #include "include/ports/SkFontConfigInterface.h" #include "include/ports/SkFontMgr_FontConfigInterface.h" +#include "include/ports/SkFontScanner_FreeType.h" #include "include/private/base/SkMutex.h" #include "src/core/SkFontDescriptor.h" #include "src/core/SkResourceCache.h" @@ -27,18 +29,12 @@ std::unique_ptr SkTypeface_FCI::onOpenStream(int* ttcIndex) const return std::unique_ptr(fFCI->openStream(this->getIdentity())); } -std::unique_ptr SkTypeface_FCI::onMakeFontData() const { - const SkFontConfigInterface::FontIdentity& id = this->getIdentity(); - return std::make_unique(std::unique_ptr(fFCI->openStream(id)), - id.fTTCIndex, 0, nullptr, 0, nullptr, 0); -} - void SkTypeface_FCI::onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) const { + SkTypeface_proxy::onGetFontDescriptor(desc, serialize); SkString name; this->getFamilyName(&name); desc->setFamilyName(name.c_str()); desc->setStyle(this->fontStyle()); - desc->setFactoryId(SkTypeface_FreeType::FactoryId); *serialize = true; } @@ -145,6 +141,7 @@ static bool find_by_FontIdentity(SkTypeface* cachedTypeface, void* ctx) { class SkFontMgr_FCI : public SkFontMgr { sk_sp fFCI; + std::unique_ptr fScanner; mutable SkMutex fMutex; mutable SkTypefaceCache fTFCache; @@ -155,8 +152,9 @@ class SkFontMgr_FCI : public SkFontMgr { mutable SkFontRequestCache fCache; public: - SkFontMgr_FCI(sk_sp fci) + SkFontMgr_FCI(sk_sp fci, std::unique_ptr scanner) : fFCI(std::move(fci)) + , fScanner(std::move(scanner)) , fCache(kMaxSize) { SkASSERT_RELEASE(fFCI); @@ -204,7 +202,11 @@ protected: // Check if a typeface with this FontIdentity is already in the FontIdentity cache. face = fTFCache.findByProcAndRef(find_by_FontIdentity, &identity); if (!face) { - face.reset(SkTypeface_FCI::Create(fFCI, identity, std::move(outFamilyName), outStyle)); + sk_sp realTypeface = fScanner->MakeFromStream( + std::unique_ptr(fFCI->openStream(identity)), + SkFontArguments().setCollectionIndex(identity.fTTCIndex)); + face.reset(SkTypeface_FCI::Create(std::move(realTypeface), fFCI, identity, + std::move(outFamilyName), outStyle, false)); // Add this FontIdentity to the FontIdentity cache. fTFCache.add(face); } @@ -254,7 +256,13 @@ protected: } }; +SK_API sk_sp SkFontMgr_New_FCI(sk_sp fci, + std::unique_ptr scanner) { + SkASSERT(fci); + return sk_make_sp(std::move(fci), std::move(scanner)); +} + SK_API sk_sp SkFontMgr_New_FCI(sk_sp fci) { SkASSERT(fci); - return sk_make_sp(std::move(fci)); + return sk_make_sp(std::move(fci), SkFontScanner_Make_FreeType()); } diff --git a/gfx/skia/skia/src/ports/SkFontMgr_android.cpp b/gfx/skia/skia/src/ports/SkFontMgr_android.cpp index 278e6df42b20..d651fb57a253 100644 --- a/gfx/skia/skia/src/ports/SkFontMgr_android.cpp +++ b/gfx/skia/skia/src/ports/SkFontMgr_android.cpp @@ -23,10 +23,10 @@ #include "src/base/SkTSearch.h" #include "src/core/SkFontDescriptor.h" #include "src/core/SkOSFile.h" +#include "src/core/SkTHash.h" #include "src/core/SkTypefaceCache.h" #include "src/ports/SkFontMgr_android_parser.h" -#include "src/ports/SkFontScanner_FreeType_priv.h" -#include "src/ports/SkTypeface_FreeType.h" +#include "src/ports/SkTypeface_proxy.h" #include #include @@ -36,95 +36,81 @@ using namespace skia_private; class SkData; namespace { -class SkTypeface_AndroidSystem : public SkTypeface_FreeType { +class SkTypeface_AndroidSystem : public SkTypeface_proxy { public: - SkTypeface_AndroidSystem(const SkString& pathName, - const bool cacheFontFiles, - int index, - const SkFixed* axes, int axesCount, + static sk_sp Make(sk_sp realTypeface, + const SkFontStyle& style, + bool isFixedPitch, + const SkString& familyName, + const TArray& lang, + FontVariant variantStyle) { + SkASSERT(realTypeface); + return sk_sp(new SkTypeface_AndroidSystem(std::move(realTypeface), + style, isFixedPitch, + familyName, lang, variantStyle)); + } + + const SkString fFamilyName; + const STArray<4, SkFixed, true> fAxes; + const STArray<4, SkLanguage, true> fLang; + const FontVariant fVariantStyle; + +protected: + SkTypeface_AndroidSystem(sk_sp realTypeface, const SkFontStyle& style, bool isFixedPitch, const SkString& familyName, const TArray& lang, FontVariant variantStyle) - : INHERITED(style, isFixedPitch) - , fPathName(pathName) - , fFamilyName(familyName) - , fIndex(index) - , fAxes(axes, axesCount) - , fLang(lang) - , fVariantStyle(variantStyle) - , fFile(cacheFontFiles ? sk_fopen(fPathName.c_str(), kRead_SkFILE_Flag) : nullptr) { - if (cacheFontFiles) { - SkASSERT(fFile); - } - } - - std::unique_ptr makeStream() const { - if (fFile) { - sk_sp data(SkData::MakeFromFILE(fFile)); - return data ? std::make_unique(std::move(data)) : nullptr; - } - return SkStream::MakeFromFile(fPathName.c_str()); - } + : SkTypeface_proxy(std::move(realTypeface), style, isFixedPitch) + , fFamilyName(familyName) + , fLang(lang) + , fVariantStyle(variantStyle) + {} void onGetFamilyName(SkString* familyName) const override { *familyName = fFamilyName; } + sk_sp onMakeClone(const SkFontArguments& args) const override { + auto proxy = SkTypeface_proxy::onMakeClone(args); + if (proxy == nullptr) { + return nullptr; + } + return SkTypeface_AndroidSystem::Make(std::move(proxy), + this->fontStyle(), this->isFixedPitch(), + fFamilyName, fLang, fVariantStyle); + } + void onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) const override { + SkTypeface_proxy::onGetFontDescriptor(desc, serialize); + SkASSERT(desc); SkASSERT(serialize); desc->setFamilyName(fFamilyName.c_str()); desc->setStyle(this->fontStyle()); - desc->setFactoryId(SkTypeface_FreeType::FactoryId); *serialize = false; } - std::unique_ptr onOpenStream(int* ttcIndex) const override { - *ttcIndex = fIndex; - return this->makeStream(); - } - std::unique_ptr onMakeFontData() const override { - return std::make_unique( - this->makeStream(), fIndex, 0, fAxes.begin(), fAxes.size(), nullptr, 0); - } - sk_sp onMakeClone(const SkFontArguments& args) const override { - SkFontStyle style = this->fontStyle(); - std::unique_ptr data = this->cloneFontData(args, &style); - if (!data) { - return nullptr; - } - return sk_make_sp(fPathName, - fFile, - fIndex, - data->getAxis(), - data->getAxisCount(), - style, - this->isFixedPitch(), - fFamilyName, - fLang, - fVariantStyle); + + SkFontStyle onGetFontStyle() const override { + return SkTypeface::onGetFontStyle(); } - const SkString fPathName; - const SkString fFamilyName; - int fIndex; - const STArray<4, SkFixed, true> fAxes; - const STArray<4, SkLanguage, true> fLang; - const FontVariant fVariantStyle; - SkAutoTCallVProc fFile; - - using INHERITED = SkTypeface_FreeType; + bool onGetFixedPitch() const override { + return SkTypeface::onGetFixedPitch(); + } }; template sk_sp sk_sp_static_cast(sk_sp&& s) { return sk_sp(static_cast(s.release())); } +using StreamForPathCache = skia_private::THashMap>; + class SkFontStyleSet_Android : public SkFontStyleSet { public: explicit SkFontStyleSet_Android(const FontFamily& family, const SkFontScanner* scanner, - const bool cacheFontFiles) { + const bool cacheFontFiles, StreamForPathCache& streamForPath) { const SkString* cannonicalFamilyName = nullptr; if (!family.fNames.empty()) { cannonicalFamilyName = &family.fNames[0]; @@ -138,43 +124,34 @@ public: SkString pathName(family.fBasePath); pathName.append(fontFile.fFileName); - std::unique_ptr stream = SkStream::MakeFromFile(pathName.c_str()); + std::unique_ptr* streamPtr = streamForPath.find(pathName); + if (!streamPtr) { + streamPtr = streamForPath.set(pathName, SkStream::MakeFromFile(pathName.c_str())); + } + if (!*streamPtr) { + SkDEBUGF("Requested font file %s cannot be opened.\n", pathName.c_str()); + continue; + } + std::unique_ptr stream = (*streamPtr)->duplicate(); if (!stream) { - SkDEBUGF("Requested font file %s does not exist or cannot be opened.\n", - pathName.c_str()); + SkDEBUGF("Requested font file %s could not be duplicated.\n", pathName.c_str()); continue; } - const int ttcIndex = fontFile.fIndex; - SkString familyName; - SkFontStyle style; - bool isFixedWidth; - SkFontScanner::AxisDefinitions axisDefinitions; - if (!scanner->scanInstance(stream.get(), ttcIndex, 0, - &familyName, &style, &isFixedWidth, &axisDefinitions)) - { - SkDEBUGF("Requested font file %s exists, but is not a valid font.\n", - pathName.c_str()); - continue; - } - - AutoSTMalloc<4, SkFixed> axisValues(axisDefinitions.size()); SkFontArguments::VariationPosition position = { - fontFile.fVariationDesignPosition.begin(), - fontFile.fVariationDesignPosition.size() + fontFile.fVariationDesignPosition.begin(), + fontFile.fVariationDesignPosition.size() }; - SkFontScanner_FreeType::computeAxisValues( - axisDefinitions, position, axisValues, familyName, &style); - - int weight = fontFile.fWeight != 0 ? fontFile.fWeight : style.weight(); - SkFontStyle::Slant slant = style.slant(); - switch (fontFile.fStyle) { - case FontFileInfo::Style::kAuto: slant = style.slant(); break; - case FontFileInfo::Style::kNormal: slant = SkFontStyle::kUpright_Slant; break; - case FontFileInfo::Style::kItalic: slant = SkFontStyle::kItalic_Slant; break; - default: SkASSERT(false); break; + // TODO: this creates the proxy with the given stream, so always cacheFontFiles. + auto proxy = scanner->MakeFromStream( + std::move(stream), + SkFontArguments().setCollectionIndex(fontFile.fIndex) + .setVariationDesignPosition(position)); + if (!proxy) { + SkDEBUGF("Requested font file %s does not have valid font data.\n", + pathName.c_str()); + continue; } - style = SkFontStyle(weight, style.width(), slant); uint32_t variant = family.fVariant; if (kDefault_FontVariant == variant) { @@ -184,13 +161,27 @@ public: // The first specified family name overrides the family name found in the font. // TODO: SkTypeface_AndroidSystem::onCreateFamilyNameIterator should return // all of the specified family names in addition to the names found in the font. + SkString familyName; + proxy->getFamilyName(&familyName); if (cannonicalFamilyName != nullptr) { familyName = *cannonicalFamilyName; } - fStyles.push_back().reset(new SkTypeface_AndroidSystem( - pathName, cacheFontFiles, ttcIndex, axisValues.get(), axisDefinitions.size(), - style, isFixedWidth, familyName, family.fLanguages, variant)); + SkFontStyle fontStyle = proxy->fontStyle(); + int weight = fontFile.fWeight != 0 ? fontFile.fWeight : fontStyle.weight(); + SkFontStyle::Slant slant = fontStyle.slant(); + switch (fontFile.fStyle) { + case FontFileInfo::Style::kAuto: slant = fontStyle.slant(); break; + case FontFileInfo::Style::kNormal: slant = SkFontStyle::kUpright_Slant; break; + case FontFileInfo::Style::kItalic: slant = SkFontStyle::kItalic_Slant; break; + default: SkASSERT(false); break; + } + fontStyle = SkFontStyle(weight, fontStyle.width(), slant); + + fStyles.push_back( + SkTypeface_AndroidSystem::Make(proxy, + fontStyle, proxy->isFixedPitch(), + familyName, family.fLanguages, variant)); } } @@ -409,7 +400,7 @@ protected: sk_sp onMakeFromStreamArgs(std::unique_ptr stream, const SkFontArguments& args) const override { - return SkTypeface_FreeType::MakeFromStream(std::move(stream), args); + return fScanner->MakeFromStream(std::move(stream), args); } sk_sp onLegacyMakeTypeface(const char familyName[], SkFontStyle style) const override { @@ -437,7 +428,9 @@ private: TArray fNameToFamilyMap; TArray fFallbackNameToFamilyMap; - void addFamily(FontFamily& family, const bool isolated, int familyIndex) { + void addFamily(FontFamily& family, const bool isolated, int familyIndex, + StreamForPathCache& streamForPath) + { TArray* nameToFamily = &fNameToFamilyMap; if (family.fIsFallbackFont) { nameToFamily = &fFallbackNameToFamilyMap; @@ -449,7 +442,7 @@ private: } sk_sp newSet = - sk_make_sp(family, fScanner.get(), isolated); + sk_make_sp(family, fScanner.get(), isolated, streamForPath); if (0 == newSet->count()) { return; } @@ -460,11 +453,12 @@ private: fStyleSets.emplace_back(std::move(newSet)); } void buildNameToFamilyMap(const SkTDArray& families, const bool isolated) { + StreamForPathCache streamForPath; int familyIndex = 0; for (FontFamily* family : families) { - addFamily(*family, isolated, familyIndex++); + addFamily(*family, isolated, familyIndex++, streamForPath); for (const auto& [unused, fallbackFamily] : family->fallbackFamilies) { - addFamily(*fallbackFamily, isolated, familyIndex++); + addFamily(*fallbackFamily, isolated, familyIndex++, streamForPath); } } } diff --git a/gfx/skia/skia/src/ports/SkFontMgr_android_ndk.cpp b/gfx/skia/skia/src/ports/SkFontMgr_android_ndk.cpp index e792bd8cefd8..1169f0ee1891 100644 --- a/gfx/skia/skia/src/ports/SkFontMgr_android_ndk.cpp +++ b/gfx/skia/skia/src/ports/SkFontMgr_android_ndk.cpp @@ -17,8 +17,8 @@ #include "src/base/SkUTF.h" #include "src/core/SkFontDescriptor.h" #include "src/core/SkOSFile.h" -#include "src/ports/SkFontScanner_FreeType_priv.h" -#include "src/ports/SkTypeface_FreeType.h" +#include "src/core/SkTHash.h" +#include "src/ports/SkTypeface_proxy.h" #include @@ -252,97 +252,69 @@ private: static_assert(::sk_is_trivially_relocatable::value); }; -class SkTypeface_AndroidNDK : public SkTypeface_FreeType { +class SkTypeface_AndroidNDK : public SkTypeface_proxy { public: - SkTypeface_AndroidNDK(std::unique_ptr file, - const SkString& pathName, - const bool cacheFontFiles, - int index, - const SkFixed* axes, int axesCount, + static sk_sp Make(sk_sp realTypeface, + const SkFontStyle& style, + bool isFixedPitch, + const SkString& familyName, + TArray&& lang) { + SkASSERT(realTypeface); + return sk_sp(new SkTypeface_AndroidNDK(std::move(realTypeface), + style, + isFixedPitch, + familyName, + std::move(lang))); + } + +private: + SkTypeface_AndroidNDK(sk_sp realTypeface, const SkFontStyle& style, bool isFixedPitch, const SkString& familyName, TArray&& lang) - : SkTypeface_FreeType(style, isFixedPitch) + : SkTypeface_proxy(std::move(realTypeface), style, isFixedPitch) , fFamilyName(familyName) - , fPathName(pathName) - , fIndex(index) - , fAxes(axes, axesCount) , fLang(std::move(lang)) - , fFile((cacheFontFiles && file) ? std::move(file) - :(cacheFontFiles && !file) ? SkStream::MakeFromFile(fPathName.c_str()) - : nullptr) - , fCacheFontFiles(cacheFontFiles) - { - if (cacheFontFiles) { - SkASSERT(fFile); - } - } + { } void onGetFamilyName(SkString* familyName) const override { *familyName = fFamilyName; } - std::unique_ptr makeStream() const { - if (fFile) { - return fFile->duplicate(); - } - return SkStream::MakeFromFile(fPathName.c_str()); - } - void onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) const override { SkASSERT(desc); SkASSERT(serialize); + SkTypeface_proxy::onGetFontDescriptor(desc, serialize); desc->setFamilyName(fFamilyName.c_str()); desc->setStyle(this->fontStyle()); - desc->setFactoryId(SkTypeface_FreeType::FactoryId); *serialize = false; } - std::unique_ptr onOpenStream(int* ttcIndex) const override { - *ttcIndex = fIndex; - return this->makeStream(); - } - std::unique_ptr onMakeFontData() const override { - return std::make_unique( - this->makeStream(), fIndex, 0, fAxes.begin(), fAxes.size(), nullptr, 0); - } + sk_sp onMakeClone(const SkFontArguments& args) const override { - SkFontStyle newStyle = this->fontStyle(); - std::unique_ptr data = this->cloneFontData(args, &newStyle); - if (!data) { + auto proxy = SkTypeface_proxy::onMakeClone(args); + if (proxy == nullptr) { return nullptr; } - return sk_sp(new SkTypeface_AndroidNDK(fFile ? fFile->duplicate() : nullptr, - fPathName, - fCacheFontFiles, - fIndex, - data->getAxis(), - data->getAxisCount(), - newStyle, - this->isFixedPitch(), - fFamilyName, - TArray())); + return SkTypeface_AndroidNDK::Make( + std::move(proxy), + this->fontStyle(), + this->isFixedPitch(), + fFamilyName, + TArray()); } - sk_sp makeNamedClone(const SkString& name) const { - return sk_sp(new SkTypeface_AndroidNDK(fFile ? fFile->duplicate() : nullptr, - fPathName, - fCacheFontFiles, - fIndex, - fAxes.data(), fAxes.size(), - this->fontStyle(), - this->isFixedPitch(), - name, - STArray<4, SkLanguage>(fLang))); + SkFontStyle onGetFontStyle() const override { + return SkTypeface::onGetFontStyle(); } + bool onGetFixedPitch() const override { + return SkTypeface::onGetFixedPitch(); + } + +public: const SkString fFamilyName; - const SkString fPathName; - int fIndex; - const STArray<4, SkFixed> fAxes; const STArray<4, SkLanguage> fLang; - std::unique_ptr fFile; - bool fCacheFontFiles; }; class SkFontStyleSet_AndroidNDK : public SkFontStyleSet { @@ -376,16 +348,13 @@ public: SkTypeface_AndroidNDK* amatch = static_cast(match.get()); SkString name; amatch->getFamilyName(&name); + SkString resourceName; + amatch->getResourceName(&resourceName); SkFontStyle fontStyle = amatch->fontStyle(); - SkString axes; - for (auto&& axis : amatch->fAxes) { - axes.appendScalar(SkFixedToScalar(axis)); - axes.append(", "); - } - SkDebugf("SKIA: Search for [%d, %d, %d] matched %s [%d, %d, %d] %s#%d [%s]\n", + SkDebugf("SKIA: Search for [%d, %d, %d] matched %s [%d, %d, %d] %s\n", pattern.weight(), pattern.width(), pattern.slant(), name.c_str(), fontStyle.weight(), fontStyle.width(), fontStyle.slant(), - amatch->fPathName.c_str(), amatch->fIndex, axes.c_str()); + resourceName.c_str()); } return match; } @@ -438,9 +407,11 @@ public: return; } + skia_private::THashMap> streamForPath; + if constexpr (kSkFontMgrVerbose) { SkDebugf("SKIA: Iterating over AFonts\n"); } while (SkAFont font = fontIter.next()) { - sk_sp typeface = this->make(std::move(font), cacheFontFiles); + sk_sp typeface = this->make(std::move(font), streamForPath); if (!typeface) { continue; } @@ -515,51 +486,38 @@ protected: return sset->matchStyle(style); } - sk_sp make(SkAFont font, bool cacheFontFiles) const { - const char* filePath = font.getFontFilePath(); + sk_sp make(SkAFont font, skia_private::THashMap>& streamForPath) const { + SkString filePath(font.getFontFilePath()); - std::unique_ptr stream = SkStream::MakeFromFile(filePath); + std::unique_ptr* streamPtr = streamForPath.find(filePath); + if (!streamPtr) { + streamPtr = streamForPath.set(filePath, SkStream::MakeFromFile(filePath.c_str())); + } + if (!*streamPtr) { + if constexpr (kSkFontMgrVerbose) { + SkDebugf("SKIA: Font file %s cannot be opened.\n", filePath.c_str()); + } + return nullptr; + } + std::unique_ptr stream = (*streamPtr)->duplicate(); if (!stream) { if constexpr (kSkFontMgrVerbose) { - SkDebugf("SKIA: Font file %s does not exist or cannot be opened.\n", filePath); + SkDebugf("SKIA: Font file %s could not be duplicated.\n", filePath.c_str()); } return nullptr; } size_t collectionIndex = font.getCollectionIndex(); if constexpr (kSkFontMgrVerbose) { - SkDebugf("SKIA: Making font from %s#%zu\n", filePath, collectionIndex); + SkDebugf("SKIA: Making font from %s#%zu\n", filePath.c_str(), collectionIndex); } if (!SkTFitsIn(collectionIndex)) { if constexpr (kSkFontMgrVerbose) { SkDebugf("SKIA: Collection index invalid!"); } return nullptr; } - const int ttcIndex = SkTo(collectionIndex); - SkString familyName; - SkFontStyle style; - bool isFixedWidth; - SkFontScanner::AxisDefinitions axisDefinitions; - if (!fScanner->scanInstance(stream.get(), ttcIndex, 0, - &familyName, &style, &isFixedWidth, &axisDefinitions)) - { - if constexpr (kSkFontMgrVerbose) { - SkDebugf("SKIA: Font file %s exists, but is not a valid font.\n", filePath); - } - return nullptr; - } - - int weight = SkTo(font.getWeight()); - SkFontStyle::Slant slant = style.slant(); - if (font.isItalic()) { - slant = SkFontStyle::kItalic_Slant; - } - int width = style.width(); constexpr SkFourByteTag wdth = SkSetFourByteTag('w','d','t','h'); - - // The family name(s) are not reported. - // This would be very helpful for aliases, like "sans-serif", "Arial", etc. - size_t requestAxisCount = font.getAxisCount(); if (!SkTFitsIn(requestAxisCount)) { if constexpr (kSkFontMgrVerbose) { SkDebugf("SKIA: Axis count unreasonable!"); } @@ -567,22 +525,44 @@ protected: } using Coordinate = SkFontArguments::VariationPosition::Coordinate; AutoSTMalloc<4, Coordinate> requestAxisValues(requestAxisCount); + std::optional requestedWidth; for (size_t i = 0; i < requestAxisCount; ++i) { uint32_t tag = font.getAxisTag(i); float value = font.getAxisValue(i); requestAxisValues[i] = { tag, value }; if (tag == wdth) { // Set the width based on the requested `wdth` axis value. - width = SkFontDescriptor::SkFontStyleWidthForWidthAxisValue(value); + requestedWidth = SkFontDescriptor::SkFontStyleWidthForWidthAxisValue(value); } } - - AutoSTMalloc<4, SkFixed> axisValues(axisDefinitions.size()); - SkFontArguments::VariationPosition position = { - requestAxisValues.get(), SkTo(requestAxisCount) + SkFontArguments::VariationPosition requestedPosition = { + requestAxisValues.get(), SkTo(requestAxisCount) }; - SkFontScanner_FreeType::computeAxisValues(axisDefinitions, position, - axisValues, familyName, nullptr); + // TODO: this creates the proxy with the given stream, so always cacheFontFiles. + auto proxy = fScanner->MakeFromStream( + std::move(stream), + SkFontArguments() + .setCollectionIndex(collectionIndex) + .setVariationDesignPosition(requestedPosition)); + if (!proxy) { + if constexpr (kSkFontMgrVerbose) { + SkDebugf("SKIA: Font file %s exists, but is not a valid font.\n", filePath.c_str()); + } + return nullptr; + } + SkFontStyle style = proxy->fontStyle(); + int weight = SkTo(font.getWeight()); + SkFontStyle::Slant slant = style.slant(); + if (font.isItalic()) { + slant = SkFontStyle::kItalic_Slant; + } + int width = requestedWidth.value_or(style.width()); + style = SkFontStyle(weight, width, slant); + + // The family name(s) are not reported. + // This would be very helpful for aliases, like "sans-serif", "Arial", etc. + SkString familyName; + proxy->getFamilyName(&familyName); STArray<4, SkLanguage> skLangs; const char* aLangs = font.getLocale(); @@ -614,14 +594,13 @@ protected: } } - style = SkFontStyle(weight, width, slant); if constexpr (kSkFontMgrVerbose) { - SkDebugf("SKIA: New typeface %s [%d %d %d]\n", familyName.c_str(), weight,width,slant); + SkDebugf("SKIA: New typeface %s [%d %d %d]\n", familyName.c_str(), style.weight(), + style.width(), style.slant()); } - return sk_sp(new SkTypeface_AndroidNDK( - std::move(stream), SkString(filePath), cacheFontFiles, ttcIndex, - axisValues.get(), axisDefinitions.size(), - style, isFixedWidth, familyName, std::move(skLangs))); + + return SkTypeface_AndroidNDK::Make( + proxy, style, proxy->isFixedPitch(), familyName, std::move(skLangs)); } @@ -756,7 +735,7 @@ protected: sk_sp onMakeFromStreamArgs(std::unique_ptr stream, const SkFontArguments& args) const override { - return SkTypeface_FreeType::MakeFromStream(std::move(stream), args); + return fScanner->MakeFromStream(std::move(stream), args); } sk_sp onLegacyMakeTypeface(const char name[], SkFontStyle style) const override { diff --git a/gfx/skia/skia/src/ports/SkFontMgr_custom_directory.cpp b/gfx/skia/skia/src/ports/SkFontMgr_custom_directory.cpp index bc68f4e552d4..9db9c7fdd10f 100644 --- a/gfx/skia/skia/src/ports/SkFontMgr_custom_directory.cpp +++ b/gfx/skia/skia/src/ports/SkFontMgr_custom_directory.cpp @@ -82,7 +82,7 @@ private: &realname, &style, &isFixedPitch, - nullptr)) { + nullptr, nullptr)) { // SkDebugf("---- failed to open <%s> <%d> as a font\n", // filename.c_str(), faceIndex); continue; diff --git a/gfx/skia/skia/src/ports/SkFontMgr_custom_embedded.cpp b/gfx/skia/skia/src/ports/SkFontMgr_custom_embedded.cpp index 1c60eab465d2..fd8370aed470 100644 --- a/gfx/skia/skia/src/ports/SkFontMgr_custom_embedded.cpp +++ b/gfx/skia/skia/src/ports/SkFontMgr_custom_embedded.cpp @@ -102,7 +102,7 @@ static void load_font_from_data(const SkFontScanner* scanner, &realname, &style, &isFixedPitch, - nullptr)) { + nullptr, nullptr)) { SkDebugf("---- failed to open <%d> <%d> <%d> as an instance\n", index, faceIndex, diff --git a/gfx/skia/skia/src/ports/SkFontMgr_fontconfig.cpp b/gfx/skia/skia/src/ports/SkFontMgr_fontconfig.cpp index a8f9fb51c88e..b269a27f3614 100644 --- a/gfx/skia/skia/src/ports/SkFontMgr_fontconfig.cpp +++ b/gfx/skia/skia/src/ports/SkFontMgr_fontconfig.cpp @@ -30,6 +30,7 @@ #include "src/core/SkOSFile.h" #include "src/core/SkScalerContext.h" #include "src/core/SkTypefaceCache.h" +#include "src/ports/SkTypeface_proxy.h" #include @@ -41,7 +42,6 @@ #include class SkData; - using namespace skia_private; // FC_POSTSCRIPT_NAME was added with b561ff20 which ended up in 2.10.92 @@ -411,34 +411,60 @@ static void fcpattern_from_skfontstyle(SkFontStyle style, FcPattern* pattern) { FcPatternAddInteger(pattern, FC_SLANT , slant); } -class SkScalerContext_fontconfig; -class SkTypeface_fontconfig : public SkTypeface { +class SkTypeface_fontconfig : public SkTypeface_proxy { public: - static sk_sp Make(SkAutoFcPattern pattern, - SkString sysroot, - SkFontScanner* scanner) { - return sk_sp( - new SkTypeface_fontconfig(std::move(pattern), std::move(sysroot), scanner)); - } - mutable SkAutoFcPattern fPattern; // Mutable for passing to FontConfig API. - const SkString fSysroot; + static sk_sp Make(SkAutoFcPattern pattern, + SkString sysroot, + SkFontScanner* scanner) { + SkString resolvedFilename; + FCLocker lock; + const char* filename = get_string(pattern, FC_FILE); + // See FontAccessible for note on searching sysroot then non-sysroot path. + if (!sysroot.isEmpty()) { + resolvedFilename = sysroot; + resolvedFilename += filename; + if (sk_exists(resolvedFilename.c_str(), kRead_SkFILE_Flag)) { + filename = resolvedFilename.c_str(); + } + } + // TODO: FC_VARIABLE and FC_FONT_VARIATIONS in arguments + int ttcIndex = get_int(pattern, FC_INDEX, 0); + auto realTypeface = scanner->MakeFromStream(SkStream::MakeFromFile(filename), + SkFontArguments().setCollectionIndex(ttcIndex)); + if (!realTypeface) { + // Unref the pattern while holding the lock. + pattern.reset(); + return nullptr; + } + SkFontStyle proxyStyle = skfontstyle_from_fcpattern(pattern); + return sk_sp(new SkTypeface_fontconfig(std::move(realTypeface), std::move(pattern), + proxyStyle, nullptr)); + } + + mutable SkAutoFcPattern fPattern; // Mutable for passing to FontConfig API. + SkFontStyle fOriginalRealStyle; + +protected: void onGetFamilyName(SkString* familyName) const override { *familyName = get_string(fPattern, FC_FAMILY); } + + SkFontStyle onGetFontStyle() const override { + return SkTypeface::onGetFontStyle(); + } + void onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) const override { // TODO: need to serialize FC_MATRIX and FC_EMBOLDEN FCLocker lock; - fProxy->onGetFontDescriptor(desc, serialize); + SkTypeface_proxy::onGetFontDescriptor(desc, serialize); desc->setFamilyName(get_string(fPattern, FC_FAMILY)); desc->setFullName(get_string(fPattern, FC_FULLNAME)); desc->setPostscriptName(get_string(fPattern, FC_POSTSCRIPT_NAME)); desc->setStyle(this->fontStyle()); *serialize = false; } - std::unique_ptr onOpenStream(int* ttcIndex) const override { - return fProxy->onOpenStream(ttcIndex); - } + void onFilterRec(SkScalerContextRec* rec) const override { // FontConfig provides 10-scale-bitmap-fonts.conf which applies an inverse "pixelsize" // matrix. It is not known if this .conf is active or not, so it is not clear if @@ -467,139 +493,60 @@ public: rec->fFlags |= SkScalerContext::kEmbolden_Flag; } - fProxy->filterRec(rec); + SkTypeface_proxy::onFilterRec(rec); } std::unique_ptr onGetAdvancedMetrics() const override { - std::unique_ptr info = fProxy->onGetAdvancedMetrics(); + std::unique_ptr info = SkTypeface_proxy::onGetAdvancedMetrics(); // Simulated fonts shouldn't be considered to be of the type of their data. if (get_matrix(fPattern, FC_MATRIX) || get_bool(fPattern, FC_EMBOLDEN)) { info->fType = SkAdvancedTypefaceMetrics::kOther_Font; } return info; } - sk_sp onMakeClone(const SkFontArguments& args) const override { - // TODO: need to clone FC_MATRIX and FC_EMBOLDEN by wrapping this - return fProxy->onMakeClone(args); - } - std::unique_ptr onCreateScalerContext(const SkScalerContextEffects& effects, const SkDescriptor* descr) const override; - - void getGlyphToUnicodeMap(SkUnichar* glyphToUnicode) const override { - fProxy->getGlyphToUnicodeMap(glyphToUnicode); - } - void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override { - fProxy->unicharsToGlyphs(chars, count, glyphs); - } - int onCountGlyphs() const override { return fProxy->countGlyphs(); } - int onGetUPEM() const override { return fProxy->getUnitsPerEm(); } - bool onGetKerningPairAdjustments(const uint16_t glyphs[], int count, - int32_t adjustments[]) const override { - return fProxy->onGetKerningPairAdjustments(glyphs, count, adjustments); - } - bool onGetPostScriptName(SkString* postScriptName) const override { - return fProxy->getPostScriptName(postScriptName); - } - SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override { - return fProxy->createFamilyNameIterator(); - } - void getPostScriptGlyphNames(SkString* names) const override { - return fProxy->getPostScriptGlyphNames(names); - } - bool onGlyphMaskNeedsCurrentColor() const override { - return fProxy->glyphMaskNeedsCurrentColor(); - } - int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], - int coordinateCount) const override { - return fProxy->onGetVariationDesignPosition(coordinates, coordinateCount); - } - int onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[], - int parameterCount) const override { - return fProxy->onGetVariationDesignParameters(parameters, parameterCount); - } - int onGetTableTags(SkFontTableTag tags[]) const override { - return fProxy->getTableTags(tags); - } - size_t onGetTableData(SkFontTableTag tag, size_t offset, size_t length, void* data) const override { - return fProxy->getTableData(tag, offset, length, data); - } ~SkTypeface_fontconfig() override { // Hold the lock while unrefing the pattern. FCLocker lock; fPattern.reset(); } -private: - SkTypeface_fontconfig(SkAutoFcPattern pattern, SkString sysroot, SkFontScanner* fontScanner) - : SkTypeface(skfontstyle_from_fcpattern(pattern), - FC_PROPORTIONAL != get_int(pattern, FC_SPACING, FC_PROPORTIONAL)) - , fPattern(std::move(pattern)) - , fSysroot(std::move(sysroot)) { - // TODO: for now only FreeTypeStream - SkString resolvedFilename; - FCLocker lock; - const char* filename = get_string(fPattern, FC_FILE); - // See FontAccessible for note on searching sysroot then non-sysroot path. - if (!fSysroot.isEmpty()) { - resolvedFilename = fSysroot; - resolvedFilename += filename; - if (sk_exists(resolvedFilename.c_str(), kRead_SkFILE_Flag)) { - filename = resolvedFilename.c_str(); - } + sk_sp onMakeClone(const SkFontArguments& args) const override { + sk_sp realTypeface = SkTypeface_proxy::onMakeClone(args); + if (!realTypeface) { + return nullptr; + } + SkAutoFcPattern pattern; + { + FCLocker lock; + FcPatternReference(fPattern); + pattern.reset(fPattern.get()); } - // TODO: FC_VARIABLE and FC_FONT_VARIATIONS in arguments - auto ttcIndex = get_int(fPattern, FC_INDEX, 0); - fProxy = fontScanner->MakeFromStream(SkStream::MakeFromFile(filename), - SkFontArguments().setCollectionIndex(ttcIndex)); - } - sk_sp fProxy; -}; -class SkScalerContext_fontconfig : public SkScalerContext { + SkFontStyle newRealStyle = realTypeface->fontStyle(); + SkFontStyle originalProxyStyle = skfontstyle_from_fcpattern(pattern); + SkFontStyle newProxyStyle( + // keep consistent weight and width offsets (though they will be clamped) + originalProxyStyle.weight() + (newRealStyle.weight() - fOriginalRealStyle.weight()), + originalProxyStyle.width() + (newRealStyle.width() - fOriginalRealStyle.width()), + // the originalProxyStyle's slant is an override for the originalRealStyle's slant + newRealStyle.slant() == fOriginalRealStyle.slant() ? originalProxyStyle.slant() + : newRealStyle.slant()); + + return sk_sp(new SkTypeface_fontconfig(std::move(realTypeface), std::move(pattern), + newProxyStyle, &fOriginalRealStyle)); + } + private: - std::unique_ptr fProxy; -public: - SkScalerContext_fontconfig(std::unique_ptr proxy, - sk_sp typeface, - const SkScalerContextEffects& effects, - const SkDescriptor* desc) - : SkScalerContext(std::move(typeface), effects, desc) - , fProxy(std::move(proxy)) - { } - - ~SkScalerContext_fontconfig() override = default; -protected: - SkScalerContext::GlyphMetrics generateMetrics(const SkGlyph& glyph, SkArenaAlloc* alloc) override { - return fProxy->generateMetrics(glyph, alloc); - } - - void generateImage(const SkGlyph& glyph, void* imageBuffer) override { - fProxy->generateImage(glyph, imageBuffer); - } - - bool generatePath(const SkGlyph& glyph, SkPath* path, bool* modified) override { - return fProxy->generatePath(glyph, path, modified); - } - - sk_sp generateDrawable(const SkGlyph& glyph) override { - return fProxy->generateDrawable(glyph); - } - - void generateFontMetrics(SkFontMetrics* metrics) override { - fProxy->generateFontMetrics(metrics); - } + SkTypeface_fontconfig(sk_sp realTypeface, SkAutoFcPattern pattern, + const SkFontStyle& proxyStyle, const SkFontStyle* originalRealStyle) + : SkTypeface_proxy(realTypeface, proxyStyle, + FC_PROPORTIONAL != get_int(pattern, FC_SPACING, FC_PROPORTIONAL)) + , fPattern(std::move(pattern)) + , fOriginalRealStyle(originalRealStyle ? *originalRealStyle + : SkTypeface_proxy::onGetFontStyle()) + {} }; -std::unique_ptr SkTypeface_fontconfig::onCreateScalerContext( - const SkScalerContextEffects& effects, const SkDescriptor* descr) const { - auto proxyScaler = fProxy->onCreateScalerContextAsProxyTypeface(effects, descr, sk_ref_sp(this)); - auto scaler = std::make_unique( - std::move(proxyScaler), - sk_ref_sp(const_cast(this)), - effects, - descr); - return scaler; -} - class SkFontMgr_fontconfig : public SkFontMgr { mutable SkAutoFcConfig fFC; // Only mutable to avoid const cast when passed to FontConfig API. const SkString fSysroot; @@ -746,9 +693,10 @@ class SkFontMgr_fontconfig : public SkFontMgr { return face; }(); if (!face) { + // Cannot hold FCLocker around Make; may need to destory pattern. face = SkTypeface_fontconfig::Make(std::move(pattern), fSysroot, fScanner.get()); if (face) { - // Cannot hold FCLocker in fTFCache.add; evicted typefaces may need to lock. + // Cannot hold FCLocker around fTFCache.add; evicted typefaces may need to lock. fTFCache.add(face); } } @@ -848,10 +796,15 @@ protected: resolvedFilename = fSysroot; resolvedFilename += filename; if (sk_exists(resolvedFilename.c_str(), kRead_SkFILE_Flag)) { - return true; + auto&& file(SkData::MakeFromFileName(resolvedFilename.c_str())); + return file && fScanner->scanFile(SkMemoryStream::Make(file).get(), nullptr); } } - return sk_exists(filename, kRead_SkFILE_Flag); + if (sk_exists(filename, kRead_SkFILE_Flag)) { + auto&& file(SkData::MakeFromFileName(filename)); + return file && fScanner->scanFile(SkMemoryStream::Make(file).get(), nullptr); + } + return false; } static bool FontFamilyNameMatches(FcPattern* font, FcPattern* pattern) { @@ -911,7 +864,7 @@ protected: for (int fontIndex = 0; fontIndex < allFonts->nfont; ++fontIndex) { FcPattern* font = allFonts->fonts[fontIndex]; - if (FontAccessible(font) && FontFamilyNameMatches(font, matchPattern)) { + if (FontFamilyNameMatches(font, matchPattern) && FontAccessible(font)) { FcFontSetAdd(matches, FcFontRenderPrepare(fFC, pattern, font)); } } @@ -952,7 +905,7 @@ protected: FcResult result; SkAutoFcPattern font(FcFontMatch(fFC, pattern, &result)); - if (!font || !FontAccessible(font) || !FontFamilyNameMatches(font, matchPattern)) { + if (!font || !FontFamilyNameMatches(font, matchPattern) || !FontAccessible(font)) { font.reset(); } return font; @@ -996,7 +949,7 @@ protected: FcResult result; SkAutoFcPattern font(FcFontMatch(fFC, pattern, &result)); - if (!font || !FontAccessible(font) || !FontContainsCharacter(font, character)) { + if (!font || !FontContainsCharacter(font, character) || !FontAccessible(font)) { font.reset(); } return font; diff --git a/gfx/skia/skia/src/ports/SkFontMgr_fuchsia.cpp b/gfx/skia/skia/src/ports/SkFontMgr_fuchsia.cpp index 3ef116573c5e..dd1d9a7e09ae 100644 --- a/gfx/skia/skia/src/ports/SkFontMgr_fuchsia.cpp +++ b/gfx/skia/skia/src/ports/SkFontMgr_fuchsia.cpp @@ -254,19 +254,23 @@ sk_sp CreateTypefaceFromSkStream(std::unique_ptr stre SkFontStyle style; SkString name; SkFontScanner::AxisDefinitions axisDefinitions; + SkFontScanner::VariationPosition current; if (!fontScanner.scanInstance(stream.get(), args.getCollectionIndex(), 0, &name, &style, &isFixedPitch, - &axisDefinitions)) { + &axisDefinitions, + ¤t)) { return nullptr; } const SkFontArguments::VariationPosition position = args.getVariationDesignPosition(); AutoSTMalloc<4, SkFixed> axisValues(axisDefinitions.size()); - SkFontScanner_FreeType::computeAxisValues(axisDefinitions, position, axisValues, name, &style); + const SkFontArguments::VariationPosition currentPos{current.data(), current.size()}; + SkFontScanner_FreeType::computeAxisValues(axisDefinitions, currentPos, position, axisValues, + name, &style); auto fontData = std::make_unique( std::move(stream), args.getCollectionIndex(), args.getPalette().index, diff --git a/gfx/skia/skia/src/ports/SkFontMgr_mac_ct.cpp b/gfx/skia/skia/src/ports/SkFontMgr_mac_ct.cpp index a3a72ebfc48f..95aa713efdb2 100644 --- a/gfx/skia/skia/src/ports/SkFontMgr_mac_ct.cpp +++ b/gfx/skia/skia/src/ports/SkFontMgr_mac_ct.cpp @@ -42,34 +42,6 @@ using namespace skia_private; -#if (defined(SK_BUILD_FOR_IOS) && defined(__IPHONE_14_0) && \ - __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_14_0) || \ - (defined(SK_BUILD_FOR_MAC) && defined(__MAC_11_0) && \ - __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_11_0) - -static uint32_t SkGetCoreTextVersion() { - // If compiling for iOS 14.0+ or macOS 11.0+, the CoreText version number - // must be derived from the OS version number. - static const uint32_t kCoreTextVersionNEWER = 0x000D0000; - return kCoreTextVersionNEWER; -} - -#else - -static uint32_t SkGetCoreTextVersion() { - // Check for CoreText availability before calling CTGetCoreTextVersion(). - static const bool kCoreTextIsAvailable = (&CTGetCoreTextVersion != nullptr); - if (kCoreTextIsAvailable) { - return CTGetCoreTextVersion(); - } - - // Default to a value that's smaller than any known CoreText version. - static const uint32_t kCoreTextVersionUNKNOWN = 0; - return kCoreTextVersionUNKNOWN; -} - -#endif - static SkUniqueCFRef make_CFString(const char s[]) { return SkUniqueCFRef(CFStringCreateWithCString(nullptr, s, kCFStringEncodingUTF8)); } @@ -100,34 +72,7 @@ static SkUniqueCFRef create_descriptor(const char familyNam return nullptr; } - // TODO(crbug.com/1018581) Some CoreText versions have errant behavior when - // certain traits set. Temporary workaround to omit specifying trait for those - // versions. - // Long term solution will involve serializing typefaces instead of relying upon - // this to match between processes. - // - // Compare CoreText.h in an up to date SDK for where these values come from. - static const uint32_t kSkiaLocalCTVersionNumber10_14 = 0x000B0000; - static const uint32_t kSkiaLocalCTVersionNumber10_15 = 0x000C0000; - - // CTFontTraits (symbolic) - // macOS 14 and iOS 12 seem to behave badly when kCTFontSymbolicTrait is set. - // macOS 15 yields LastResort font instead of a good default font when - // kCTFontSymbolicTrait is set. - if (SkGetCoreTextVersion() < kSkiaLocalCTVersionNumber10_14) { - CTFontSymbolicTraits ctFontTraits = 0; - if (style.weight() >= SkFontStyle::kBold_Weight) { - ctFontTraits |= kCTFontBoldTrait; - } - if (style.slant() != SkFontStyle::kUpright_Slant) { - ctFontTraits |= kCTFontItalicTrait; - } - SkUniqueCFRef cfFontTraits( - CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits)); - if (cfFontTraits) { - CFDictionaryAddValue(cfTraits.get(), kCTFontSymbolicTrait, cfFontTraits.get()); - } - } + // Setting both kCTFontSymbolicTrait and the specific WWS traits may lead to strange outcomes. // CTFontTraits (weight) CGFloat ctWeight = SkCTFontCTWeightForCSSWeight(style.weight()); @@ -144,14 +89,13 @@ static SkUniqueCFRef create_descriptor(const char familyNam CFDictionaryAddValue(cfTraits.get(), kCTFontWidthTrait, cfFontWidth.get()); } // CTFontTraits (slant) - // macOS 15 behaves badly when kCTFontSlantTrait is set. - if (SkGetCoreTextVersion() != kSkiaLocalCTVersionNumber10_15) { - CGFloat ctSlant = style.slant() == SkFontStyle::kUpright_Slant ? 0 : 1; - SkUniqueCFRef cfFontSlant( - CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctSlant)); - if (cfFontSlant) { - CFDictionaryAddValue(cfTraits.get(), kCTFontSlantTrait, cfFontSlant.get()); - } + // Slope value set to 0.07 based on WebKit's implementation for better font matching + static const CGFloat kSystemFontItalicSlope = 0.07; + CGFloat ctSlant = style.slant() == SkFontStyle::kUpright_Slant ? 0 : kSystemFontItalicSlope; + SkUniqueCFRef cfFontSlant( + CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctSlant)); + if (cfFontSlant) { + CFDictionaryAddValue(cfTraits.get(), kCTFontSlantTrait, cfFontSlant.get()); } // CTFontTraits CFDictionaryAddValue(cfAttributes.get(), kCTFontTraitsAttribute, cfTraits.get()); diff --git a/gfx/skia/skia/src/ports/SkFontScanner_FreeType_priv.h b/gfx/skia/skia/src/ports/SkFontScanner_FreeType_priv.h index 7534760d249c..f6106c937b66 100644 --- a/gfx/skia/skia/src/ports/SkFontScanner_FreeType_priv.h +++ b/gfx/skia/skia/src/ports/SkFontScanner_FreeType_priv.h @@ -26,17 +26,18 @@ public: SkString* name, SkFontStyle* style, bool* isFixedPitch, - AxisDefinitions* axes) const override; + AxisDefinitions* axes, + VariationPosition* position) const override; sk_sp MakeFromStream(std::unique_ptr stream, const SkFontArguments& args) const override; SkTypeface::FactoryId getFactoryId() const override; static void computeAxisValues( - AxisDefinitions axisDefinitions, - const SkFontArguments::VariationPosition position, + const AxisDefinitions& axisDefinitions, + const SkFontArguments::VariationPosition currentPosition, + const SkFontArguments::VariationPosition requestedPosition, SkFixed* axisValues, const SkString& name, - SkFontStyle* style, - const SkFontArguments::VariationPosition::Coordinate* currentPosition = nullptr); + SkFontStyle* style); private: FT_Face openFace(SkStreamAsset* stream, int ttcIndex, FT_Stream ftStream) const; FT_Library fLibrary; diff --git a/gfx/skia/skia/src/ports/SkFontScanner_fontations.cpp b/gfx/skia/skia/src/ports/SkFontScanner_fontations.cpp index 919fad1d5dc1..824c6b8c2232 100644 --- a/gfx/skia/skia/src/ports/SkFontScanner_fontations.cpp +++ b/gfx/skia/skia/src/ports/SkFontScanner_fontations.cpp @@ -44,7 +44,9 @@ bool SkFontScanner_Fontations::scanFile(SkStreamAsset* stream, int* numFaces) co if (!fontations_ffi::font_or_collection(slice, num_fonts)) { return false; } - *numFaces = num_fonts == 0 ? 1 : num_fonts; + if (numFaces) { + *numFaces = num_fonts == 0 ? 1 : num_fonts; + } return true; } @@ -82,7 +84,8 @@ bool SkFontScanner_Fontations::scanInstance(SkStreamAsset* stream, SkString* name, SkFontStyle* style, bool* isFixedPitch, - AxisDefinitions* axes) const { + AxisDefinitions* axes, + VariationPosition* position) const { sk_sp fontData = make_data_avoiding_copy(stream); rust::Box bridgeFontFaceRef = make_bridge_font_ref(fontData.get(), faceIndex); @@ -147,6 +150,12 @@ bool SkFontScanner_Fontations::scanInstance(SkStreamAsset* stream, fontStyle.width, static_cast(fontStyle.slant)); } + if (position) { + position->reset(variationPosition.coordinateCount); + for (int i = 0; i < variationPosition.coordinateCount; ++i) { + (*position)[i] = variationPosition.coordinates[i]; + } + } } } @@ -162,10 +171,10 @@ bool SkFontScanner_Fontations::scanInstance(SkStreamAsset* stream, SkASSERT(size == size1); for (auto i = 0; i < size; ++i) { const auto var = variationAxes[i]; - (*axes)[i].fTag = var.tag; - (*axes)[i].fMinimum = var.min; - (*axes)[i].fDefault = var.def; - (*axes)[i].fMaximum = var.max; + (*axes)[i].tag = var.tag; + (*axes)[i].min = var.min; + (*axes)[i].def = var.def; + (*axes)[i].max = var.max; } } diff --git a/gfx/skia/skia/src/ports/SkFontScanner_fontations.h b/gfx/skia/skia/src/ports/SkFontScanner_fontations.h deleted file mode 100644 index 7a2cc0e16caf..000000000000 --- a/gfx/skia/skia/src/ports/SkFontScanner_fontations.h +++ /dev/null @@ -1,43 +0,0 @@ -/* -* Copyright 2024 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 SKFONTSCANNER_FONTATIONS_H_ -#define SKFONTSCANNER_FONTATIONS_H_ - -#include "include/core/SkSpan.h" -#include "include/core/SkTypeface.h" -#include "include/core/SkTypes.h" -#include "include/private/base/SkMutex.h" -#include "include/private/base/SkTArray.h" -#include "src/base/SkSharedMutex.h" -#include "src/core/SkFontScanner.h" -#include "src/core/SkGlyph.h" -#include "src/core/SkScalerContext.h" -#include "src/utils/SkCharToGlyphCache.h" - -struct SkAdvancedTypefaceMetrics; -class SkFontDescriptor; -class SkFontData; - -class SkFontScanner_Fontations : public SkFontScanner { -public: - SkFontScanner_Fontations(); - ~SkFontScanner_Fontations() override; - - bool scanFile(SkStreamAsset* stream, int* numFaces) const override; - bool scanFace(SkStreamAsset* stream, int faceIndex, int* numInstances) const override; - bool scanInstance(SkStreamAsset* stream, - int faceIndex, - int instanceIndex, - SkString* name, - SkFontStyle* style, - bool* isFixedPitch, - AxisDefinitions* axes) const override; -private: -}; - -#endif // SKFONTSCANNER_FONTATIONS_H_ diff --git a/gfx/skia/skia/src/ports/SkFontScanner_fontations_priv.h b/gfx/skia/skia/src/ports/SkFontScanner_fontations_priv.h index eb5ee176407f..06afb6bb5c64 100644 --- a/gfx/skia/skia/src/ports/SkFontScanner_fontations_priv.h +++ b/gfx/skia/skia/src/ports/SkFontScanner_fontations_priv.h @@ -30,7 +30,8 @@ public: SkString* name, SkFontStyle* style, bool* isFixedPitch, - AxisDefinitions* axes) const override; + AxisDefinitions* axes, + VariationPosition* position) const override; sk_sp MakeFromStream(std::unique_ptr stream, const SkFontArguments& args) const override; SkFourByteTag getFactoryId() const override; diff --git a/gfx/skia/skia/src/ports/SkGlobalInitialization_default.cpp b/gfx/skia/skia/src/ports/SkGlobalInitialization_default.cpp index 5caa6b54a315..ffb8efd75174 100644 --- a/gfx/skia/skia/src/ports/SkGlobalInitialization_default.cpp +++ b/gfx/skia/skia/src/ports/SkGlobalInitialization_default.cpp @@ -29,10 +29,10 @@ */ void SkFlattenable::PrivateInitializer::InitEffects() { // Shaders. + SkRegisterConicalGradientShaderFlattenable(); SkRegisterLinearGradientShaderFlattenable(); SkRegisterRadialGradientShaderFlattenable(); SkRegisterSweepGradientShaderFlattenable(); - SkRegisterConicalGradientShaderFlattenable(); // Color filters. SkRegisterComposeColorFilterFlattenable(); diff --git a/gfx/skia/skia/src/ports/SkScalerContext_mac_ct.cpp b/gfx/skia/skia/src/ports/SkScalerContext_mac_ct.cpp index 89a047139a52..99c9b4eae087 100644 --- a/gfx/skia/skia/src/ports/SkScalerContext_mac_ct.cpp +++ b/gfx/skia/skia/src/ports/SkScalerContext_mac_ct.cpp @@ -20,7 +20,6 @@ #endif #include "include/core/SkColor.h" -#include "include/core/SkColorPriv.h" #include "include/core/SkFontMetrics.h" #include "include/core/SkFontTypes.h" #include "include/core/SkMatrix.h" @@ -29,13 +28,14 @@ #include "include/core/SkRect.h" #include "include/core/SkScalar.h" #include "include/core/SkTypeface.h" -#include "include/private/SkColorData.h" #include "include/private/base/SkFixed.h" #include "include/private/base/SkTemplates.h" #include "include/private/base/SkTo.h" #include "src/base/SkAutoMalloc.h" #include "src/base/SkEndian.h" #include "src/base/SkMathPriv.h" +#include "src/core/SkColorData.h" +#include "src/core/SkColorPriv.h" #include "src/core/SkGlyph.h" #include "src/core/SkMask.h" #include "src/core/SkMaskGamma.h" @@ -116,10 +116,10 @@ static CGAffineTransform MatrixToCGAffineTransform(const SkMatrix& matrix) { SkScalarToCGFloat(matrix[SkMatrix::kMTransY])); } -SkScalerContext_Mac::SkScalerContext_Mac(sk_sp typeface, +SkScalerContext_Mac::SkScalerContext_Mac(SkTypeface_Mac& typeface, const SkScalerContextEffects& effects, const SkDescriptor* desc) - : INHERITED(std::move(typeface), effects, desc) + : INHERITED(typeface, effects, desc) , fOffscreen(fRec.fForegroundColor) , fDoSubPosition(SkToBool(fRec.fFlags & kSubpixelPositioning_Flag)) diff --git a/gfx/skia/skia/src/ports/SkScalerContext_mac_ct.h b/gfx/skia/skia/src/ports/SkScalerContext_mac_ct.h index 902567d8f1e7..a6a65bb1e164 100644 --- a/gfx/skia/skia/src/ports/SkScalerContext_mac_ct.h +++ b/gfx/skia/skia/src/ports/SkScalerContext_mac_ct.h @@ -41,7 +41,7 @@ typedef uint32_t CGRGBPixel; class SkScalerContext_Mac : public SkScalerContext { public: - SkScalerContext_Mac(sk_sp, const SkScalerContextEffects&, const SkDescriptor*); + SkScalerContext_Mac(SkTypeface_Mac&, const SkScalerContextEffects&, const SkDescriptor*); protected: GlyphMetrics generateMetrics(const SkGlyph&, SkArenaAlloc*) override; diff --git a/gfx/skia/skia/src/ports/SkScalerContext_win_dw.cpp b/gfx/skia/skia/src/ports/SkScalerContext_win_dw.cpp index bce9f00109e8..bd495ad1c060 100644 --- a/gfx/skia/skia/src/ports/SkScalerContext_win_dw.cpp +++ b/gfx/skia/skia/src/ports/SkScalerContext_win_dw.cpp @@ -265,10 +265,10 @@ static bool is_axis_aligned(const SkScalerContextRec& rec) { } //namespace -SkScalerContext_DW::SkScalerContext_DW(sk_sp typefaceRef, +SkScalerContext_DW::SkScalerContext_DW(DWriteFontTypeface& typefaceRef, const SkScalerContextEffects& effects, const SkDescriptor* desc) - : SkScalerContext(std::move(typefaceRef), effects, desc) + : SkScalerContext(typefaceRef, effects, desc) { DWriteFontTypeface* typeface = this->getDWriteTypeface(); fGlyphCount = typeface->fDWriteFontFace->GetGlyphCount(); @@ -1487,7 +1487,7 @@ bool SkScalerContext_DW::drawColorV1Image(const SkGlyph&, SkCanvas&) { return fa bool SkScalerContext_DW::setAdvance(const SkGlyph& glyph, SkVector* advance) { *advance = {0, 0}; - uint16_t glyphId = glyph.getGlyphID(); + UINT16 glyphId = glyph.getGlyphID(); DWriteFontTypeface* typeface = this->getDWriteTypeface(); // DirectWrite treats all out of bounds glyph ids as having the same data as glyph 0. @@ -1926,7 +1926,7 @@ void SkScalerContext_DW::generateFontMetrics(SkFontMetrics* metrics) { /////////////////////////////////////////////////////////////////////////////// -#include "include/private/SkColorData.h" +#include "src/core/SkColorData.h" void SkScalerContext_DW::BilevelToBW(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph, void* imageBuffer) { diff --git a/gfx/skia/skia/src/ports/SkScalerContext_win_dw.h b/gfx/skia/skia/src/ports/SkScalerContext_win_dw.h index 92e29add42b6..58f3dc56f5c6 100644 --- a/gfx/skia/skia/src/ports/SkScalerContext_win_dw.h +++ b/gfx/skia/skia/src/ports/SkScalerContext_win_dw.h @@ -26,7 +26,7 @@ struct DWRITE_PAINT_ELEMENT; class SkScalerContext_DW : public SkScalerContext { public: - SkScalerContext_DW(sk_sp, + SkScalerContext_DW(DWriteFontTypeface&, const SkScalerContextEffects&, const SkDescriptor*); ~SkScalerContext_DW() override; diff --git a/gfx/skia/skia/src/ports/SkTypeface_FreeType.h b/gfx/skia/skia/src/ports/SkTypeface_FreeType.h index 825f560d720d..5d17cfd409f8 100644 --- a/gfx/skia/skia/src/ports/SkTypeface_FreeType.h +++ b/gfx/skia/skia/src/ports/SkTypeface_FreeType.h @@ -47,19 +47,20 @@ protected: ~SkTypeface_FreeType() override; std::unique_ptr cloneFontData(const SkFontArguments&, SkFontStyle* style) const; - std::unique_ptr onCreateScalerContext(const SkScalerContextEffects&, - const SkDescriptor*) const override; + std::unique_ptr onCreateScalerContext( + const SkScalerContextEffects&, + const SkDescriptor*) const override; std::unique_ptr onCreateScalerContextAsProxyTypeface( - const SkScalerContextEffects&, - const SkDescriptor*, - sk_sp) const override; + const SkScalerContextEffects&, + const SkDescriptor*, + SkTypeface* proxyTypeface) const override; void onFilterRec(SkScalerContextRec*) const override; void getGlyphToUnicodeMap(SkUnichar*) const override; std::unique_ptr onGetAdvancedMetrics() const override; void getPostScriptGlyphNames(SkString* dstArray) const override; bool onGetPostScriptName(SkString*) const override; int onGetUPEM() const override; - bool onGetKerningPairAdjustments(const uint16_t glyphs[], int count, + bool onGetKerningPairAdjustments(const SkGlyphID glyphs[], int count, int32_t adjustments[]) const override; void onCharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const override; int onCountGlyphs() const override; diff --git a/gfx/skia/skia/src/ports/SkTypeface_fontations.cpp b/gfx/skia/skia/src/ports/SkTypeface_fontations.cpp index d2102d524fd4..36021bbe547b 100644 --- a/gfx/skia/skia/src/ports/SkTypeface_fontations.cpp +++ b/gfx/skia/skia/src/ports/SkTypeface_fontations.cpp @@ -17,6 +17,7 @@ #include "include/core/SkStream.h" #include "include/effects/SkGradientShader.h" #include "include/pathops/SkPathOps.h" +#include "include/private/base/SkMutex.h" #include "src/base/SkScopeExit.h" #include "src/core/SkFontDescriptor.h" #include "src/core/SkFontPriv.h" @@ -28,9 +29,16 @@ namespace { [[maybe_unused]] static inline const constexpr bool kSkShowTextBlitCoverage = false; sk_sp streamToData(const std::unique_ptr& font_data) { - // TODO(drott): From a stream this causes a full read/copy. Make sure - // we can instantiate this directly from the decompressed buffer that - // Blink has after OTS and woff2 decompression. + if (!font_data) { + return SkData::MakeEmpty(); + } + // TODO(drott): Remove this once SkData::MakeFromStream is able to do this itself. + if (font_data->getData()) { + return font_data->getData(); + } + if (font_data->getMemoryBase() && font_data->getLength()) { + return SkData::MakeWithCopy(font_data->getMemoryBase(), font_data->getLength()); + } font_data->rewind(); return SkData::MakeFromStream(font_data.get(), font_data->getLength()); } @@ -93,6 +101,19 @@ sk_sp SkTypeface_Make_Fontations(std::unique_ptr font return SkTypeface_Fontations::MakeFromStream(std::move(fontData), args); } +sk_sp SkTypeface_Make_Fontations(sk_sp fontData, + const SkFontArguments& args) { + return SkTypeface_Fontations::MakeFromData(std::move(fontData), args); +} + +static_assert( + sizeof(fontations_ffi::PaletteOverride) == sizeof(SkFontArguments::Palette::Override) && + sizeof(fontations_ffi::PaletteOverride::index) == + sizeof(SkFontArguments::Palette::Override::index) && + sizeof(fontations_ffi::PaletteOverride::color_8888) == + sizeof(SkFontArguments::Palette::Override::color), + "Struct fontations_ffi::PaletteOverride must match SkFontArguments::Palette::Override."); + SkTypeface_Fontations::SkTypeface_Fontations( sk_sp fontData, const SkFontStyle& style, @@ -101,6 +122,7 @@ SkTypeface_Fontations::SkTypeface_Fontations( rust::Box&& mappingIndex, rust::Box&& normalizedCoords, rust::Box&& outlines, + rust::Box&& glyphStyles, rust::Vec&& palette) : SkTypeface(style, true) , fFontData(std::move(fontData)) @@ -109,6 +131,7 @@ SkTypeface_Fontations::SkTypeface_Fontations( , fMappingIndex(std::move(mappingIndex)) , fBridgeNormalizedCoords(std::move(normalizedCoords)) , fOutlines(std::move(outlines)) + , fGlyphStyles(std::move(glyphStyles)) , fPalette(std::move(palette)) {} sk_sp SkTypeface_Fontations::MakeFromStream(std::unique_ptr stream, @@ -118,7 +141,7 @@ sk_sp SkTypeface_Fontations::MakeFromStream(std::unique_ptr SkTypeface_Fontations::MakeFromData(sk_sp data, const SkFontArguments& args) { - uint32_t ttcIndex = args.getCollectionIndex(); + uint32_t ttcIndex = args.getCollectionIndex() & 0xFFFF; rust::Box bridgeFontRef = make_bridge_font_ref(data, ttcIndex); if (!fontations_ffi::font_ref_is_valid(*bridgeFontRef)) { return nullptr; @@ -172,6 +195,10 @@ sk_sp SkTypeface_Fontations::MakeFromData(sk_sp data, rust::Box outlines = fontations_ffi::get_outline_collection(*bridgeFontRef); + // Container for precomputed work for autohinting, see Skrifa's GlyphStyles. + rust::Box glyphStyles = + fontations_ffi::get_bridge_glyph_styles(); + rust::Slice paletteOverrides( reinterpret_cast(args.getPalette().overrides), args.getPalette().overrideCount); @@ -185,6 +212,7 @@ sk_sp SkTypeface_Fontations::MakeFromData(sk_sp data, std::move(mappingIndex), std::move(normalizedCoords), std::move(outlines), + std::move(glyphStyles), std::move(palette))); } @@ -243,11 +271,11 @@ bool SkTypeface_Fontations::onGlyphMaskNeedsCurrentColor() const { void SkTypeface_Fontations::onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const { - sk_bzero(glyphs, count * sizeof(glyphs[0])); - - for (int i = 0; i < count; ++i) { - glyphs[i] = fontations_ffi::lookup_glyph_or_zero(*fBridgeFontRef, *fMappingIndex, chars[i]); - } + size_t realCount = SkToSizeT(count); + rust::Slice codepointSlice{reinterpret_cast(chars), realCount}; + rust::Slice glyphSlice{reinterpret_cast(glyphs), realCount}; + fontations_ffi::lookup_glyph_or_zero(*fBridgeFontRef, *fMappingIndex, + codepointSlice, glyphSlice); } int SkTypeface_Fontations::onCountGlyphs() const { return fontations_ffi::num_glyphs(*fBridgeFontRef); @@ -307,78 +335,115 @@ SkTypeface::LocalizedStrings* SkTypeface_Fontations::onCreateFamilyNameIterator( class SkFontationsScalerContext : public SkScalerContext { public: - SkFontationsScalerContext(sk_sp proxyTypeface, + SkFontationsScalerContext(const SkTypeface_Fontations& realTypeface, const SkScalerContextEffects& effects, const SkDescriptor* desc, - sk_sp realTypeface) - : SkScalerContext(realTypeface, effects, desc) - , fBridgeFontRef( - static_cast(proxyTypeface.get())->getBridgeFontRef()) - , fBridgeNormalizedCoords(static_cast(proxyTypeface.get()) - ->getBridgeNormalizedCoords()) - , fOutlines(static_cast(proxyTypeface.get())->getOutlines()) - , fPalette(static_cast(proxyTypeface.get())->getPalette()) + SkTypeface& proxyTypeface) + : SkScalerContext(proxyTypeface, effects, desc) // proxyTypeface owns the realTypeface + , fBridgeFontRef(realTypeface.getBridgeFontRef()) + , fBridgeNormalizedCoords(realTypeface.getBridgeNormalizedCoords()) + , fOutlines(realTypeface.getOutlines()) + , fGlyphStyles(realTypeface.getGlyphStyles()) + , fMappingIndex(realTypeface.getMappingIndex()) + , fPalette(realTypeface.getPalette()) , fHintingInstance(fontations_ffi::no_hinting_instance()) { fRec.computeMatrices( SkScalerContextRec::PreMatrixScale::kVertical, &fScale, &fRemainingMatrix); fDoLinearMetrics = this->isLinearMetrics(); - bool forceAutohinting = SkToBool(fRec.fFlags & kForceAutohinting_Flag); + // See below for the exception for SkFontHinting::kSlight. + fontations_ffi::AutoHintingControl autoHintingControl = + SkToBool(fRec.fFlags & kForceAutohinting_Flag) + ? fontations_ffi::AutoHintingControl::ForceForGlyfAndCff + : fontations_ffi::AutoHintingControl::AutoAsFallback; - if (SkMask::kBW_Format == fRec.fMaskFormat) { - if (fRec.getHinting() == SkFontHinting::kNone) { - fHintingInstance = fontations_ffi::no_hinting_instance(); - fDoLinearMetrics = true; - } else { - fHintingInstance = fontations_ffi::make_mono_hinting_instance( - fOutlines, fScale.y(), fBridgeNormalizedCoords); - fDoLinearMetrics = false; - } + // Hinting-reliant fonts exist that display incorrect contours when not executing their + // hinting instructions. Detect those and force-enable hinting for them. + if (fontations_ffi::hinting_reliant(fOutlines)) { + fHintingInstance = fontations_ffi::make_mono_hinting_instance( + fOutlines, fScale.y(), fBridgeNormalizedCoords); + fDoLinearMetrics = false; } else { - switch (fRec.getHinting()) { - case SkFontHinting::kNone: + if (SkMask::kBW_Format == fRec.fMaskFormat) { + if (fRec.getHinting() == SkFontHinting::kNone) { fHintingInstance = fontations_ffi::no_hinting_instance(); fDoLinearMetrics = true; - break; - case SkFontHinting::kSlight: - // Unhinted metrics. - fHintingInstance = fontations_ffi::make_hinting_instance( - fOutlines, - fScale.y(), - fBridgeNormalizedCoords, - true /* do_light_hinting */, - false /* do_lcd_antialiasing */, - false /* lcd_orientation_vertical */, - true /* force_autohinting */); - fDoLinearMetrics = true; - break; - case SkFontHinting::kNormal: - // No hinting to subpixel coordinates. - fHintingInstance = fontations_ffi::make_hinting_instance( - fOutlines, - fScale.y(), - fBridgeNormalizedCoords, - false /* do_light_hinting */, - false /* do_lcd_antialiasing */, - false /* lcd_orientation_vertical */, - forceAutohinting /* force_autohinting */); - break; - case SkFontHinting::kFull: - // Attempt to make use of hinting to subpixel coordinates. - fHintingInstance = fontations_ffi::make_hinting_instance( - fOutlines, - fScale.y(), - fBridgeNormalizedCoords, - false /* do_light_hinting */, - isLCD(fRec) /* do_lcd_antialiasing */, - SkToBool(fRec.fFlags & - SkScalerContext:: - kLCD_Vertical_Flag) /* lcd_orientation_vertical */, - forceAutohinting /* force_autohinting */); + } else { + fHintingInstance = fontations_ffi::make_mono_hinting_instance( + fOutlines, fScale.y(), fBridgeNormalizedCoords); + fDoLinearMetrics = false; + } + } else { + switch (fRec.getHinting()) { + case SkFontHinting::kNone: + fHintingInstance = fontations_ffi::no_hinting_instance(); + fDoLinearMetrics = true; + break; + case SkFontHinting::kSlight: + // Unhinted metrics. + if (autoHintingControl != + fontations_ffi::AutoHintingControl::ForceForGlyfAndCff) { + autoHintingControl = + fontations_ffi::AutoHintingControl::PreferAutoOverHintsForGlyf; + } + fHintingInstance = fontations_ffi::make_hinting_instance( + fOutlines, + fGlyphStyles, + fScale.y(), + fBridgeNormalizedCoords, + true /* do_light_hinting */, + false /* do_lcd_antialiasing */, + false /* lcd_orientation_vertical */, + autoHintingControl); + fDoLinearMetrics = true; + break; + case SkFontHinting::kNormal: + // No hinting to subpixel coordinates. + fHintingInstance = fontations_ffi::make_hinting_instance( + fOutlines, + fGlyphStyles, + fScale.y(), + fBridgeNormalizedCoords, + false /* do_light_hinting */, + false /* do_lcd_antialiasing */, + false /* lcd_orientation_vertical */, + autoHintingControl); + break; + case SkFontHinting::kFull: + // Attempt to make use of hinting to subpixel coordinates. + fHintingInstance = fontations_ffi::make_hinting_instance( + fOutlines, + fGlyphStyles, + fScale.y(), + fBridgeNormalizedCoords, + false /* do_light_hinting */, + isLCD(fRec) /* do_lcd_antialiasing */, + SkToBool(fRec.fFlags & + SkScalerContext:: + kLCD_Vertical_Flag) /* lcd_orientation_vertical */, + autoHintingControl); + } } } } + bool getContourHeightForLetter(SkUnichar letter, SkScalar& height) { + SkGlyphID glyphId; + rust::Slice codepointSlice{reinterpret_cast(&letter), 1}; + rust::Slice glyphSlice{reinterpret_cast(&glyphId), 1}; + fontations_ffi::lookup_glyph_or_zero(fBridgeFontRef, fMappingIndex, + codepointSlice, glyphSlice); + if (!glyphId) { + return false; + } + SkPath glyphPath; + if (!generateYScalePathForGlyphId(glyphId, &glyphPath, fScale.y(), *fHintingInstance)) { + return false; + } + height = glyphPath.getBounds().height(); + return true; + } + // yScale is only used if hintinInstance is set to Unhinted, // otherwise the size is controlled by the configured hintingInstance. // hintingInstance argument is needed as COLRv1 drawing performs unhinted, @@ -390,6 +455,11 @@ public: const fontations_ffi::BridgeHintingInstance& hintingInstance) { fontations_ffi::BridgeScalerMetrics scalerMetrics; + // See https://crbug.com/390889644 - while SkScalerContexts are single-thread + // in general, generateYScalePathForGlyphId() can be called from a COLRv1 + // SkDrawable as well (see generateDrawable()). For this reason access to the + // path extraction array needs to be made thread-safe here. + SkAutoMutexExclusive l(fPathMutex); // Keep allocations in check. The rust side pre-allocates a fixed amount, // and afer leaving this function, we shrink to the same amount. SK_AT_SCOPE_EXIT(fontations_ffi::shrink_verbs_points_if_needed(fPathVerbs, fPathPoints)); @@ -657,7 +727,11 @@ protected: SkASSERT(SkMask::kARGB32_Format != mask.fFormat); const bool doBGR = SkToBool(fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag); const bool doVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag); - const bool a8LCD = SkToBool(fRec.fFlags & SkScalerContext::kGenA8FromLCD_Flag); + // See https://issues.skia.org/issues/396360753 + // We would like Fontations anti-aliasing on a surface with unknown pixel geometry to + // look like the FreeType backend in order to avoid perceived regressions + // in sharpness, so we ignore SkScalerContext::kGenA8FromLCD_Flag in fRec.fFlags. + const bool a8LCD = false; const bool hairline = glyph.pathIsHairline(); // Path offseting for subpixel positioning is not needed here, @@ -775,6 +849,26 @@ protected: out_metrics->fXHeight = -metrics.x_height; out_metrics->fCapHeight = -metrics.cap_height; out_metrics->fFlags = 0; + + // Cap height synthesis. + if (!out_metrics->fCapHeight) { + SkScalar height; + if (getContourHeightForLetter('H', height)) { + out_metrics->fCapHeight = height; + } else { + out_metrics->fCapHeight = metrics.ascent; + } + } + + if (!out_metrics->fXHeight) { + SkScalar xHeight; + if (getContourHeightForLetter('x', xHeight)) { + out_metrics->fXHeight = xHeight; + } else { + out_metrics->fXHeight = metrics.ascent; + } + } + if (fontations_ffi::table_data(fBridgeFontRef, SkSetFourByteTag('f', 'v', 'a', 'r'), 0, @@ -809,11 +903,14 @@ private: const fontations_ffi::BridgeFontRef& fBridgeFontRef; const fontations_ffi::BridgeNormalizedCoords& fBridgeNormalizedCoords; const fontations_ffi::BridgeOutlineCollection& fOutlines; + const fontations_ffi::BridgeGlyphStyles& fGlyphStyles; + const fontations_ffi::BridgeMappingIndex& fMappingIndex; const SkSpan fPalette; rust::Box fHintingInstance; bool fDoLinearMetrics = false; // Keeping the path extraction target buffers around significantly avoids // allocation churn. + SkMutex fPathMutex; rust::Vec fPathVerbs; rust::Vec fPathPoints; @@ -884,12 +981,12 @@ std::unique_ptr SkTypeface_Fontations::onCreateScalerContext( std::unique_ptr SkTypeface_Fontations::onCreateScalerContextAsProxyTypeface( const SkScalerContextEffects& effects, const SkDescriptor* desc, - sk_sp realTypeface) const { + SkTypeface* proxyTypeface) const { return std::make_unique( - sk_ref_sp(const_cast(this)), + *this, effects, desc, - realTypeface ? realTypeface : sk_ref_sp(const_cast(this))); + proxyTypeface ? *proxyTypeface : *const_cast(this)); } std::unique_ptr SkTypeface_Fontations::onGetAdvancedMetrics() const { diff --git a/gfx/skia/skia/src/ports/SkTypeface_fontations_priv.h b/gfx/skia/skia/src/ports/SkTypeface_fontations_priv.h index f1b64324e7a6..572c2b8ada34 100644 --- a/gfx/skia/skia/src/ports/SkTypeface_fontations_priv.h +++ b/gfx/skia/skia/src/ports/SkTypeface_fontations_priv.h @@ -191,14 +191,17 @@ private: rust::Box&& mappingIndex, rust::Box&& normalizedCoords, rust::Box&& outlines, + rust::Box&& glyph_styles, rust::Vec&& palette); public: - const fontations_ffi::BridgeFontRef& getBridgeFontRef() { return *fBridgeFontRef; } - const fontations_ffi::BridgeNormalizedCoords& getBridgeNormalizedCoords() { + const fontations_ffi::BridgeFontRef& getBridgeFontRef() const { return *fBridgeFontRef; } + const fontations_ffi::BridgeNormalizedCoords& getBridgeNormalizedCoords() const { return *fBridgeNormalizedCoords; } - const fontations_ffi::BridgeOutlineCollection& getOutlines() { return *fOutlines; } + const fontations_ffi::BridgeOutlineCollection& getOutlines() const { return *fOutlines; } + const fontations_ffi::BridgeGlyphStyles& getGlyphStyles() const { return *fGlyphStyles; } + const fontations_ffi::BridgeMappingIndex& getMappingIndex() const { return *fMappingIndex; } SkSpan getPalette() const { return SkSpan(reinterpret_cast(fPalette.data()), fPalette.size()); } @@ -216,7 +219,7 @@ protected: std::unique_ptr onCreateScalerContextAsProxyTypeface( const SkScalerContextEffects&, const SkDescriptor*, - sk_sp) const override; + SkTypeface* proxyTypeface) const override; void onFilterRec(SkScalerContextRec*) const override; std::unique_ptr onGetAdvancedMetrics() const override; void onGetFontDescriptor(SkFontDescriptor*, bool*) const override; @@ -246,6 +249,7 @@ private: rust::Box fMappingIndex; rust::Box fBridgeNormalizedCoords; rust::Box fOutlines; + rust::Box fGlyphStyles; rust::Vec fPalette; mutable SkOnce fGlyphMasksMayNeedCurrentColorOnce; diff --git a/gfx/skia/skia/src/ports/SkTypeface_mac_ct.cpp b/gfx/skia/skia/src/ports/SkTypeface_mac_ct.cpp index 9ef885b83510..81e11e4f4542 100644 --- a/gfx/skia/skia/src/ports/SkTypeface_mac_ct.cpp +++ b/gfx/skia/skia/src/ports/SkTypeface_mac_ct.cpp @@ -67,19 +67,6 @@ using namespace skia_private; -// These attributes to set the palette not publically exported as of macOS 14.5. -// However, they appear to be available and functional back to at least macOS 10.15.7. -static CFStringRef getCTFontPaletteAttribute() { - static CFStringRef* kCTFontPaletteAttributePtr = - static_cast(dlsym(RTLD_DEFAULT, "kCTFontPaletteAttribute")); - return *kCTFontPaletteAttributePtr; -} -static CFStringRef getCTFontPaletteColorsAttribute() { - static CFStringRef* kCTFontPaletteColorsAttributePtr = - static_cast(dlsym(RTLD_DEFAULT, "kCTFontPaletteColorsAttribute")); - return *kCTFontPaletteColorsAttributePtr; -} - /** Assumes src and dst are not nullptr. */ void SkStringFromCFString(CFStringRef src, SkString* dst) { // Reserve enough room for the worst-case string, @@ -106,7 +93,6 @@ SK_GETCFTYPEID(CFArray); SK_GETCFTYPEID(CFBoolean); SK_GETCFTYPEID(CFDictionary); SK_GETCFTYPEID(CFNumber); -SK_GETCFTYPEID(CGColor); /* Checked dynamic downcast of CFTypeRef. * @@ -134,8 +120,7 @@ static bool SkCFDynamicCast(CFTypeRef cf, CF* cfAsCF, char const* name) { } return false; } - // CFTypeRef is `void const *` but CGColorRef is non-const `CGColor *` - *cfAsCF = static_cast(const_cast(cf)); + *cfAsCF = static_cast(cf); return true; } @@ -436,11 +421,10 @@ static void get_plane_glyph_map(const uint8_t* bits, CGGlyph glyphs[2] = {0, 0}; if (CTFontGetGlyphsForCharacters(ctFont, utf16, glyphs, count)) { SkASSERT(glyphs[1] == 0); - SkASSERT(glyphs[0] < glyphCount); // CTFontCopyCharacterSet and CTFontGetGlyphsForCharacters seem to add 'support' // for characters 0x9, 0xA, and 0xD mapping them to the glyph for character 0x20? // Prefer mappings to codepoints at or above 0x20. - if (glyphToUnicode[glyphs[0]] < 0x20) { + if (glyphs[0] < glyphCount && glyphToUnicode[glyphs[0]] < 0x20) { glyphToUnicode[glyphs[0]] = codepoint; } } @@ -905,8 +889,7 @@ sk_sp SkTypeface_Mac::onCopyTableData(SkFontTableTag tag) const { std::unique_ptr SkTypeface_Mac::onCreateScalerContext( const SkScalerContextEffects& effects, const SkDescriptor* desc) const { - return std::make_unique( - sk_ref_sp(const_cast(this)), effects, desc); + return std::make_unique(*const_cast(this), effects, desc); } void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const { @@ -1045,68 +1028,6 @@ void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc, desc->setPostscriptName(get_str(CTFontCopyPostScriptName(fFontRef.get()), &tmpStr)); desc->setStyle(this->fontStyle()); desc->setFactoryId(FactoryId); - - SkUniqueCFRef ctDesc(CTFontCopyFontDescriptor(fFontRef.get())); - SkUniqueCFRef attributes(CTFontDescriptorCopyAttributes(ctDesc.get())); - - int palette; - CFNumberRef paletteNumber; - CFTypeRef paletteRef = CFDictionaryGetValue(attributes.get(), getCTFontPaletteAttribute()); - if (!paletteRef) { - desc->setPaletteIndex(0); - } else if (SkCFNumberDynamicCast(paletteRef, &palette, &paletteNumber, "Palette index")) { - desc->setPaletteIndex(palette); - } - - CFTypeRef paletteOverrides = CFDictionaryGetValue(attributes.get(), - getCTFontPaletteColorsAttribute()); - if (paletteOverrides) { - CFDictionaryRef paletteOverrideDict; - if (SkCFDynamicCast(paletteOverrides, &paletteOverrideDict, "Palette")) { - CFIndex overrideCount = CFDictionaryGetCount(paletteOverrideDict); - - struct Context { - SkFontArguments::Palette::Override* overrides; - size_t currentOverride; - } context{ desc->setPaletteEntryOverrides(overrideCount), 0 }; - CFDictionaryApplyFunction(paletteOverrideDict, - [](const void* key, const void* value, void* ctx) -> void { - uint16_t index; - int indexInt; - CFNumberRef indexNumber; - CFTypeRef indexRef = static_cast(key); - if (!SkCFNumberDynamicCast(indexRef, &indexInt, &indexNumber, "Override index")) { - return; - } - if (!SkTFitsIn(indexInt)) { - return; - } - index = SkTo(indexInt); - - CGColorRef cgColor; - CFTypeRef colorRef = static_cast(value); - if (!SkCFDynamicCast(colorRef, &cgColor, "Palette color")) { - return; - } - if (CGColorGetNumberOfComponents(cgColor) != 4) { - return; - } - const CGFloat* components = CGColorGetComponents(cgColor); - SkColor4f color4f = {(float)components[0], (float)components[1], - (float)components[2], (float)components[3]}; - SkColor color = color4f.toSkColor(); - - Context& context = *static_cast(ctx); - context.overrides[context.currentOverride] = { index, color}; - ++context.currentOverride; - }, &context); - // If there were any early returns, set the rest of the overrides. - for (size_t i = context.currentOverride; i < SkToSizeT(overrideCount); ++i) { - context.overrides[i] = { 0xFFFF, SK_ColorBLACK }; - } - } - } - *isLocalStream = fIsFromStream; } @@ -1261,63 +1182,20 @@ static CTFontVariation ctvariation_from_SkFontArguments(CTFontRef ct, CFArrayRef opsz }; } -static CGColorRef CGColorForSkColor(CGColorSpaceRef rgbcs, SkColor color) { - SkColor4f color4f = SkColor4f::FromColor(color); - CGFloat components[4] = { - (CGFloat)color4f.fR, - (CGFloat)color4f.fG, - (CGFloat)color4f.fB, - (CGFloat)color4f.fA, - }; - return CGColorCreate(rgbcs, components); -} - -static bool apply_palette(CFMutableDictionaryRef attributes, - const SkFontArguments::Palette& palette) { - bool changedAttributes = false; - if (palette.index != 0 || palette.overrideCount) { - SkUniqueCFRef paletteIndex( - CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &palette.index)); - CFDictionarySetValue(attributes, getCTFontPaletteAttribute(), paletteIndex.get()); - changedAttributes = true; - } - - if (palette.overrideCount) { - SkUniqueCFRef overrides( - CFDictionaryCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks)); - SkUniqueCFRef rgb(CGColorSpaceCreateDeviceRGB()); - for (auto&& paletteOverride : SkSpan(palette.overrides, palette.overrideCount)) { - int paletteOverrideIndex = paletteOverride.index; - SkUniqueCFRef index( - CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt16Type, &paletteOverrideIndex)); - SkUniqueCFRef color(CGColorForSkColor(rgb.get(), paletteOverride.color)); - CFDictionarySetValue(overrides.get(), index.get(), color.get()); - } - if (CFDictionaryGetCount(overrides.get())) { - CFDictionarySetValue(attributes, getCTFontPaletteColorsAttribute(), overrides.get()); - changedAttributes = true; - } - } - - return changedAttributes; -} - sk_sp SkTypeface_Mac::onMakeClone(const SkFontArguments& args) const { - SkUniqueCFRef attributes( - CFDictionaryCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks)); - - bool changedAttributes = apply_palette(attributes.get(), args.getPalette()); - CTFontVariation ctVariation = ctvariation_from_SkFontArguments(fFontRef.get(), this->getVariationAxes(), args); - CTFontRef ctFont = fFontRef.get(); - SkUniqueCFRef wrongOpszFont; + + SkUniqueCFRef ctVariant; if (ctVariation.variation) { + SkUniqueCFRef attributes( + CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)); + + CTFontRef ctFont = fFontRef.get(); + SkUniqueCFRef wrongOpszFont; if (ctVariation.wrongOpszVariation) { // On macOS 11 cloning a system font with an opsz axis and not changing the // value of the opsz axis (either by setting it to the same value or not @@ -1332,20 +1210,16 @@ sk_sp SkTypeface_Mac::onMakeClone(const SkFontArguments& args) const CFDictionarySetValue(attributes.get(), kCTFontVariationAttribute, ctVariation.wrongOpszVariation.get()); SkUniqueCFRef varDesc( - CTFontDescriptorCreateWithAttributes(attributes.get())); + CTFontDescriptorCreateWithAttributes(attributes.get())); wrongOpszFont.reset(CTFontCreateCopyWithAttributes(ctFont, 0, nullptr, varDesc.get())); ctFont = wrongOpszFont.get(); } + CFDictionarySetValue(attributes.get(), kCTFontVariationAttribute, ctVariation.variation.get()); - changedAttributes = true; - } - - SkUniqueCFRef ctVariant; - if (changedAttributes) { - SkUniqueCFRef desc( - CTFontDescriptorCreateWithAttributes(attributes.get())); - ctVariant.reset(CTFontCreateCopyWithAttributes(ctFont, 0, nullptr, desc.get())); + SkUniqueCFRef varDesc( + CTFontDescriptorCreateWithAttributes(attributes.get())); + ctVariant.reset(CTFontCreateCopyWithAttributes(ctFont, 0, nullptr, varDesc.get())); } else { ctVariant.reset((CTFontRef)CFRetain(fFontRef.get())); } @@ -1425,32 +1299,28 @@ sk_sp SkTypeface_Mac::MakeFromStream(std::unique_ptr return nullptr; } - SkUniqueCFRef attributes( - CFDictionaryCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks)); - - bool changedAttributes = apply_palette(attributes.get(), args.getPalette()); - SkUniqueCFRef ctVariant; CTFontVariation ctVariation; - if (args.getVariationDesignPosition().coordinateCount != 0) { + if (args.getVariationDesignPosition().coordinateCount == 0) { + ctVariant = std::move(ct); + } else { SkUniqueCFRef axes(CTFontCopyVariationAxes(ct.get())); ctVariation = ctvariation_from_SkFontArguments(ct.get(), axes.get(), args); if (ctVariation.variation) { + SkUniqueCFRef attributes( + CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)); CFDictionaryAddValue(attributes.get(), kCTFontVariationAttribute, ctVariation.variation.get()); - changedAttributes = true; + SkUniqueCFRef varDesc( + CTFontDescriptorCreateWithAttributes(attributes.get())); + ctVariant.reset(CTFontCreateCopyWithAttributes(ct.get(), 0, nullptr, varDesc.get())); + } else { + ctVariant = std::move(ct); } } - if (changedAttributes) { - SkUniqueCFRef desc( - CTFontDescriptorCreateWithAttributes(attributes.get())); - ctVariant.reset(CTFontCreateCopyWithAttributes(ct.get(), 0, nullptr, desc.get())); - } else { - ctVariant = std::move(ct); - } if (!ctVariant) { return nullptr; } diff --git a/gfx/skia/skia/src/ports/SkTypeface_proxy.cpp b/gfx/skia/skia/src/ports/SkTypeface_proxy.cpp new file mode 100644 index 000000000000..1e97fafe57da --- /dev/null +++ b/gfx/skia/skia/src/ports/SkTypeface_proxy.cpp @@ -0,0 +1,138 @@ +/* + * Copyright 2024 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "src/core/SkAdvancedTypefaceMetrics.h" +#include "src/ports/SkTypeface_proxy.h" + +#include +#include + +int SkTypeface_proxy::onGetUPEM() const { return fRealTypeface->getUnitsPerEm(); } + +std::unique_ptr SkTypeface_proxy::onOpenStream(int* ttcIndex) const { + return fRealTypeface->onOpenStream(ttcIndex); +} + +sk_sp SkTypeface_proxy::onMakeClone(const SkFontArguments& args) const { + return fRealTypeface->onMakeClone(args); +} + +bool SkTypeface_proxy::onGlyphMaskNeedsCurrentColor() const { + return fRealTypeface->glyphMaskNeedsCurrentColor(); +} + +int SkTypeface_proxy::onGetVariationDesignPosition( + SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const { + return fRealTypeface->onGetVariationDesignPosition(coordinates, coordinateCount); +} + +int SkTypeface_proxy::onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[], + int parameterCount) const { + return fRealTypeface->onGetVariationDesignParameters(parameters, parameterCount); +} + +SkFontStyle SkTypeface_proxy::onGetFontStyle() const { + return fRealTypeface->onGetFontStyle(); +} + +bool SkTypeface_proxy::onGetFixedPitch() const { + return fRealTypeface->onGetFixedPitch(); +} + +void SkTypeface_proxy::onGetFamilyName(SkString* familyName) const { + fRealTypeface->onGetFamilyName(familyName); +} + +bool SkTypeface_proxy::onGetPostScriptName(SkString* postScriptName) const { + return fRealTypeface->getPostScriptName(postScriptName); +} + +int SkTypeface_proxy::onGetResourceName(SkString* resourceName) const { + return fRealTypeface->getResourceName(resourceName); +} + +SkTypeface::LocalizedStrings* SkTypeface_proxy::onCreateFamilyNameIterator() const { + return fRealTypeface->createFamilyNameIterator(); +} + +int SkTypeface_proxy::onGetTableTags(SkFontTableTag tags[]) const { + return fRealTypeface->getTableTags(tags); +} + +size_t SkTypeface_proxy::onGetTableData(SkFontTableTag tag, size_t offset, size_t length, void* data) const { + return fRealTypeface->getTableData(tag, offset, length, data); +} + +std::unique_ptr SkTypeface_proxy::onCreateScalerContext( + const SkScalerContextEffects& effects, const SkDescriptor* desc) const { + return std::make_unique( + fRealTypeface->onCreateScalerContextAsProxyTypeface(effects, desc, const_cast(this)), + *const_cast(this), + effects, + desc); +} + +void SkTypeface_proxy::onFilterRec(SkScalerContextRec* rec) const { + fRealTypeface->onFilterRec(rec); +} + +void SkTypeface_proxy::onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) const { + fRealTypeface->onGetFontDescriptor(desc, serialize); +} + +void SkTypeface_proxy::getGlyphToUnicodeMap(SkUnichar* glyphToUnicode) const { + fRealTypeface->getGlyphToUnicodeMap(glyphToUnicode); +} + +void SkTypeface_proxy::getPostScriptGlyphNames(SkString* names) const { + return fRealTypeface->getPostScriptGlyphNames(names); +} + +std::unique_ptr SkTypeface_proxy::onGetAdvancedMetrics() const { + return fRealTypeface->onGetAdvancedMetrics(); +} + +void SkTypeface_proxy::onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const { + fRealTypeface->unicharsToGlyphs(chars, count, glyphs); +} + +int SkTypeface_proxy::onCountGlyphs() const { return fRealTypeface->countGlyphs(); } + +void* SkTypeface_proxy::onGetCTFontRef() const { return fRealTypeface->onGetCTFontRef(); } + +bool SkTypeface_proxy::onGetKerningPairAdjustments(const SkGlyphID glyphs[], int count, + int32_t adjustments[]) const { + return fRealTypeface->onGetKerningPairAdjustments(glyphs, count, adjustments); +} + +SkScalerContext_proxy::SkScalerContext_proxy(std::unique_ptr realScalerContext, + SkTypeface_proxy& proxyTypeface, + const SkScalerContextEffects& effects, + const SkDescriptor* desc) + : SkScalerContext(proxyTypeface, effects, desc) + , fRealScalerContext(std::move(realScalerContext)) +{ } + +SkScalerContext::GlyphMetrics SkScalerContext_proxy::generateMetrics(const SkGlyph& glyph, SkArenaAlloc* alloc) { + return fRealScalerContext->generateMetrics(glyph, alloc); +} + +void SkScalerContext_proxy::generateImage(const SkGlyph& glyph, void* imageBuffer) { + fRealScalerContext->generateImage(glyph, imageBuffer); +} + +bool SkScalerContext_proxy::generatePath(const SkGlyph& glyph, SkPath* path, bool* modified) { + return fRealScalerContext->generatePath(glyph, path, modified); +} + +sk_sp SkScalerContext_proxy::generateDrawable(const SkGlyph& glyph) { + return fRealScalerContext->generateDrawable(glyph); +} + +void SkScalerContext_proxy::generateFontMetrics(SkFontMetrics* metrics) { + fRealScalerContext->generateFontMetrics(metrics); +} diff --git a/gfx/skia/skia/src/ports/SkTypeface_proxy.h b/gfx/skia/skia/src/ports/SkTypeface_proxy.h new file mode 100644 index 000000000000..6d759b6d00b5 --- /dev/null +++ b/gfx/skia/skia/src/ports/SkTypeface_proxy.h @@ -0,0 +1,81 @@ +/* + * 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 SkTypeface_proxy_DEFINED +#define SkTypeface_proxy_DEFINED +#include "include/core/SkStream.h" +#include "include/core/SkString.h" +#include "src/core/SkScalerContext.h" + +#include +#include +#include +#include +#include +#include + +class SkTypeface_proxy; +class SkScalerContext_proxy : public SkScalerContext { +public: + SkScalerContext_proxy(std::unique_ptr realScalerContext, + SkTypeface_proxy& proxyTypeface, + const SkScalerContextEffects& effects, + const SkDescriptor* desc); + + ~SkScalerContext_proxy() override = default; +protected: + SkScalerContext::GlyphMetrics generateMetrics(const SkGlyph&, SkArenaAlloc* alloc) override; + void generateImage(const SkGlyph& glyph, void* imageBuffer) override; + bool generatePath(const SkGlyph& glyph, SkPath* path, bool* modified) override; + sk_sp generateDrawable(const SkGlyph& glyph) override; + void generateFontMetrics(SkFontMetrics* metrics) override; +private: + std::unique_ptr fRealScalerContext; +}; + +class SkTypeface_proxy : public SkTypeface { +public: + SkTypeface_proxy(sk_sp realTypeface, + const SkFontStyle& style, bool isFixedPitch = false) + : SkTypeface(style, isFixedPitch) + , fRealTypeface(std::move(realTypeface)) { SkASSERT_RELEASE(fRealTypeface); } + +protected: + int onGetUPEM() const override; + std::unique_ptr onOpenStream(int* ttcIndex) const override; + sk_sp onMakeClone(const SkFontArguments& args) const override; + bool onGlyphMaskNeedsCurrentColor() const override; + int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const override; + int onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[], + int parameterCount) const override; + SkFontStyle onGetFontStyle() const override; + bool onGetFixedPitch() const override; + void onGetFamilyName(SkString* familyName) const override; + bool onGetPostScriptName(SkString* postScriptName) const override; + int onGetResourceName(SkString* resourceName) const override; + SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override; + int onGetTableTags(SkFontTableTag tags[]) const override; + size_t onGetTableData(SkFontTableTag, size_t offset, size_t length, void* data) const override; + std::unique_ptr onCreateScalerContext( + const SkScalerContextEffects& effects, const SkDescriptor* desc) const override; + void onFilterRec(SkScalerContextRec* rec) const override; + void onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) const override; + void getGlyphToUnicodeMap(SkUnichar* unichar) const override; + void getPostScriptGlyphNames(SkString* glyphNames) const override; + std::unique_ptr onGetAdvancedMetrics() const override; + void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override; + int onCountGlyphs() const override; + void* onGetCTFontRef() const override; + bool onGetKerningPairAdjustments(const SkGlyphID glyphs[], + int count, + int32_t adjustments[]) const override; +private: + sk_sp fRealTypeface; +}; + +#endif // SkTypeface_proxy_DEFINED diff --git a/gfx/skia/skia/src/ports/SkTypeface_win_dw.cpp b/gfx/skia/skia/src/ports/SkTypeface_win_dw.cpp index ceecbd5f4368..45abee6fd10e 100644 --- a/gfx/skia/skia/src/ports/SkTypeface_win_dw.cpp +++ b/gfx/skia/skia/src/ports/SkTypeface_win_dw.cpp @@ -261,6 +261,50 @@ bool DWriteFontTypeface::onGetPostScriptName(SkString* skPostScriptName) const { return true; } +int DWriteFontTypeface::onGetResourceName(SkString* resourceName) const { + UINT32 numFiles = 0; + HRZM(fDWriteFontFace->GetFiles(&numFiles, nullptr), + "Could not get number of font files."); + if (numFiles < 1 || !resourceName) { + return numFiles; + } + + auto fontFiles = std::make_unique[]>(numFiles); + HR_GENERAL(fDWriteFontFace->GetFiles(&numFiles, &fontFiles[0]), + "Could not get font files.", numFiles); + + IDWriteFontFile* fontFile = fontFiles[0].get(); + const void* fontFileKey; + UINT32 fontFileKeySize; + HR_GENERAL(fontFile->GetReferenceKey(&fontFileKey, &fontFileKeySize), + "Could not get font file reference key.", numFiles); + + SkTScopedComPtr fontFileLoader; + HR_GENERAL(fontFile->GetLoader(&fontFileLoader), + "Could not get font file loader.", numFiles); + + SkTScopedComPtr localFontFileLoader; + HR_GENERAL(fontFileLoader->QueryInterface(&localFontFileLoader), + nullptr, numFiles); + + UINT32 fontFilePathLen; + HR_GENERAL(localFontFileLoader->GetFilePathLengthFromKey(fontFileKey, fontFileKeySize, + &fontFilePathLen), + "Could not get file path length.", numFiles); + + SkSMallocWCHAR fontFilePath(static_cast(fontFilePathLen)+1); + HR_GENERAL(localFontFileLoader->GetFilePathFromKey(fontFileKey, fontFileKeySize, + fontFilePath, fontFilePathLen+1), + "Could not get file path.", numFiles); + + SkString localResourceName; + HR_GENERAL(sk_wchar_to_skstring(fontFilePath.get(), fontFilePathLen, &localResourceName), + nullptr, numFiles); + *resourceName = std::move(localResourceName); + + return numFiles; +} + void DWriteFontTypeface::onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) const { // Get the family name. @@ -607,7 +651,7 @@ std::unique_ptr DWriteFontTypeface::onCreateScalerContext( const SkScalerContextEffects& effects, const SkDescriptor* desc) const { return std::make_unique( - sk_ref_sp(const_cast(this)), effects, desc); + *const_cast(this), effects, desc); } void DWriteFontTypeface::onFilterRec(SkScalerContextRec* rec) const { diff --git a/gfx/skia/skia/src/ports/SkTypeface_win_dw.h b/gfx/skia/skia/src/ports/SkTypeface_win_dw.h index 387fea7764e9..5ab66e1e5727 100644 --- a/gfx/skia/skia/src/ports/SkTypeface_win_dw.h +++ b/gfx/skia/skia/src/ports/SkTypeface_win_dw.h @@ -157,6 +157,7 @@ protected: int onGetUPEM() const override; void onGetFamilyName(SkString* familyName) const override; bool onGetPostScriptName(SkString*) const override; + int onGetResourceName(SkString*) const override; SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override; bool onGlyphMaskNeedsCurrentColor() const override; int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], diff --git a/gfx/skia/skia/src/shaders/SkBlendShader.cpp b/gfx/skia/skia/src/shaders/SkBlendShader.cpp index 2ec12f77a5a6..fccb774f5dbf 100644 --- a/gfx/skia/skia/src/shaders/SkBlendShader.cpp +++ b/gfx/skia/skia/src/shaders/SkBlendShader.cpp @@ -60,8 +60,8 @@ static float* append_two_shaders(const SkStageRec& rec, SkShader* s0, SkShader* s1) { struct Storage { - float fCoords[2 * SkRasterPipeline_kMaxStride]; - float fRes0[4 * SkRasterPipeline_kMaxStride]; + float fCoords[2 * SkRasterPipelineContexts::kMaxStride]; + float fRes0[4 * SkRasterPipelineContexts::kMaxStride]; }; auto storage = rec.fAlloc->make(); diff --git a/gfx/skia/skia/src/shaders/SkColorShader.cpp b/gfx/skia/skia/src/shaders/SkColorShader.cpp index 90b5754e9357..d2bd0f241154 100644 --- a/gfx/skia/skia/src/shaders/SkColorShader.cpp +++ b/gfx/skia/skia/src/shaders/SkColorShader.cpp @@ -11,12 +11,13 @@ #include "include/core/SkColorSpace.h" #include "include/core/SkData.h" #include "include/core/SkFlattenable.h" +#include "include/core/SkRefCnt.h" #include "include/core/SkShader.h" #include "include/private/base/SkFloatingPoint.h" -#include "include/private/base/SkTPin.h" #include "src/core/SkColorSpacePriv.h" #include "src/core/SkColorSpaceXformSteps.h" #include "src/core/SkEffectPriv.h" +#include "src/core/SkPicturePriv.h" #include "src/core/SkRasterPipeline.h" #include "src/core/SkReadBuffer.h" #include "src/core/SkWriteBuffer.h" @@ -24,88 +25,73 @@ #include -SkColorShader::SkColorShader(SkColor c) : fColor(c) {} +namespace { -bool SkColorShader::isOpaque() const { - return SkColorGetA(fColor) == 255; +sk_sp legacy_color4_create_proc(SkReadBuffer& buffer) { + if (buffer.validate(buffer.isVersionLT(SkPicturePriv::Version::kCombineColorShaders))) { + // Only SKPs older than this version should be referring to this create proc. + SkColor4f color; + sk_sp colorSpace; + buffer.readColor4f(&color); + if (buffer.readBool()) { + sk_sp data = buffer.readByteArrayAsData(); + colorSpace = data ? SkColorSpace::Deserialize(data->data(), data->size()) : nullptr; + } + return SkShaders::Color(color, std::move(colorSpace)); + } + + return nullptr; } +} // anonymous namespace + sk_sp SkColorShader::CreateProc(SkReadBuffer& buffer) { - return sk_make_sp(buffer.readColor()); + if (buffer.isVersionLT(SkPicturePriv::Version::kCombineColorShaders)) { + // Stored an 8-bit color only. + return SkShaders::Color(buffer.readColor()); + } else { + // Stores a floating-point color in sRGB. + SkColor4f color; + buffer.readColor4f(&color); + return SkShaders::Color(color, SkColorSpace::MakeSRGB()); + } } void SkColorShader::flatten(SkWriteBuffer& buffer) const { - buffer.writeColor(fColor); -} - -SkColor4Shader::SkColor4Shader(const SkColor4f& color, sk_sp space) - : fColorSpace(std::move(space)) - , fColor({color.fR, color.fG, color.fB, SkTPin(color.fA, 0.0f, 1.0f)}) -{} - -sk_sp SkColor4Shader::CreateProc(SkReadBuffer& buffer) { - SkColor4f color; - sk_sp colorSpace; - buffer.readColor4f(&color); - if (buffer.readBool()) { - sk_sp data = buffer.readByteArrayAsData(); - colorSpace = data ? SkColorSpace::Deserialize(data->data(), data->size()) : nullptr; - } - return SkShaders::Color(color, std::move(colorSpace)); -} - -void SkColor4Shader::flatten(SkWriteBuffer& buffer) const { buffer.writeColor4f(fColor); - sk_sp colorSpaceData = fColorSpace ? fColorSpace->serialize() : nullptr; - if (colorSpaceData) { - buffer.writeBool(true); - buffer.writeDataAsByteArray(colorSpaceData.get()); - } else { - buffer.writeBool(false); - } } bool SkColorShader::appendStages(const SkStageRec& rec, const SkShaders::MatrixRec&) const { - SkColor4f color = SkColor4f::FromColor(fColor); + SkColor4f color = fColor; SkColorSpaceXformSteps(sk_srgb_singleton(), kUnpremul_SkAlphaType, - rec.fDstCS, kUnpremul_SkAlphaType).apply(color.vec()); - rec.fPipeline->appendConstantColor(rec.fAlloc, color.premul().vec()); - return true; -} - -bool SkColor4Shader::appendStages(const SkStageRec& rec, const SkShaders::MatrixRec&) const { - SkColor4f color = fColor; - SkColorSpaceXformSteps(fColorSpace.get(), kUnpremul_SkAlphaType, - rec.fDstCS, kUnpremul_SkAlphaType).apply(color.vec()); - rec.fPipeline->appendConstantColor(rec.fAlloc, color.premul().vec()); - return true; -} - -bool SkColor4Shader::onAsLuminanceColor(SkColor4f* lum) const { - SkColor4f color = fColor; - SkColorSpaceXformSteps(fColorSpace.get(), kUnpremul_SkAlphaType, - sk_srgb_singleton(), kUnpremul_SkAlphaType).apply(color.vec()); - *lum = color; + rec.fDstCS, kPremul_SkAlphaType).apply(color.vec()); + rec.fPipeline->appendConstantColor(rec.fAlloc, color.vec()); return true; } ///////////////////////////////////////////////////////////////////////////////////////////////// -void SkRegisterColor4ShaderFlattenable() { - SK_REGISTER_FLATTENABLE(SkColor4Shader); -} - void SkRegisterColorShaderFlattenable() { SK_REGISTER_FLATTENABLE(SkColorShader); + SkFlattenable::Register("SkColorShader4", legacy_color4_create_proc); } namespace SkShaders { -sk_sp Color(SkColor color) { return sk_make_sp(color); } +sk_sp Color(SkColor color) { + return Color(SkColor4f::FromColor(color), SkColorSpace::MakeSRGB()); +} sk_sp Color(const SkColor4f& color, sk_sp space) { if (!SkIsFinite(color.vec(), 4)) { return nullptr; } - return sk_make_sp(color, std::move(space)); + + // Convert to sRGB to simplify what must be stored, remaining unpremul until the final dst + // color space is known during actual shading. Also pin the alpha to [0,1]. + SkColor4f srgb = color.pinAlpha(); + SkColorSpaceXformSteps(space.get(), kUnpremul_SkAlphaType, + sk_srgb_singleton(), kUnpremul_SkAlphaType).apply(srgb.vec()); + + return sk_make_sp(srgb); } } // namespace SkShaders diff --git a/gfx/skia/skia/src/shaders/SkColorShader.h b/gfx/skia/skia/src/shaders/SkColorShader.h index 8fb823aec989..c80f3c748324 100644 --- a/gfx/skia/skia/src/shaders/SkColorShader.h +++ b/gfx/skia/skia/src/shaders/SkColorShader.h @@ -9,9 +9,7 @@ #define SkColorShader_DEFINED #include "include/core/SkColor.h" -#include "include/core/SkColorSpace.h" #include "include/core/SkFlattenable.h" -#include "include/core/SkRefCnt.h" #include "src/shaders/SkShaderBase.h" class SkReadBuffer; @@ -19,24 +17,23 @@ class SkWriteBuffer; struct SkStageRec; /** \class SkColorShader - A Shader that represents a single color. In general, this effect can be - accomplished by just using the color field on the paint, but if an - actual shader object is needed, this provides that feature. + A Shader that represents a single color. In general, this effect can be accomplished by just + using the color field on the paint, but if an actual shader object is needed, this provides that + feature. Note: like all shaders, at draw time the paint's alpha will be respected, and is + applied to the specified color. */ class SkColorShader : public SkShaderBase { public: - /** Create a ColorShader that ignores the color in the paint, and uses the - specified color. Note: like all shaders, at draw time the paint's alpha - will be respected, and is applied to the specified color. + /** Create a ColorShader wrapping the given sRGB color. */ - explicit SkColorShader(SkColor c); + explicit SkColorShader(const SkColor4f& c) : fColor(c) {} - bool isOpaque() const override; + bool isOpaque() const override { return fColor.isOpaque(); } bool isConstant() const override { return true; } ShaderType type() const override { return ShaderType::kColor; } - SkColor color() const { return fColor; } + const SkColor4f& color() const { return fColor; } private: friend void ::SkRegisterColorShaderFlattenable(); @@ -45,36 +42,14 @@ private: void flatten(SkWriteBuffer&) const override; bool onAsLuminanceColor(SkColor4f* lum) const override { - *lum = SkColor4f::FromColor(fColor); + *lum = fColor; return true; } bool appendStages(const SkStageRec&, const SkShaders::MatrixRec&) const override; - SkColor fColor; -}; - -class SkColor4Shader : public SkShaderBase { -public: - SkColor4Shader(const SkColor4f&, sk_sp); - - bool isOpaque() const override { return fColor.isOpaque(); } - bool isConstant() const override { return true; } - - ShaderType type() const override { return ShaderType::kColor4; } - - sk_sp colorSpace() const { return fColorSpace; } - SkColor4f color() const { return fColor; } - -private: - friend void ::SkRegisterColor4ShaderFlattenable(); - SK_FLATTENABLE_HOOKS(SkColor4Shader) - - void flatten(SkWriteBuffer&) const override; - bool onAsLuminanceColor(SkColor4f* lum) const override; - bool appendStages(const SkStageRec&, const SkShaders::MatrixRec&) const override; - - sk_sp fColorSpace; + // The color is stored in extended sRGB, regardless of the original color space that was + // passed into SkShaders::Color(). const SkColor4f fColor; }; diff --git a/gfx/skia/skia/src/shaders/SkCoordClampShader.cpp b/gfx/skia/skia/src/shaders/SkCoordClampShader.cpp index c40877ce61b4..8e3fc300d7e4 100644 --- a/gfx/skia/skia/src/shaders/SkCoordClampShader.cpp +++ b/gfx/skia/skia/src/shaders/SkCoordClampShader.cpp @@ -43,7 +43,7 @@ bool SkCoordClampShader::appendStages(const SkStageRec& rec, // Strictly speaking, childMRec's total matrix is not valid. It is only valid inside the subset // rectangle. However, we don't mark it as such because we want the "total matrix is valid" // behavior in SkImageShader for filtering. - auto clampCtx = rec.fAlloc->make(); + auto clampCtx = rec.fAlloc->make(); *clampCtx = {fSubset.fLeft, fSubset.fTop, fSubset.fRight, fSubset.fBottom}; rec.fPipeline->append(SkRasterPipelineOp::clamp_x_and_y, clampCtx); return as_SB(fShader)->appendStages(rec, *childMRec); diff --git a/gfx/skia/skia/src/shaders/SkImageShader.cpp b/gfx/skia/skia/src/shaders/SkImageShader.cpp index 616721bf777b..f2c0ccc6c7f8 100644 --- a/gfx/skia/skia/src/shaders/SkImageShader.cpp +++ b/gfx/skia/skia/src/shaders/SkImageShader.cpp @@ -429,16 +429,16 @@ namespace { struct MipLevelHelper { SkPixmap pm; SkMatrix inv; - SkRasterPipeline_GatherCtx* gather; - SkRasterPipeline_TileCtx* limitX; - SkRasterPipeline_TileCtx* limitY; - SkRasterPipeline_DecalTileCtx* decalCtx = nullptr; + SkRasterPipelineContexts::GatherCtx* gather; + SkRasterPipelineContexts::TileCtx* limitX; + SkRasterPipelineContexts::TileCtx* limitY; + SkRasterPipelineContexts::DecalTileCtx* decalCtx = nullptr; void allocAndInit(SkArenaAlloc* alloc, const SkSamplingOptions& sampling, SkTileMode tileModeX, SkTileMode tileModeY) { - gather = alloc->make(); + gather = alloc->make(); gather->pixels = pm.addr(); gather->stride = pm.rowBytesAsPixels(); gather->width = pm.width(); @@ -449,8 +449,8 @@ struct MipLevelHelper { .getColMajor(gather->weights); } - limitX = alloc->make(); - limitY = alloc->make(); + limitX = alloc->make(); + limitY = alloc->make(); limitX->scale = pm.width(); limitX->invScale = 1.0f / pm.width(); limitY->scale = pm.height(); @@ -475,7 +475,7 @@ struct MipLevelHelper { } if (tileModeX == SkTileMode::kDecal || tileModeY == SkTileMode::kDecal) { - decalCtx = alloc->make(); + decalCtx = alloc->make(); decalCtx->limit_x = limitX->scale; decalCtx->limit_y = limitY->scale; @@ -550,11 +550,11 @@ bool SkImageShader::appendStages(const SkStageRec& rec, const SkShaders::MatrixR upper.allocAndInit(alloc, sampling, fTileModeX, fTileModeY); MipLevelHelper lower; - SkRasterPipeline_MipmapCtx* mipmapCtx = nullptr; + SkRasterPipelineContexts::MipmapCtx* mipmapCtx = nullptr; float lowerWeight = access->lowerWeight(); if (lowerWeight > 0) { std::tie(lower.pm, lower.inv) = access->lowerLevel(); - mipmapCtx = alloc->make(); + mipmapCtx = alloc->make(); mipmapCtx->lowerWeight = lowerWeight; mipmapCtx->scaleX = static_cast(lower.pm.width()) / upper.pm.width(); mipmapCtx->scaleY = static_cast(lower.pm.height()) / upper.pm.height(); @@ -735,7 +735,8 @@ bool SkImageShader::appendStages(const SkStageRec& rec, const SkShaders::MatrixR } // This context can be shared by both levels when doing linear mipmap filtering - SkRasterPipeline_SamplerCtx* sampler = alloc->make(); + SkRasterPipelineContexts::SamplerCtx* sampler = + alloc->make(); auto sample = [&](SkRasterPipelineOp setup_x, SkRasterPipelineOp setup_y, diff --git a/gfx/skia/skia/src/shaders/SkPerlinNoiseShaderImpl.cpp b/gfx/skia/skia/src/shaders/SkPerlinNoiseShaderImpl.cpp index e84376352b88..03e769e2cc7b 100644 --- a/gfx/skia/skia/src/shaders/SkPerlinNoiseShaderImpl.cpp +++ b/gfx/skia/skia/src/shaders/SkPerlinNoiseShaderImpl.cpp @@ -91,7 +91,7 @@ bool SkPerlinNoiseShader::appendStages(const SkStageRec& rec, const_cast(this)->fPaintingData = this->getPaintingData(); }); - auto* ctx = rec.fAlloc->make(); + auto* ctx = rec.fAlloc->make(); ctx->noiseType = fType; ctx->baseFrequencyX = fPaintingData->fBaseFrequency.fX; ctx->baseFrequencyY = fPaintingData->fBaseFrequency.fY; diff --git a/gfx/skia/skia/src/shaders/SkRuntimeShader.cpp b/gfx/skia/skia/src/shaders/SkRuntimeShader.cpp index 5a49c2351565..70a48662eab5 100644 --- a/gfx/skia/skia/src/shaders/SkRuntimeShader.cpp +++ b/gfx/skia/skia/src/shaders/SkRuntimeShader.cpp @@ -19,6 +19,7 @@ #include "include/sksl/SkSLDebugTrace.h" #include "src/base/SkTLazy.h" #include "src/core/SkEffectPriv.h" +#include "src/core/SkKnownRuntimeEffects.h" #include "src/core/SkPicturePriv.h" #include "src/core/SkReadBuffer.h" #include "src/core/SkRuntimeEffectPriv.h" @@ -100,12 +101,6 @@ bool SkRuntimeShader::appendStages(const SkStageRec& rec, const SkShaders::Matri return false; } -void SkRuntimeShader::flatten(SkWriteBuffer& buffer) const { - buffer.writeString(fEffect->source().c_str()); - buffer.writeDataAsByteArray(this->uniformData(nullptr).get()); - SkRuntimeEffectPriv::WriteChildEffects(buffer, fChildren); -} - sk_sp SkRuntimeShader::uniformData(const SkColorSpace* dstCS) const { if (fUniformData) { return fUniformData; @@ -118,13 +113,43 @@ sk_sp SkRuntimeShader::uniformData(const SkColorSpace* dstCS) cons return uniforms; } +void SkRuntimeShader::flatten(SkWriteBuffer& buffer) const { + if (SkKnownRuntimeEffects::IsSkiaKnownRuntimeEffect(fEffect->fStableKey)) { + // We only serialize Skia-internal stableKeys. First party stable keys are not serialized. + buffer.write32(fEffect->fStableKey); + } else { + buffer.write32(0); + buffer.writeString(fEffect->source().c_str()); + } + buffer.writeDataAsByteArray(this->uniformData(nullptr).get()); + SkRuntimeEffectPriv::WriteChildEffects(buffer, fChildren); +} + sk_sp SkRuntimeShader::CreateProc(SkReadBuffer& buffer) { if (!buffer.validate(buffer.allowSkSL())) { return nullptr; } - SkString sksl; - buffer.readString(&sksl); + sk_sp effect; + if (!buffer.isVersionLT(SkPicturePriv::kSerializeStableKeys)) { + uint32_t candidateStableKey = buffer.readUInt(); + effect = SkKnownRuntimeEffects::MaybeGetKnownRuntimeEffect(candidateStableKey); + if (!effect && !buffer.validate(candidateStableKey == 0)) { + return nullptr; + } + } + + if (!effect) { + SkString sksl; + buffer.readString(&sksl); + effect = SkMakeCachedRuntimeEffect(SkRuntimeEffect::MakeForShader, std::move(sksl)); + } + if constexpr (!kLenientSkSLDeserialization) { + if (!buffer.validate(effect != nullptr)) { + return nullptr; + } + } + sk_sp uniforms = buffer.readByteArrayAsData(); SkTLazy localM; @@ -135,13 +160,6 @@ sk_sp SkRuntimeShader::CreateProc(SkReadBuffer& buffer) { } } - auto effect = SkMakeCachedRuntimeEffect(SkRuntimeEffect::MakeForShader, std::move(sksl)); - if constexpr (!kLenientSkSLDeserialization) { - if (!buffer.validate(effect != nullptr)) { - return nullptr; - } - } - skia_private::STArray<4, SkRuntimeEffect::ChildPtr> children; if (!SkRuntimeEffectPriv::ReadChildEffects(buffer, effect.get(), &children)) { return nullptr; diff --git a/gfx/skia/skia/src/shaders/SkShaderBase.cpp b/gfx/skia/skia/src/shaders/SkShaderBase.cpp index f274420bf495..88df72b193c9 100644 --- a/gfx/skia/skia/src/shaders/SkShaderBase.cpp +++ b/gfx/skia/skia/src/shaders/SkShaderBase.cpp @@ -124,7 +124,7 @@ bool SkShaderBase::ContextRec::isLegacyCompatible(SkColorSpace* shaderColorSpace // always premul (or opaque). (And those "or opaque" caveats won't make any difference here.) SkAlphaType shaderAT = kPremul_SkAlphaType, dstAT = kPremul_SkAlphaType; return 0 == - SkColorSpaceXformSteps{shaderColorSpace, shaderAT, fDstColorSpace, dstAT}.flags.mask(); + SkColorSpaceXformSteps{shaderColorSpace, shaderAT, fDstColorSpace, dstAT}.fFlags.mask(); } sk_sp SkShaderBase::makeAsALocalMatrixShader(SkMatrix*) const { return nullptr; } diff --git a/gfx/skia/skia/src/shaders/SkShaderBase.h b/gfx/skia/skia/src/shaders/SkShaderBase.h index 18080a232e75..6d0c3d0d03f9 100644 --- a/gfx/skia/skia/src/shaders/SkShaderBase.h +++ b/gfx/skia/skia/src/shaders/SkShaderBase.h @@ -163,7 +163,6 @@ private: M(Blend) \ M(CTM) \ M(Color) \ - M(Color4) \ M(ColorFilter) \ M(CoordClamp) \ M(Empty) \ @@ -423,7 +422,6 @@ inline const SkShaderBase* as_SB(const sk_sp& shader) { } void SkRegisterBlendShaderFlattenable(); -void SkRegisterColor4ShaderFlattenable(); void SkRegisterColorShaderFlattenable(); void SkRegisterCoordClampShaderFlattenable(); void SkRegisterEmptyShaderFlattenable(); diff --git a/gfx/skia/skia/src/shaders/SkTriColorShader.cpp b/gfx/skia/skia/src/shaders/SkTriColorShader.cpp index c3f354ae6c9f..559a812fa64c 100644 --- a/gfx/skia/skia/src/shaders/SkTriColorShader.cpp +++ b/gfx/skia/skia/src/shaders/SkTriColorShader.cpp @@ -10,8 +10,8 @@ #include "include/core/SkMatrix.h" #include "include/core/SkPoint.h" #include "include/core/SkTypes.h" -#include "include/private/SkColorData.h" #include "src/base/SkVx.h" +#include "src/core/SkColorData.h" #include "src/core/SkEffectPriv.h" #include "src/core/SkRasterPipeline.h" #include "src/core/SkRasterPipelineOpList.h" diff --git a/gfx/skia/skia/src/shaders/SkTriColorShader.h b/gfx/skia/skia/src/shaders/SkTriColorShader.h index 68065102a836..06c31f2ef1fb 100644 --- a/gfx/skia/skia/src/shaders/SkTriColorShader.h +++ b/gfx/skia/skia/src/shaders/SkTriColorShader.h @@ -9,7 +9,7 @@ #include "include/core/SkMatrix.h" #include "include/core/SkTypes.h" -#include "include/private/SkColorData.h" +#include "src/core/SkColorData.h" #include "src/shaders/SkShaderBase.h" struct SkPoint; diff --git a/gfx/skia/skia/src/shaders/gradients/SkConicalGradient.cpp b/gfx/skia/skia/src/shaders/gradients/SkConicalGradient.cpp index a2ae4e94475b..7dc868db271c 100644 --- a/gfx/skia/skia/src/shaders/gradients/SkConicalGradient.cpp +++ b/gfx/skia/skia/src/shaders/gradients/SkConicalGradient.cpp @@ -213,7 +213,7 @@ void SkConicalGradient::appendGradientStages(SkArenaAlloc* alloc, } if (fType == Type::kStrip) { - auto* ctx = alloc->make(); + auto* ctx = alloc->make(); SkScalar scaledR0 = fRadius1 / this->getCenterX1(); ctx->fP0 = scaledR0 * scaledR0; p->append(SkRasterPipelineOp::xy_to_2pt_conical_strip, ctx); @@ -222,7 +222,7 @@ void SkConicalGradient::appendGradientStages(SkArenaAlloc* alloc, return; } - auto* ctx = alloc->make(); + auto* ctx = alloc->make(); ctx->fP0 = 1 / fFocalData.fR1; ctx->fP1 = fFocalData.fFocalX; diff --git a/gfx/skia/skia/src/shaders/gradients/SkGradientBaseShader.cpp b/gfx/skia/skia/src/shaders/gradients/SkGradientBaseShader.cpp index 47541cca55cc..4bf2477b406f 100644 --- a/gfx/skia/skia/src/shaders/gradients/SkGradientBaseShader.cpp +++ b/gfx/skia/skia/src/shaders/gradients/SkGradientBaseShader.cpp @@ -15,15 +15,16 @@ #include "include/core/SkImageInfo.h" #include "include/core/SkShader.h" #include "include/core/SkTileMode.h" -#include "include/private/SkColorData.h" #include "include/private/base/SkFloatingPoint.h" #include "include/private/base/SkMalloc.h" #include "include/private/base/SkTArray.h" #include "include/private/base/SkTPin.h" #include "include/private/base/SkTo.h" +#include "modules/skcms/skcms.h" #include "src/base/SkArenaAlloc.h" #include "src/base/SkFloatBits.h" #include "src/base/SkVx.h" +#include "src/core/SkColorData.h" #include "src/core/SkColorSpacePriv.h" #include "src/core/SkColorSpaceXformSteps.h" #include "src/core/SkConvertPixels.h" @@ -302,71 +303,81 @@ SkGradientBaseShader::SkGradientBaseShader(const Descriptor& desc, const SkMatri SkGradientBaseShader::~SkGradientBaseShader() {} -static void add_stop_color(SkRasterPipeline_GradientCtx* ctx, +static void add_stop_color(SkRasterPipelineContexts::GradientCtx* ctx, size_t stop, - SkPMColor4f Fs, - SkPMColor4f Bs) { - (ctx->fs[0])[stop] = Fs.fR; - (ctx->fs[1])[stop] = Fs.fG; - (ctx->fs[2])[stop] = Fs.fB; - (ctx->fs[3])[stop] = Fs.fA; + const SkPMColor4f& Fs, + const SkPMColor4f& Bs) { + (ctx->factors[0])[stop] = Fs.fR; + (ctx->factors[1])[stop] = Fs.fG; + (ctx->factors[2])[stop] = Fs.fB; + (ctx->factors[3])[stop] = Fs.fA; - (ctx->bs[0])[stop] = Bs.fR; - (ctx->bs[1])[stop] = Bs.fG; - (ctx->bs[2])[stop] = Bs.fB; - (ctx->bs[3])[stop] = Bs.fA; + (ctx->biases[0])[stop] = Bs.fR; + (ctx->biases[1])[stop] = Bs.fG; + (ctx->biases[2])[stop] = Bs.fB; + (ctx->biases[3])[stop] = Bs.fA; } -static void add_const_color(SkRasterPipeline_GradientCtx* ctx, size_t stop, SkPMColor4f color) { +static void add_const_color(SkRasterPipelineContexts::GradientCtx* ctx, + size_t stop, + const SkPMColor4f& color) { add_stop_color(ctx, stop, {0, 0, 0, 0}, color); } // Calculate a factor F and a bias B so that color = F*t + B when t is in range of -// the stop. Assume that the distance between stops is 1/gapCount. -static void init_stop_evenly(SkRasterPipeline_GradientCtx* ctx, +// the stop. Assume that all stops have width 1/gapCount and the stop parameter +// refers to the nth stop. +static void init_stop_evenly(SkRasterPipelineContexts::GradientCtx* ctx, float gapCount, size_t stop, - SkPMColor4f c_l, - SkPMColor4f c_r) { - // Clankium's GCC 4.9 targeting ARMv7 is barfing when we use Sk4f math here, so go scalar... - SkPMColor4f Fs = { - (c_r.fR - c_l.fR) * gapCount, - (c_r.fG - c_l.fG) * gapCount, - (c_r.fB - c_l.fB) * gapCount, - (c_r.fA - c_l.fA) * gapCount, - }; - SkPMColor4f Bs = { - c_l.fR - Fs.fR * (stop / gapCount), - c_l.fG - Fs.fG * (stop / gapCount), - c_l.fB - Fs.fB * (stop / gapCount), - c_l.fA - Fs.fA * (stop / gapCount), - }; - add_stop_color(ctx, stop, Fs, Bs); + const SkPMColor4f& leftC, + const SkPMColor4f& rightC) { + auto left = skvx::float4::Load(leftC.vec()); + auto right = skvx::float4::Load(rightC.vec()); + + SkPMColor4f factor, bias; + + // We start with the following 2 linear equations and 2 unknowns (factor, bias) + // left = factor * t + bias + // right = factor * (t + gap) + bias + // gap = 1/gapCount + // t = gap * stop + + // right - left = factor * (t + gap) - factor * t + // right - left = factor * gap + // factor = (right - left) / gap (and gap = 1/gapCount) + auto factor4 = ((right - left) * gapCount); + (left - (factor4 * (stop / gapCount))).store(bias.vec()); + factor4.store(factor.vec()); + + add_stop_color(ctx, stop, factor, bias); } -// For each stop we calculate a bias B and a scale factor F, such that -// for any t between stops n and n+1, the color we want is B[n] + F[n]*t. -static void init_stop_pos(SkRasterPipeline_GradientCtx* ctx, +// Calculate a factor F and a bias B so that color = F*t + B when t is in range of +// the stop. Unlike init_stop_evenly, this handles stops +static void init_stop_pos(SkRasterPipelineContexts::GradientCtx* ctx, size_t stop, float t_l, - float c_scale, - SkPMColor4f c_l, - SkPMColor4f c_r) { - // See note about Clankium's old compiler in init_stop_evenly(). - SkPMColor4f Fs = { - (c_r.fR - c_l.fR) * c_scale, - (c_r.fG - c_l.fG) * c_scale, - (c_r.fB - c_l.fB) * c_scale, - (c_r.fA - c_l.fA) * c_scale, - }; - SkPMColor4f Bs = { - c_l.fR - Fs.fR * t_l, - c_l.fG - Fs.fG * t_l, - c_l.fB - Fs.fB * t_l, - c_l.fA - Fs.fA * t_l, - }; + float gapReciprocal, + const SkPMColor4f& leftC, + const SkPMColor4f& rightC) { + // gapReciprocal is 1/gapWidth. If two colors were on top of each other, we should + // have skipped that as a "stop". + SkASSERT(SkIsFinite(gapReciprocal)); + + auto left = skvx::float4::Load(leftC.vec()); + auto right = skvx::float4::Load(rightC.vec()); + + SkPMColor4f factor, bias; + + // See init_stop_evenly for this derivation, noting that gap = 1/gapReciprocal + // and t = t_l + auto factor4 = ((right - left) * gapReciprocal); + (left - (factor4 * t_l)).store(bias.vec()); + factor4.store(factor.vec()); + ctx->ts[stop] = t_l; - add_stop_color(ctx, stop, Fs, Bs); + add_stop_color(ctx, stop, factor, bias); } void SkGradientBaseShader::AppendGradientFillStages(SkRasterPipeline* p, @@ -378,83 +389,104 @@ void SkGradientBaseShader::AppendGradientFillStages(SkRasterPipeline* p, if (count == 2 && positions == nullptr) { const SkPMColor4f c_l = pmColors[0], c_r = pmColors[1]; - // See F and B below. - auto ctx = alloc->make(); - (skvx::float4::Load(c_r.vec()) - skvx::float4::Load(c_l.vec())).store(ctx->f); - (skvx::float4::Load(c_l.vec())).store(ctx->b); + auto ctx = alloc->make(); + (skvx::float4::Load(c_r.vec()) - skvx::float4::Load(c_l.vec())).store(ctx->factor); + (skvx::float4::Load(c_l.vec())).store(ctx->bias); p->append(SkRasterPipelineOp::evenly_spaced_2_stop_gradient, ctx); - } else { - auto* ctx = alloc->make(); - - // Note: In order to handle clamps in search, the search assumes a stop conceptully placed - // at -inf. Therefore, the max number of stops is fColorCount+1. - for (int i = 0; i < 4; i++) { - // Allocate at least at for the AVX2 gather from a YMM register. - ctx->fs[i] = alloc->makeArray(std::max(count + 1, 8)); - ctx->bs[i] = alloc->makeArray(std::max(count + 1, 8)); - } - - if (positions == nullptr) { - // Handle evenly distributed stops. - - size_t stopCount = count; - float gapCount = stopCount - 1; - - SkPMColor4f c_l = pmColors[0]; - for (size_t i = 0; i < stopCount - 1; i++) { - SkPMColor4f c_r = pmColors[i + 1]; - init_stop_evenly(ctx, gapCount, i, c_l, c_r); - c_l = c_r; - } - add_const_color(ctx, stopCount - 1, c_l); - - ctx->stopCount = stopCount; - p->append(SkRasterPipelineOp::evenly_spaced_gradient, ctx); - } else { - // Handle arbitrary stops. - - ctx->ts = alloc->makeArray(count + 1); - - // Remove the default stops inserted by SkGradientBaseShader::SkGradientBaseShader - // because they are naturally handled by the search method. - int firstStop; - int lastStop; - if (count > 2) { - firstStop = pmColors[0] != pmColors[1] ? 0 : 1; - lastStop = pmColors[count - 2] != pmColors[count - 1] ? count - 1 : count - 2; - } else { - firstStop = 0; - lastStop = 1; - } - - size_t stopCount = 0; - float t_l = positions[firstStop]; - SkPMColor4f c_l = pmColors[firstStop]; - add_const_color(ctx, stopCount++, c_l); - // N.B. lastStop is the index of the last stop, not one after. - for (int i = firstStop; i < lastStop; i++) { - float t_r = positions[i + 1]; - SkPMColor4f c_r = pmColors[i + 1]; - SkASSERT(t_l <= t_r); - if (t_l < t_r) { - float c_scale = sk_ieee_float_divide(1, t_r - t_l); - if (SkIsFinite(c_scale)) { - init_stop_pos(ctx, stopCount, t_l, c_scale, c_l, c_r); - stopCount += 1; - } - } - t_l = t_r; - c_l = c_r; - } - - ctx->ts[stopCount] = t_l; - add_const_color(ctx, stopCount++, c_l); - - ctx->stopCount = stopCount; - p->append(SkRasterPipelineOp::gradient, ctx); - } + return; } + // Linear gradients with evenly spaced stops involve doing calculations to interpolate + // between color n and color n+1 based on t (in range [0.0,1.0]). + // color_n * (t - t_n) / gap_n + color_{n+1} * (t_{n+1} - t) / gap_n + // We could just stick the colors and the gaps calculation in RP and do this calculation, + // but instead we can precompute things to make the RP calculation simpler and faster. + // For each gap, we calculate four linear equations in the form y = m*x + b, or rather + // color_channel = factor * t + bias + // We do this pre-computation in init_stop_evenly and init_stop_pos. + + auto* ctx = alloc->make(); + + // Allocate at least enough for the AVX2 gather from a YMM register. + constexpr int kMaxRegisterSize = 8; + + // There are n - 1 gaps between n colors plus 2 regions to the left and right + // of the gradient to account for colors. For evenly spaced gradients, we cheat + // and skip the left gap, using one block of floats unused. + const size_t factorBiasFloats = std::max(count + 1, kMaxRegisterSize); + const size_t tsForArbitraryStops = count + 1; + using SkRasterPipelineContexts::kRGBAChannels; + + // We need space for all factors and biases, and while we are at it, some space + // if we need to include the arbitrary stops. + const size_t toAlloc = 2 * kRGBAChannels * factorBiasFloats + tsForArbitraryStops; + float* gradientCtxBuffer = alloc->makeArray(toAlloc); + for (size_t i = 0; i < kRGBAChannels; i++) { + ctx->factors[i] = gradientCtxBuffer; + gradientCtxBuffer += factorBiasFloats; + ctx->biases[i] = gradientCtxBuffer; + gradientCtxBuffer += factorBiasFloats; + } + + if (positions == nullptr) { + // Handle evenly distributed stops. + + size_t stopCount = count; + float gapCount = stopCount - 1; + + SkPMColor4f c_l = pmColors[0]; + for (size_t i = 0; i < gapCount; i++) { + SkPMColor4f c_r = pmColors[i + 1]; + init_stop_evenly(ctx, gapCount, i, c_l, c_r); + c_l = c_r; + } + add_const_color(ctx, stopCount - 1, c_l); + + ctx->stopCount = stopCount; + p->append(SkRasterPipelineOp::evenly_spaced_gradient, ctx); + return; + } + + // Handle arbitrary stops. + ctx->ts = gradientCtxBuffer; + + // Remove the default stops inserted by SkGradientBaseShader::SkGradientBaseShader + // because they are naturally handled by the search method. + int firstStop; + int lastStop; + if (count > 2) { + firstStop = pmColors[0] != pmColors[1] ? 0 : 1; + lastStop = pmColors[count - 2] != pmColors[count - 1] ? count - 1 : count - 2; + } else { + firstStop = 0; + lastStop = 1; + } + + size_t stopCount = 0; + float t_l = positions[firstStop]; + SkPMColor4f c_l = pmColors[firstStop]; + add_const_color(ctx, stopCount++, c_l); + + for (int i = firstStop; i < lastStop; i++) { + float t_r = positions[i + 1]; + SkPMColor4f c_r = pmColors[i + 1]; + SkASSERT(t_l <= t_r); + if (t_l < t_r) { + float c_scale = sk_ieee_float_divide(1, t_r - t_l); + if (SkIsFinite(c_scale)) { + init_stop_pos(ctx, stopCount, t_l, c_scale, c_l, c_r); + stopCount += 1; + } + } + t_l = t_r; + c_l = c_r; + } + + ctx->ts[stopCount] = t_l; + add_const_color(ctx, stopCount++, c_l); + + ctx->stopCount = stopCount; + p->append(SkRasterPipelineOp::gradient, ctx); } void SkGradientBaseShader::AppendInterpolatedToDstStages(SkRasterPipeline* p, @@ -530,7 +562,7 @@ bool SkGradientBaseShader::appendStages(const SkStageRec& rec, const SkShaders::MatrixRec& mRec) const { SkRasterPipeline* p = rec.fPipeline; SkArenaAlloc* alloc = rec.fAlloc; - SkRasterPipeline_DecalTileCtx* decal_ctx = nullptr; + SkRasterPipelineContexts::DecalTileCtx* decal_ctx = nullptr; std::optional newMRec = mRec.apply(rec, fPtsToUnit); if (!newMRec.has_value()) { @@ -549,7 +581,7 @@ bool SkGradientBaseShader::appendStages(const SkStageRec& rec, p->append(SkRasterPipelineOp::repeat_x_1); break; case SkTileMode::kDecal: - decal_ctx = alloc->make(); + decal_ctx = alloc->make(); decal_ctx->limit_x = SkBits2Float(SkFloat2Bits(1.0f) + 1); // reuse mask + limit_x stage, or create a custom decal_1 that just stores the mask p->append(SkRasterPipelineOp::decal_x, decal_ctx); @@ -641,6 +673,21 @@ static sk_sp intermediate_color_space(SkGradientShader::Interpolat // part of the conversion is a matrix multiply, which could be absorbed into the // color space xform. return SkColorSpace::MakeSRGBLinear(); + + // These rectangular color spaces have their own transfer curves. + case ColorSpace::kDisplayP3: + return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDisplayP3); + + case ColorSpace::kRec2020: + return SkColorSpace::MakeRGB(SkNamedTransferFn::kRec2020, SkNamedGamut::kRec2020); + + case ColorSpace::kProphotoRGB: + static skcms_Matrix3x3 lin_proPhoto_to_XYZ_D50; + SkNamedPrimaries::kProPhotoRGB.toXYZD50(&lin_proPhoto_to_XYZ_D50); + return SkColorSpace::MakeRGB(SkNamedTransferFn::kProPhotoRGB, lin_proPhoto_to_XYZ_D50); + + case ColorSpace::kA98RGB: + return SkColorSpace::MakeRGB(SkNamedTransferFn::kA98RGB, SkNamedGamut::kAdobeRGB); } SkUNREACHABLE; } @@ -960,7 +1007,7 @@ SkColor4fXformer::SkColor4fXformer(const SkGradientBaseShader* shader, } SkColorConverter::SkColorConverter(const SkColor* colors, int count) { - const float ONE_OVER_255 = 1.f / 255; + constexpr float ONE_OVER_255 = 1.f / 255; for (int i = 0; i < count; ++i) { fColors4f.push_back({SkColorGetR(colors[i]) * ONE_OVER_255, SkColorGetG(colors[i]) * ONE_OVER_255, diff --git a/gfx/skia/skia/src/shaders/gradients/SkGradientBaseShader.h b/gfx/skia/skia/src/shaders/gradients/SkGradientBaseShader.h index 12e604c30286..ef60d31cf6da 100644 --- a/gfx/skia/skia/src/shaders/gradients/SkGradientBaseShader.h +++ b/gfx/skia/skia/src/shaders/gradients/SkGradientBaseShader.h @@ -15,10 +15,10 @@ #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" #include "include/effects/SkGradientShader.h" -#include "include/private/SkColorData.h" #include "include/private/base/SkAssert.h" #include "include/private/base/SkTArray.h" #include "include/private/base/SkTemplates.h" +#include "src/core/SkColorData.h" #include "src/shaders/SkShaderBase.h" #include @@ -139,9 +139,10 @@ public: SkColor4f* fColors; // points into fStorage SkScalar* fPositions; // points into fStorage, or nullptr - int fColorCount; // length of fColors (and fPositions, if not nullptr) sk_sp fColorSpace; // color space of gradient stops Interpolation fInterpolation; + + int fColorCount; // length of fColors (and fPositions, if not nullptr) bool fFirstStopIsImplicit; bool fLastStopIsImplicit; diff --git a/gfx/skia/skia/src/sksl/SkSLProgramSettings.h b/gfx/skia/skia/src/sksl/SkSLProgramSettings.h index fd75dcac96d9..4fe06d785d64 100644 --- a/gfx/skia/skia/src/sksl/SkSLProgramSettings.h +++ b/gfx/skia/skia/src/sksl/SkSLProgramSettings.h @@ -65,7 +65,7 @@ struct ProgramSettings { // correctness bool fValidateSPIRV = true; // If true, any synthetic uniforms must use push constant syntax - bool fUsePushConstants = false; + bool fUseVulkanPushConstantsForGaneshRTAdjust = false; // TODO(skia:11209) - Replace this with a "promised" capabilities? // Sets a maximum SkSL version. Compilation will fail if the program uses features that aren't // allowed at the requested version. For instance, a valid program must have fully-unrollable diff --git a/gfx/skia/skia/src/sksl/codegen/SkSLCodeGenerator.h b/gfx/skia/skia/src/sksl/codegen/SkSLCodeGenerator.h index a500a24ea87b..9f4c843a16bb 100644 --- a/gfx/skia/skia/src/sksl/codegen/SkSLCodeGenerator.h +++ b/gfx/skia/skia/src/sksl/codegen/SkSLCodeGenerator.h @@ -44,9 +44,6 @@ public: void setOutputStream(OutputStream* output) { fOut = output; } protected: -#if defined(SK_USE_LEGACY_MIPMAP_LOD_BIAS) - static constexpr float kSharpenTexturesBias = -.5f; -#else // For SkMipmapMode::kLinear we want a bias such that when the unbiased LOD value is // midway between levels we select just the larger level, i.e. a bias of -.5. However, using // this bias with kNearest mode with a draw that is a perfect power of two downscale puts us @@ -57,7 +54,6 @@ protected: // we are using -.475. They do not at -.48. All other GPUs passed tests with -.499. Though, at // this time the bias is not implemented in the MSL codegen and so iOS/Metal was not tested. static constexpr float kSharpenTexturesBias = -.475f; -#endif const Program& fProgram; Context fContext; diff --git a/gfx/skia/skia/src/sksl/codegen/SkSLGLSLCodeGenerator.cpp b/gfx/skia/skia/src/sksl/codegen/SkSLGLSLCodeGenerator.cpp index 8d0dec5ee1f7..82ce3ebcc0c9 100644 --- a/gfx/skia/skia/src/sksl/codegen/SkSLGLSLCodeGenerator.cpp +++ b/gfx/skia/skia/src/sksl/codegen/SkSLGLSLCodeGenerator.cpp @@ -502,47 +502,44 @@ void GLSLCodeGenerator::writeInverseSqrtHack(const Expression& x) { this->write("))"); } -static constexpr char kDeterminant2[] = R"( -float _determinant2(mat2 m) { -return m[0].x*m[1].y - m[0].y*m[1].x; -} -)"; +static constexpr char kDeterminant2[] = +"float _determinant2(mat2 m) {" + "return m[0].x*m[1].y - m[0].y*m[1].x;" +"}"; -static constexpr char kDeterminant3[] = R"( -float _determinant3(mat3 m) { -float - a00 = m[0].x, a01 = m[0].y, a02 = m[0].z, - a10 = m[1].x, a11 = m[1].y, a12 = m[1].z, - a20 = m[2].x, a21 = m[2].y, a22 = m[2].z, - b01 = a22*a11 - a12*a21, - b11 =-a22*a10 + a12*a20, - b21 = a21*a10 - a11*a20; -return a00*b01 + a01*b11 + a02*b21; -} -)"; +static constexpr char kDeterminant3[] = +"float _determinant3(mat3 m) {" + "float " + "a00 = m[0].x, a01 = m[0].y, a02 = m[0].z," + "a10 = m[1].x, a11 = m[1].y, a12 = m[1].z," + "a20 = m[2].x, a21 = m[2].y, a22 = m[2].z," + "b01 = a22*a11 - a12*a21," + "b11 =-a22*a10 + a12*a20," + "b21 = a21*a10 - a11*a20;" + "return a00*b01 + a01*b11 + a02*b21;" +"}"; -static constexpr char kDeterminant4[] = R"( -mat4 _determinant4(mat4 m) { -float - a00 = m[0].x, a01 = m[0].y, a02 = m[0].z, a03 = m[0].w, - a10 = m[1].x, a11 = m[1].y, a12 = m[1].z, a13 = m[1].w, - a20 = m[2].x, a21 = m[2].y, a22 = m[2].z, a23 = m[2].w, - a30 = m[3].x, a31 = m[3].y, a32 = m[3].z, a33 = m[3].w, - b00 = a00*a11 - a01*a10, - b01 = a00*a12 - a02*a10, - b02 = a00*a13 - a03*a10, - b03 = a01*a12 - a02*a11, - b04 = a01*a13 - a03*a11, - b05 = a02*a13 - a03*a12, - b06 = a20*a31 - a21*a30, - b07 = a20*a32 - a22*a30, - b08 = a20*a33 - a23*a30, - b09 = a21*a32 - a22*a31, - b10 = a21*a33 - a23*a31, - b11 = a22*a33 - a23*a32; -return b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06; -} -)"; +static constexpr char kDeterminant4[] = +"mat4 _determinant4(mat4 m) {" + "float " + "a00 = m[0].x, a01 = m[0].y, a02 = m[0].z, a03 = m[0].w," + "a10 = m[1].x, a11 = m[1].y, a12 = m[1].z, a13 = m[1].w," + "a20 = m[2].x, a21 = m[2].y, a22 = m[2].z, a23 = m[2].w," + "a30 = m[3].x, a31 = m[3].y, a32 = m[3].z, a33 = m[3].w," + "b00 = a00*a11 - a01*a10," + "b01 = a00*a12 - a02*a10," + "b02 = a00*a13 - a03*a10," + "b03 = a01*a12 - a02*a11," + "b04 = a01*a13 - a03*a11," + "b05 = a02*a13 - a03*a12," + "b06 = a20*a31 - a21*a30," + "b07 = a20*a32 - a22*a30," + "b08 = a20*a33 - a23*a30," + "b09 = a21*a32 - a22*a31," + "b10 = a21*a33 - a23*a31," + "b11 = a22*a33 - a23*a32;" + "return b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06;" +"}"; void GLSLCodeGenerator::writeDeterminantHack(const Expression& mat) { const Type& type = mat.type(); @@ -575,68 +572,65 @@ void GLSLCodeGenerator::writeDeterminantHack(const Expression& mat) { this->write(")"); } -static constexpr char kInverse2[] = R"( -mat2 _inverse2(mat2 m) { -return mat2(m[1].y, -m[0].y, -m[1].x, m[0].x) / (m[0].x * m[1].y - m[0].y * m[1].x); -} -)"; +static constexpr char kInverse2[] = +"mat2 _inverse2(mat2 m) {" + "return mat2(m[1].y, -m[0].y, -m[1].x, m[0].x) / (m[0].x * m[1].y - m[0].y * m[1].x);" +"}"; -static constexpr char kInverse3[] = R"( -mat3 _inverse3(mat3 m) { -float - a00 = m[0].x, a01 = m[0].y, a02 = m[0].z, - a10 = m[1].x, a11 = m[1].y, a12 = m[1].z, - a20 = m[2].x, a21 = m[2].y, a22 = m[2].z, - b01 = a22*a11 - a12*a21, - b11 =-a22*a10 + a12*a20, - b21 = a21*a10 - a11*a20, - det = a00*b01 + a01*b11 + a02*b21; -return mat3( - b01, (-a22*a01 + a02*a21), ( a12*a01 - a02*a11), - b11, ( a22*a00 - a02*a20), (-a12*a00 + a02*a10), - b21, (-a21*a00 + a01*a20), ( a11*a00 - a01*a10)) / det; -} -)"; +static constexpr char kInverse3[] = +"mat3 _inverse3(mat3 m) {" + "float " + "a00 = m[0].x, a01 = m[0].y, a02 = m[0].z," + "a10 = m[1].x, a11 = m[1].y, a12 = m[1].z," + "a20 = m[2].x, a21 = m[2].y, a22 = m[2].z," + "b01 = a22*a11 - a12*a21," + "b11 =-a22*a10 + a12*a20," + "b21 = a21*a10 - a11*a20," + "det = a00*b01 + a01*b11 + a02*b21;" + "return mat3(" + "b01, (-a22*a01 + a02*a21), ( a12*a01 - a02*a11)," + "b11, ( a22*a00 - a02*a20), (-a12*a00 + a02*a10)," + "b21, (-a21*a00 + a01*a20), ( a11*a00 - a01*a10)) / det;" +"}"; -static constexpr char kInverse4[] = R"( -mat4 _inverse4(mat4 m) { -float - a00 = m[0].x, a01 = m[0].y, a02 = m[0].z, a03 = m[0].w, - a10 = m[1].x, a11 = m[1].y, a12 = m[1].z, a13 = m[1].w, - a20 = m[2].x, a21 = m[2].y, a22 = m[2].z, a23 = m[2].w, - a30 = m[3].x, a31 = m[3].y, a32 = m[3].z, a33 = m[3].w, - b00 = a00*a11 - a01*a10, - b01 = a00*a12 - a02*a10, - b02 = a00*a13 - a03*a10, - b03 = a01*a12 - a02*a11, - b04 = a01*a13 - a03*a11, - b05 = a02*a13 - a03*a12, - b06 = a20*a31 - a21*a30, - b07 = a20*a32 - a22*a30, - b08 = a20*a33 - a23*a30, - b09 = a21*a32 - a22*a31, - b10 = a21*a33 - a23*a31, - b11 = a22*a33 - a23*a32, - det = b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06; -return mat4( - a11*b11 - a12*b10 + a13*b09, - a02*b10 - a01*b11 - a03*b09, - a31*b05 - a32*b04 + a33*b03, - a22*b04 - a21*b05 - a23*b03, - a12*b08 - a10*b11 - a13*b07, - a00*b11 - a02*b08 + a03*b07, - a32*b02 - a30*b05 - a33*b01, - a20*b05 - a22*b02 + a23*b01, - a10*b10 - a11*b08 + a13*b06, - a01*b08 - a00*b10 - a03*b06, - a30*b04 - a31*b02 + a33*b00, - a21*b02 - a20*b04 - a23*b00, - a11*b07 - a10*b09 - a12*b06, - a00*b09 - a01*b07 + a02*b06, - a31*b01 - a30*b03 - a32*b00, - a20*b03 - a21*b01 + a22*b00) / det; -} -)"; +static constexpr char kInverse4[] = +"mat4 _inverse4(mat4 m) {" + "float " + "a00 = m[0].x, a01 = m[0].y, a02 = m[0].z, a03 = m[0].w," + "a10 = m[1].x, a11 = m[1].y, a12 = m[1].z, a13 = m[1].w," + "a20 = m[2].x, a21 = m[2].y, a22 = m[2].z, a23 = m[2].w," + "a30 = m[3].x, a31 = m[3].y, a32 = m[3].z, a33 = m[3].w," + "b00 = a00*a11 - a01*a10," + "b01 = a00*a12 - a02*a10," + "b02 = a00*a13 - a03*a10," + "b03 = a01*a12 - a02*a11," + "b04 = a01*a13 - a03*a11," + "b05 = a02*a13 - a03*a12," + "b06 = a20*a31 - a21*a30," + "b07 = a20*a32 - a22*a30," + "b08 = a20*a33 - a23*a30," + "b09 = a21*a32 - a22*a31," + "b10 = a21*a33 - a23*a31," + "b11 = a22*a33 - a23*a32," + "det = b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06;" + "return mat4(" + "a11*b11 - a12*b10 + a13*b09," + "a02*b10 - a01*b11 - a03*b09," + "a31*b05 - a32*b04 + a33*b03," + "a22*b04 - a21*b05 - a23*b03," + "a12*b08 - a10*b11 - a13*b07," + "a00*b11 - a02*b08 + a03*b07," + "a32*b02 - a30*b05 - a33*b01," + "a20*b05 - a22*b02 + a23*b01," + "a10*b10 - a11*b08 + a13*b06," + "a01*b08 - a00*b10 - a03*b06," + "a30*b04 - a31*b02 + a33*b00," + "a21*b02 - a20*b04 - a23*b00," + "a11*b07 - a10*b09 - a12*b06," + "a00*b09 - a01*b07 + a02*b06," + "a31*b01 - a30*b03 - a32*b00," + "a20*b03 - a21*b01 + a22*b00) / det;" +"}"; void GLSLCodeGenerator::writeInverseHack(const Expression& mat) { const Type& type = mat.type(); diff --git a/gfx/skia/skia/src/sksl/codegen/SkSLMetalCodeGenerator.cpp b/gfx/skia/skia/src/sksl/codegen/SkSLMetalCodeGenerator.cpp index 265a717fb636..b3e21b61b12f 100644 --- a/gfx/skia/skia/src/sksl/codegen/SkSLMetalCodeGenerator.cpp +++ b/gfx/skia/skia/src/sksl/codegen/SkSLMetalCodeGenerator.cpp @@ -732,71 +732,68 @@ void MetalCodeGenerator::writeFunctionCall(const FunctionCall& c) { } } -static constexpr char kInverse2x2[] = R"( -template -matrix mat2_inverse(matrix m) { -return matrix(m[1].y, -m[0].y, -m[1].x, m[0].x) * (1/determinant(m)); -} -)"; +static constexpr char kInverse2x2[] = +"template " +"matrix mat2_inverse(matrix m) {" + "return matrix(m[1].y, -m[0].y, -m[1].x, m[0].x) * (1/determinant(m));" +"}"; -static constexpr char kInverse3x3[] = R"( -template -matrix mat3_inverse(matrix m) { -T - a00 = m[0].x, a01 = m[0].y, a02 = m[0].z, - a10 = m[1].x, a11 = m[1].y, a12 = m[1].z, - a20 = m[2].x, a21 = m[2].y, a22 = m[2].z, - b01 = a22*a11 - a12*a21, - b11 = -a22*a10 + a12*a20, - b21 = a21*a10 - a11*a20, - det = a00*b01 + a01*b11 + a02*b21; -return matrix( - b01, (-a22*a01 + a02*a21), ( a12*a01 - a02*a11), - b11, ( a22*a00 - a02*a20), (-a12*a00 + a02*a10), - b21, (-a21*a00 + a01*a20), ( a11*a00 - a01*a10)) * (1/det); -} -)"; +static constexpr char kInverse3x3[] = +"template " +"matrix mat3_inverse(matrix m) {" + "T " + "a00 = m[0].x, a01 = m[0].y, a02 = m[0].z," + "a10 = m[1].x, a11 = m[1].y, a12 = m[1].z," + "a20 = m[2].x, a21 = m[2].y, a22 = m[2].z," + "b01 = a22*a11 - a12*a21," + "b11 = -a22*a10 + a12*a20," + "b21 = a21*a10 - a11*a20," + "det = a00*b01 + a01*b11 + a02*b21;" + "return matrix(" + "b01, (-a22*a01 + a02*a21), ( a12*a01 - a02*a11)," + "b11, ( a22*a00 - a02*a20), (-a12*a00 + a02*a10)," + "b21, (-a21*a00 + a01*a20), ( a11*a00 - a01*a10)) * (1/det);" +"}"; -static constexpr char kInverse4x4[] = R"( -template -matrix mat4_inverse(matrix m) { -T - a00 = m[0].x, a01 = m[0].y, a02 = m[0].z, a03 = m[0].w, - a10 = m[1].x, a11 = m[1].y, a12 = m[1].z, a13 = m[1].w, - a20 = m[2].x, a21 = m[2].y, a22 = m[2].z, a23 = m[2].w, - a30 = m[3].x, a31 = m[3].y, a32 = m[3].z, a33 = m[3].w, - b00 = a00*a11 - a01*a10, - b01 = a00*a12 - a02*a10, - b02 = a00*a13 - a03*a10, - b03 = a01*a12 - a02*a11, - b04 = a01*a13 - a03*a11, - b05 = a02*a13 - a03*a12, - b06 = a20*a31 - a21*a30, - b07 = a20*a32 - a22*a30, - b08 = a20*a33 - a23*a30, - b09 = a21*a32 - a22*a31, - b10 = a21*a33 - a23*a31, - b11 = a22*a33 - a23*a32, - det = b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06; -return matrix( - a11*b11 - a12*b10 + a13*b09, - a02*b10 - a01*b11 - a03*b09, - a31*b05 - a32*b04 + a33*b03, - a22*b04 - a21*b05 - a23*b03, - a12*b08 - a10*b11 - a13*b07, - a00*b11 - a02*b08 + a03*b07, - a32*b02 - a30*b05 - a33*b01, - a20*b05 - a22*b02 + a23*b01, - a10*b10 - a11*b08 + a13*b06, - a01*b08 - a00*b10 - a03*b06, - a30*b04 - a31*b02 + a33*b00, - a21*b02 - a20*b04 - a23*b00, - a11*b07 - a10*b09 - a12*b06, - a00*b09 - a01*b07 + a02*b06, - a31*b01 - a30*b03 - a32*b00, - a20*b03 - a21*b01 + a22*b00) * (1/det); -} -)"; +static constexpr char kInverse4x4[] = +"template " +"matrix mat4_inverse(matrix m) {" + "T " + "a00 = m[0].x, a01 = m[0].y, a02 = m[0].z, a03 = m[0].w," + "a10 = m[1].x, a11 = m[1].y, a12 = m[1].z, a13 = m[1].w," + "a20 = m[2].x, a21 = m[2].y, a22 = m[2].z, a23 = m[2].w," + "a30 = m[3].x, a31 = m[3].y, a32 = m[3].z, a33 = m[3].w," + "b00 = a00*a11 - a01*a10," + "b01 = a00*a12 - a02*a10," + "b02 = a00*a13 - a03*a10," + "b03 = a01*a12 - a02*a11," + "b04 = a01*a13 - a03*a11," + "b05 = a02*a13 - a03*a12," + "b06 = a20*a31 - a21*a30," + "b07 = a20*a32 - a22*a30," + "b08 = a20*a33 - a23*a30," + "b09 = a21*a32 - a22*a31," + "b10 = a21*a33 - a23*a31," + "b11 = a22*a33 - a23*a32," + "det = b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06;" + "return matrix(" + "a11*b11 - a12*b10 + a13*b09," + "a02*b10 - a01*b11 - a03*b09," + "a31*b05 - a32*b04 + a33*b03," + "a22*b04 - a21*b05 - a23*b03," + "a12*b08 - a10*b11 - a13*b07," + "a00*b11 - a02*b08 + a03*b07," + "a32*b02 - a30*b05 - a33*b01," + "a20*b05 - a22*b02 + a23*b01," + "a10*b10 - a11*b08 + a13*b06," + "a01*b08 - a00*b10 - a03*b06," + "a30*b04 - a31*b02 + a33*b00," + "a21*b02 - a20*b04 - a23*b00," + "a11*b07 - a10*b09 - a12*b06," + "a00*b09 - a01*b07 + a02*b06," + "a31*b01 - a30*b03 - a32*b00," + "a20*b03 - a21*b01 + a22*b00) * (1/det);" +"}"; std::string MetalCodeGenerator::getInversePolyfill(const ExpressionArray& arguments) { // Only use polyfills for a function taking a single-argument square matrix. @@ -829,13 +826,12 @@ std::string MetalCodeGenerator::getInversePolyfill(const ExpressionArray& argume } void MetalCodeGenerator::writeMatrixCompMult() { - static constexpr char kMatrixCompMult[] = R"( -template -matrix matrixCompMult(matrix a, const matrix b) { - for (int c = 0; c < C; ++c) { a[c] *= b[c]; } - return a; -} -)"; + static constexpr char kMatrixCompMult[] = +"template " +"matrix matrixCompMult(matrix a, const matrix b) {" + "for (int c = 0; c < C; ++c) { a[c] *= b[c]; }" + "return a;" +"}"; if (!fWrittenMatrixCompMult) { fWrittenMatrixCompMult = true; fExtraFunctions.writeText(kMatrixCompMult); @@ -843,14 +839,13 @@ matrix matrixCompMult(matrix a, const matrix b) { } void MetalCodeGenerator::writeOuterProduct() { - static constexpr char kOuterProduct[] = R"( -template -matrix outerProduct(const vec a, const vec b) { - matrix m; - for (int c = 0; c < C; ++c) { m[c] = a * b[c]; } - return m; -} -)"; + static constexpr char kOuterProduct[] = +"template " +"matrix outerProduct(const vec a, const vec b) {" + "matrix m;" + "for (int c = 0; c < C; ++c) { m[c] = a * b[c]; }" + "return m;" +"}"; if (!fWrittenOuterProduct) { fWrittenOuterProduct = true; fExtraFunctions.writeText(kOuterProduct); @@ -1569,16 +1564,15 @@ void MetalCodeGenerator::writeConstructorArrayCast(const ConstructorArrayCast& c std::string name = "array_of_" + outTypeName + "_from_" + inTypeName; if (!fHelpers.contains(name)) { fHelpers.add(name); - fExtraFunctions.printf(R"( -template -array<%s, N> %s(thread const array<%s, N>& x) { - array<%s, N> result; - for (int i = 0; i < N; ++i) { - result[i] = %s(x[i]); - } - return result; -} -)", + fExtraFunctions.printf( +"template " +"array<%s, N> %s(thread const array<%s, N>& x) {" + "array<%s, N> result;" + "for (int i = 0; i < N; ++i) {" + "result[i] = %s(x[i]);" + "}" + "return result;" +"}", outTypeName.c_str(), name.c_str(), inTypeName.c_str(), outTypeName.c_str(), outTypeName.c_str()); @@ -1600,11 +1594,10 @@ std::string MetalCodeGenerator::getVectorFromMat2x2ConstructorHelper(const Type& if (!fHelpers.contains(name)) { fHelpers.add(name); - fExtraFunctions.printf(R"( -%s4 %s(%s2x2 x) { - return %s4(x[0].xy, x[1].xy); -} -)", baseType.c_str(), name.c_str(), baseType.c_str(), baseType.c_str()); + fExtraFunctions.printf( +"%s4 %s(%s2x2 x) {" + "return %s4(x[0].xy, x[1].xy);" +"}", baseType.c_str(), name.c_str(), baseType.c_str(), baseType.c_str()); } return name; @@ -1941,32 +1934,31 @@ void MetalCodeGenerator::writeMatrixEqualityHelpers(const Type& left, const Type if (!fHelpers.contains(key)) { fHelpers.add(key); - fExtraFunctionPrototypes.printf(R"( -thread bool operator==(const %s left, const %s right); -thread bool operator!=(const %s left, const %s right); -)", + fExtraFunctionPrototypes.printf( +"thread bool operator==(const %s left, const %s right);" +"thread bool operator!=(const %s left, const %s right);", this->typeName(left).c_str(), this->typeName(right).c_str(), this->typeName(left).c_str(), this->typeName(right).c_str()); fExtraFunctions.printf( - "thread bool operator==(const %s left, const %s right) {\n" - " return ", + "thread bool operator==(const %s left, const %s right) {" + "return ", this->typeName(left).c_str(), this->typeName(right).c_str()); const char* separator = ""; for (int index=0; indextypeName(left).c_str(), this->typeName(right).c_str()); } } @@ -2010,31 +2002,29 @@ void MetalCodeGenerator::writeArrayEqualityHelpers(const Type& type) { std::string key = "ArrayEquality []"; if (!fHelpers.contains(key)) { fHelpers.add(key); - fExtraFunctionPrototypes.writeText(R"( -template -bool operator==(const array_ref left, const array_ref right); -template -bool operator!=(const array_ref left, const array_ref right); -)"); - fExtraFunctions.writeText(R"( -template -bool operator==(const array_ref left, const array_ref right) { - if (left.size() != right.size()) { - return false; - } - for (size_t index = 0; index < left.size(); ++index) { - if (!all(left[index] == right[index])) { - return false; - } - } - return true; -} + fExtraFunctionPrototypes.writeText( +"template " +"bool operator==(const array_ref left, const array_ref right);" +"template " +"bool operator!=(const array_ref left, const array_ref right);"); + fExtraFunctions.writeText( +"template " +"bool operator==(const array_ref left, const array_ref right) {" + "if (left.size() != right.size()) {" + "return false;" + "}" + "for (size_t index = 0; index < left.size(); ++index) {" + "if (!all(left[index] == right[index])) {" + "return false;" + "}" + "}" + "return true;" +"}" -template -bool operator!=(const array_ref left, const array_ref right) { - return !(left == right); -} -)"); +"template " +"bool operator!=(const array_ref left, const array_ref right) {" + "return !(left == right);" +"}"); } } @@ -2051,18 +2041,17 @@ void MetalCodeGenerator::writeStructEqualityHelpers(const Type& type) { // Write operator== and operator!= for this struct, since those are assumed to exist in SkSL // and GLSL but do not exist by default in Metal. - fExtraFunctionPrototypes.printf(R"( -thread bool operator==(thread const %s& left, thread const %s& right); -thread bool operator!=(thread const %s& left, thread const %s& right); -)", + fExtraFunctionPrototypes.printf( +"thread bool operator==(thread const %s& left, thread const %s& right);" +"thread bool operator!=(thread const %s& left, thread const %s& right);", this->typeName(type).c_str(), this->typeName(type).c_str(), this->typeName(type).c_str(), this->typeName(type).c_str()); fExtraFunctions.printf( - "thread bool operator==(thread const %s& left, thread const %s& right) {\n" - " return ", + "thread bool operator==(thread const %s& left, thread const %s& right) {" + "return ", this->typeName(type).c_str(), this->typeName(type).c_str()); @@ -2080,14 +2069,14 @@ thread bool operator!=(thread const %s& left, thread const %s& right); (int)field.fName.size(), field.fName.data(), (int)field.fName.size(), field.fName.data()); } - separator = " &&\n "; + separator = " && "; } fExtraFunctions.printf( - ";\n" - "}\n" - "thread bool operator!=(thread const %s& left, thread const %s& right) {\n" - " return !(left == right);\n" - "}\n", + ";" + "}" + "thread bool operator!=(thread const %s& left, thread const %s& right) {" + "return !(left == right);" + "}", this->typeName(type).c_str(), this->typeName(type).c_str()); } @@ -3058,22 +3047,20 @@ void MetalCodeGenerator::writeSampler2DPolyfill() { } fWrotePolyfill = true; - std::string polyfill = SkSL::String::printf(R"( -struct sampler2D { - texture2d tex; - sampler smp; -}; -half4 sample(sampler2D i, float2 p, float b=%g) { return i.tex.sample(i.smp, p, bias(b)); } -half4 sample(sampler2D i, float3 p, float b=%g) { return i.tex.sample(i.smp, p.xy / p.z, bias(b)); } -half4 sampleLod(sampler2D i, float2 p, float lod) { return i.tex.sample(i.smp, p, level(lod)); } -half4 sampleLod(sampler2D i, float3 p, float lod) { - return i.tex.sample(i.smp, p.xy / p.z, level(lod)); -} -half4 sampleGrad(sampler2D i, float2 p, float2 dPdx, float2 dPdy) { - return i.tex.sample(i.smp, p, gradient2d(dPdx, dPdy)); -} - -)", + std::string polyfill = SkSL::String::printf( +"struct sampler2D {" + "texture2d tex;" + "sampler smp;" +"};" +"half4 sample(sampler2D i, float2 p, float b=%g) { return i.tex.sample(i.smp, p, bias(b)); }" +"half4 sample(sampler2D i, float3 p, float b=%g) { return i.tex.sample(i.smp, p.xy / p.z, bias(b)); }" +"half4 sampleLod(sampler2D i, float2 p, float lod) { return i.tex.sample(i.smp, p, level(lod)); }" +"half4 sampleLod(sampler2D i, float3 p, float lod) {" + "return i.tex.sample(i.smp, p.xy / p.z, level(lod));" +"}" +"half4 sampleGrad(sampler2D i, float2 p, float2 dPdx, float2 dPdy) {" + "return i.tex.sample(i.smp, p, gradient2d(dPdx, dPdy));" +"}", fTextureBias, fTextureBias); fCodeGen->write(polyfill.c_str()); @@ -3244,9 +3231,9 @@ void MetalCodeGenerator::writeInterfaceBlocks() { } if (!wroteInterfaceBlock && fProgram.fInterface.fRTFlipUniform != Program::Interface::kRTFlip_None) { - this->writeLine("struct sksl_synthetic_uniforms {"); - this->writeLine(" float2 " SKSL_RTFLIP_NAME ";"); - this->writeLine("};"); + this->writeLine("struct sksl_synthetic_uniforms {" + "float2 " SKSL_RTFLIP_NAME ";" + "};"); } } diff --git a/gfx/skia/skia/src/sksl/codegen/SkSLRasterPipelineBuilder.cpp b/gfx/skia/skia/src/sksl/codegen/SkSLRasterPipelineBuilder.cpp index 6b09521e490c..5f0d4b4698bc 100644 --- a/gfx/skia/skia/src/sksl/codegen/SkSLRasterPipelineBuilder.cpp +++ b/gfx/skia/skia/src/sksl/codegen/SkSLRasterPipelineBuilder.cpp @@ -1453,7 +1453,7 @@ void Program::appendCopy(TArray* pipeline, int32_t* immutablePtr = reinterpret_cast(basePtr + src); if (immutable_data_is_splattable(immutablePtr, numSlots)) { auto stage = (ProgramOp)((int)ProgramOp::copy_constant + numSlots - 1); - SkRasterPipeline_ConstantCtx ctx; + SkRasterPipelineContexts::ConstantCtx ctx; ctx.dst = dst; ctx.value = *immutablePtr; pipeline->push_back({stage, SkRPCtxUtils::Pack(ctx, alloc)}); @@ -1463,7 +1463,7 @@ void Program::appendCopy(TArray* pipeline, // We can't use a splat, so emit the requested copy op. auto stage = (ProgramOp)((int)baseStage + numSlots - 1); - SkRasterPipeline_BinaryOpCtx ctx; + SkRasterPipelineContexts::BinaryOpCtx ctx; ctx.dst = dst; ctx.src = src; pipeline->push_back({stage, SkRPCtxUtils::Pack(ctx, alloc)}); @@ -1535,7 +1535,7 @@ void Program::appendImmediateBinaryOp(TArray* pipeline, SkArenaAlloc* all SkASSERT(is_immediate_op((BuilderOp)baseStage)); int slotsPerStage = is_multi_slot_immediate_op((BuilderOp)baseStage) ? 4 : 1; - SkRasterPipeline_ConstantCtx ctx; + SkRasterPipelineContexts::ConstantCtx ctx; ctx.dst = dst; ctx.value = value; @@ -1558,7 +1558,7 @@ void Program::appendAdjacentNWayBinaryOp(TArray* pipeline, SkArenaAlloc* SkASSERT((dst + SkOpts::raster_pipeline_highp_stride * numSlots * sizeof(float)) == src); if (numSlots > 0) { - SkRasterPipeline_BinaryOpCtx ctx; + SkRasterPipelineContexts::BinaryOpCtx ctx; ctx.dst = dst; ctx.src = src; pipeline->push_back({stage, SkRPCtxUtils::Pack(ctx, alloc)}); @@ -1591,7 +1591,7 @@ void Program::appendAdjacentNWayTernaryOp(TArray* pipeline, SkArenaAlloc* SkASSERT((src0 + SkOpts::raster_pipeline_highp_stride * numSlots * sizeof(float)) == src1); if (numSlots > 0) { - SkRasterPipeline_TernaryOpCtx ctx; + SkRasterPipelineContexts::TernaryOpCtx ctx; ctx.dst = dst; ctx.delta = src0 - dst; pipeline->push_back({stage, SkRPCtxUtils::Pack(ctx, alloc)}); @@ -1678,9 +1678,9 @@ std::optional Program::allocateSlotData(SkArenaAlloc* alloc) // Store the temp stack immediately after the values, and immutable data after the stack. SlotData s; - s.values = SkSpan(slotPtr, N * fNumValueSlots); - s.stack = SkSpan(s.values.end(), N * fNumTempStackSlots); - s.immutable = SkSpan(s.stack.end(), 1 * fNumImmutableSlots); + s.values = SkSpan{slotPtr, N * fNumValueSlots}; + s.stack = SkSpan{s.values.end(), N * fNumTempStackSlots}; + s.immutable = SkSpan{s.stack.end(), 1 * fNumImmutableSlots}; return s; } @@ -1701,7 +1701,7 @@ bool Program::appendStages(SkRasterPipeline* pipeline, // Allocate buffers for branch targets and labels; these are needed to convert labels into // actual offsets into the pipeline and fix up branches. - TArray branchContexts; + TArray branchContexts; branchContexts.reserve_exact(fNumLabels); TArray labelOffsets; labelOffsets.push_back_n(fNumLabels, -1); @@ -1774,7 +1774,7 @@ bool Program::appendStages(SkRasterPipeline* pipeline, case ProgramOp::branch_if_no_lanes_active: case ProgramOp::branch_if_no_active_lanes_eq: { // The branch context contain a valid label ID at this point. - auto* branchCtx = static_cast(stage.ctx); + auto* branchCtx = static_cast(stage.ctx); int labelID = branchCtx->offset; SkASSERT(labelID >= 0 && labelID < fNumLabels); @@ -1900,7 +1900,7 @@ void Program::makeStages(TArray* pipeline, SkASSERT(inst.fImmA >= 0 && inst.fImmA < fNumLabels); EmitStackRewindForBackwardsBranch(inst.fImmA); - auto* ctx = alloc->make(); + auto* ctx = alloc->make(); ctx->offset = inst.fImmA; pipeline->push_back({(ProgramOp)inst.fOp, ctx}); break; @@ -1909,7 +1909,7 @@ void Program::makeStages(TArray* pipeline, SkASSERT(inst.fImmA >= 0 && inst.fImmA < fNumLabels); EmitStackRewindForBackwardsBranch(inst.fImmA); - auto* ctx = alloc->make(); + auto* ctx = alloc->make(); ctx->offset = inst.fImmA; pipeline->push_back({ProgramOp::branch_if_all_lanes_active, ctx}); break; @@ -1918,7 +1918,7 @@ void Program::makeStages(TArray* pipeline, SkASSERT(inst.fImmA >= 0 && inst.fImmA < fNumLabels); EmitStackRewindForBackwardsBranch(inst.fImmA); - auto* ctx = alloc->make(); + auto* ctx = alloc->make(); ctx->offset = inst.fImmA; ctx->value = inst.fImmB; ctx->ptr = reinterpret_cast(tempStackPtr - N); @@ -1926,7 +1926,7 @@ void Program::makeStages(TArray* pipeline, break; } case BuilderOp::init_lane_masks: { - auto* ctx = alloc->make(); + auto* ctx = alloc->make(); pipeline->push_back({ProgramOp::init_lane_masks, ctx}); break; } @@ -2083,7 +2083,7 @@ void Program::makeStages(TArray* pipeline, case BuilderOp::swizzle_2: case BuilderOp::swizzle_3: case BuilderOp::swizzle_4: { - SkRasterPipeline_SwizzleCtx ctx; + SkRasterPipelineContexts::SwizzleCtx ctx; ctx.dst = OffsetFromBase(tempStackPtr - (N * inst.fImmA)); // Unpack component nybbles into byte-offsets pointing at stack slots. unpack_nybbles_to_offsets(inst.fImmB, SkSpan(ctx.offsets)); @@ -2094,7 +2094,7 @@ void Program::makeStages(TArray* pipeline, int consumed = inst.fImmA; int generated = inst.fImmB; - auto* ctx = alloc->make(); + auto* ctx = alloc->make(); ctx->ptr = reinterpret_cast(tempStackPtr) - (N * consumed); ctx->count = generated; // Unpack immB and immC from nybble form into the offset array. @@ -2110,7 +2110,7 @@ void Program::makeStages(TArray* pipeline, (inst.fImmA * inst.fImmB) + // left-matrix (inst.fImmC * inst.fImmD); // right-matrix - SkRasterPipeline_MatrixMultiplyCtx ctx; + SkRasterPipelineContexts::MatrixMultiplyCtx ctx; ctx.dst = OffsetFromBase(tempStackPtr - (N * consumed)); ctx.leftColumns = inst.fImmA; ctx.leftRows = inst.fImmB; @@ -2174,7 +2174,7 @@ void Program::makeStages(TArray* pipeline, // immA: number of slots to copy // immB: dynamic stack ID ProgramOp op; - auto* ctx = alloc->make(); + auto* ctx = alloc->make(); ctx->indirectOffset = reinterpret_cast(tempStackMap[inst.fImmB]) - (1 * N); ctx->indirectLimit = inst.fSlotB - inst.fSlotA - inst.fImmA; @@ -2206,7 +2206,7 @@ void Program::makeStages(TArray* pipeline, float* dst = (inst.fOp == BuilderOp::push_uniform) ? tempStackPtr : SlotB(); for (int remaining = inst.fImmA; remaining > 0; remaining -= 4) { - auto ctx = alloc->make(); + auto ctx = alloc->make(); ctx->dst = reinterpret_cast(dst); ctx->src = reinterpret_cast(src); switch (remaining) { @@ -2283,7 +2283,7 @@ void Program::makeStages(TArray* pipeline, float* dst = (inst.fOp == BuilderOp::copy_constant) ? SlotA() : tempStackPtr; // Splat constant values onto the stack. for (int remaining = inst.fImmA; remaining > 0; remaining -= 4) { - SkRasterPipeline_ConstantCtx ctx; + SkRasterPipelineContexts::ConstantCtx ctx; ctx.dst = OffsetFromBase(dst); ctx.value = inst.fImmB; void* ptr = SkRPCtxUtils::Pack(ctx, alloc); @@ -2319,7 +2319,7 @@ void Program::makeStages(TArray* pipeline, // immB: swizzle components // immC: offset from stack top auto stage = (ProgramOp)((int)ProgramOp::swizzle_copy_slot_masked + inst.fImmA - 1); - auto* ctx = alloc->make(); + auto* ctx = alloc->make(); ctx->src = reinterpret_cast(tempStackPtr) - (inst.fImmC * N); ctx->dst = reinterpret_cast(SlotA()); unpack_nybbles_to_offsets(inst.fImmB, SkSpan(ctx->offsets)); @@ -2355,7 +2355,7 @@ void Program::makeStages(TArray* pipeline, // immD: dynamic stack ID float* sourceStackPtr = tempStackMap[inst.fImmB]; - auto* ctx = alloc->make(); + auto* ctx = alloc->make(); ctx->dst = reinterpret_cast(tempStackPtr); ctx->src = reinterpret_cast(sourceStackPtr) - (inst.fImmC * N); ctx->indirectOffset = @@ -2372,7 +2372,7 @@ void Program::makeStages(TArray* pipeline, // immB: swizzle components // immC: offset from stack top // immD: dynamic stack ID - auto* ctx = alloc->make(); + auto* ctx = alloc->make(); ctx->src = reinterpret_cast(tempStackPtr) - (inst.fImmC * N); ctx->dst = reinterpret_cast(SlotA()); ctx->indirectOffset = @@ -2385,7 +2385,7 @@ void Program::makeStages(TArray* pipeline, break; } case BuilderOp::case_op: { - SkRasterPipeline_CaseOpCtx ctx; + SkRasterPipelineContexts::CaseOpCtx ctx; ctx.expectedValue = inst.fImmA; ctx.offset = OffsetFromBase(tempStackPtr - (2 * N)); pipeline->push_back({ProgramOp::case_op, SkRPCtxUtils::Pack(ctx, alloc)}); @@ -2413,20 +2413,20 @@ void Program::makeStages(TArray* pipeline, break; case BuilderOp::trace_line: { - auto* ctx = AllocTraceContext((SkRasterPipeline_TraceLineCtx*)nullptr); + auto* ctx = AllocTraceContext((SkRasterPipelineContexts::TraceLineCtx*)nullptr); ctx->lineNumber = inst.fImmB; pipeline->push_back({ProgramOp::trace_line, ctx}); break; } case BuilderOp::trace_scope: { - auto* ctx = AllocTraceContext((SkRasterPipeline_TraceScopeCtx*)nullptr); + auto* ctx = AllocTraceContext((SkRasterPipelineContexts::TraceScopeCtx*)nullptr); ctx->delta = inst.fImmB; pipeline->push_back({ProgramOp::trace_scope, ctx}); break; } case BuilderOp::trace_enter: case BuilderOp::trace_exit: { - auto* ctx = AllocTraceContext((SkRasterPipeline_TraceFuncCtx*)nullptr); + auto* ctx = AllocTraceContext((SkRasterPipelineContexts::TraceFuncCtx*)nullptr); ctx->funcIdx = inst.fImmB; pipeline->push_back({(ProgramOp)inst.fOp, ctx}); break; @@ -2438,7 +2438,7 @@ void Program::makeStages(TArray* pipeline, // immA: trace-mask stack ID // immB: number of slots // immC: dynamic stack ID - auto* ctx = AllocTraceContext((SkRasterPipeline_TraceVarCtx*)nullptr); + auto* ctx = AllocTraceContext((SkRasterPipelineContexts::TraceVarCtx*)nullptr); ctx->slotIdx = inst.fSlotA; ctx->numSlots = inst.fImmB; ctx->data = reinterpret_cast(SlotA()); @@ -2532,7 +2532,7 @@ public: } // Interprets the context value as a branch offset. - std::string branchOffset(const SkRasterPipeline_BranchCtx* ctx, int index) const { + std::string branchOffset(const SkRasterPipelineContexts::BranchCtx* ctx, int index) const { // The context's offset field contains a label ID int labelID = ctx->offset; const int* targetIndex = fLabelToStageMap.find(labelID); @@ -2725,7 +2725,7 @@ public: std::tuple constantCtx(const void* v, int slots, bool showAsFloat = true) const { - auto ctx = SkRPCtxUtils::Unpack((const SkRasterPipeline_ConstantCtx*)v); + auto ctx = SkRPCtxUtils::Unpack((const SkRasterPipelineContexts::ConstantCtx*)v); return {this->offsetCtx(ctx.dst, slots), this->imm(sk_bit_cast(ctx.value), showAsFloat)}; } @@ -2733,7 +2733,7 @@ public: // Interprets the context value as a BinaryOp structure for copy_n_slots (numSlots is dictated // by the op itself). std::tuple binaryOpCtx(const void* v, int numSlots) const { - auto ctx = SkRPCtxUtils::Unpack((const SkRasterPipeline_BinaryOpCtx*)v); + auto ctx = SkRPCtxUtils::Unpack((const SkRasterPipelineContexts::BinaryOpCtx*)v); return {this->offsetCtx(ctx.dst, numSlots), this->offsetCtx(ctx.src, numSlots)}; } @@ -2741,7 +2741,7 @@ public: // Interprets the context value as a BinaryOp structure for copy_n_uniforms (numSlots is // dictated by the op itself). std::tuple copyUniformCtx(const void* v, int numSlots) const { - const auto *ctx = static_cast(v); + const auto* ctx = static_cast(v); return {this->ptrCtx(ctx->dst, numSlots), this->multiImmCtx(reinterpret_cast(ctx->src), numSlots)}; } @@ -2761,7 +2761,7 @@ public: // Interprets the context value as a BinaryOp structure (numSlots is inferred from the distance // between pointers). std::tuple adjacentBinaryOpCtx(const void* v) const { - auto ctx = SkRPCtxUtils::Unpack((const SkRasterPipeline_BinaryOpCtx*)v); + auto ctx = SkRPCtxUtils::Unpack((const SkRasterPipelineContexts::BinaryOpCtx*)v); int numSlots = (ctx.src - ctx.dst) / (N * sizeof(float)); return this->adjacentOffsetCtx(ctx.dst, numSlots); } @@ -2783,7 +2783,7 @@ public: // Interprets the context value as a TernaryOp structure (numSlots is inferred from `delta`). std::tuple adjacentTernaryOpCtx(const void* v) const { - auto ctx = SkRPCtxUtils::Unpack((const SkRasterPipeline_TernaryOpCtx*)v); + auto ctx = SkRPCtxUtils::Unpack((const SkRasterPipelineContexts::TernaryOpCtx*)v); int numSlots = ctx.delta / (sizeof(float) * N); return this->adjacent3OffsetCtx(ctx.dst, numSlots); } @@ -2829,7 +2829,7 @@ public: // Interprets the context value as a SwizzleCtx structure. std::tuple swizzleCtx(ProgramOp op, const void* v) const { - auto ctx = SkRPCtxUtils::Unpack((const SkRasterPipeline_SwizzleCtx*)v); + auto ctx = SkRPCtxUtils::Unpack((const SkRasterPipelineContexts::SwizzleCtx*)v); int destSlots = (int)op - (int)BuilderOp::swizzle_1 + 1; return {this->offsetCtx(ctx.dst, destSlots), this->swizzlePtr(this->offsetToPtr(ctx.dst), SkSpan(ctx.offsets, destSlots))}; @@ -2837,7 +2837,7 @@ public: // Interprets the context value as a SwizzleCopyCtx structure. std::tuple swizzleCopyCtx(ProgramOp op, const void* v) const { - const auto* ctx = static_cast(v); + const auto* ctx = static_cast(v); int destSlots = (int)op - (int)BuilderOp::swizzle_copy_slot_masked + 1; return {this->swizzlePtr(ctx->dst, SkSpan(ctx->offsets, destSlots)), @@ -2846,7 +2846,7 @@ public: // Interprets the context value as a ShuffleCtx structure. std::tuple shuffleCtx(const void* v) const { - const auto* ctx = static_cast(v); + const auto* ctx = static_cast(v); std::string dst = this->ptrCtx(ctx->ptr, ctx->count); std::string src = "(" + dst + ")["; @@ -2864,7 +2864,7 @@ public: // Interprets the context value as a packed MatrixMultiplyCtx structure. std::tuple matrixMultiply(const void* v) const { - auto ctx = SkRPCtxUtils::Unpack((const SkRasterPipeline_MatrixMultiplyCtx*)v); + auto ctx = SkRPCtxUtils::Unpack((const SkRasterPipelineContexts::MatrixMultiplyCtx*)v); int leftMatrix = ctx.leftColumns * ctx.leftRows; int rightMatrix = ctx.rightColumns * ctx.rightRows; int resultMatrix = ctx.rightColumns * ctx.leftRows; @@ -2977,7 +2977,8 @@ void Program::Dumper::dump(SkWStream* out, bool writeInstructionCount) { break; case POp::case_op: { - auto ctx = SkRPCtxUtils::Unpack((const SkRasterPipeline_CaseOpCtx*)stage.ctx); + auto ctx = + SkRPCtxUtils::Unpack((const SkRasterPipelineContexts::CaseOpCtx*)stage.ctx); opArg1 = this->offsetCtx(ctx.offset, 1); opArg2 = this->offsetCtx(ctx.offset + sizeof(int32_t) * N, 1); opArg3 = this->imm(sk_bit_cast(ctx.expectedValue), /*showAsFloat=*/false); @@ -3184,7 +3185,8 @@ void Program::Dumper::dump(SkWStream* out, bool writeInstructionCount) { case POp::copy_from_indirect_uniform_unmasked: case POp::copy_from_indirect_unmasked: case POp::copy_to_indirect_masked: { - const auto* ctx = static_cast(stage.ctx); + const auto* ctx = + static_cast(stage.ctx); // We don't incorporate the indirect-limit in the output opArg1 = this->ptrCtx(ctx->dst, ctx->slots); opArg2 = this->ptrCtx(ctx->src, ctx->slots); @@ -3192,7 +3194,8 @@ void Program::Dumper::dump(SkWStream* out, bool writeInstructionCount) { break; } case POp::swizzle_copy_to_indirect_masked: { - const auto* ctx = static_cast(stage.ctx); + const auto* ctx = + static_cast(stage.ctx); opArg1 = this->ptrCtx(ctx->dst, this->swizzleWidth(SkSpan(ctx->offsets, ctx->slots))); opArg2 = this->ptrCtx(ctx->src, ctx->slots); @@ -3314,19 +3317,20 @@ void Program::Dumper::dump(SkWStream* out, bool writeInstructionCount) { case POp::branch_if_all_lanes_active: case POp::branch_if_any_lanes_active: case POp::branch_if_no_lanes_active: - opArg1 = this->branchOffset(static_cast(stage.ctx), - index); + opArg1 = this->branchOffset( + static_cast(stage.ctx), index); break; case POp::branch_if_no_active_lanes_eq: { - const auto* ctx = static_cast(stage.ctx); + const auto* ctx = + static_cast(stage.ctx); opArg1 = this->branchOffset(ctx, index); opArg2 = this->ptrCtx(ctx->ptr, 1); opArg3 = this->imm(sk_bit_cast(ctx->value)); break; } case POp::trace_var: { - const auto* ctx = static_cast(stage.ctx); + const auto* ctx = static_cast(stage.ctx); opArg1 = this->ptrCtx(ctx->traceMask, 1); opArg2 = this->ptrCtx(ctx->data, ctx->numSlots); if (ctx->indirectOffset != nullptr) { @@ -3335,14 +3339,14 @@ void Program::Dumper::dump(SkWStream* out, bool writeInstructionCount) { break; } case POp::trace_line: { - const auto* ctx = static_cast(stage.ctx); + const auto* ctx = static_cast(stage.ctx); opArg1 = this->ptrCtx(ctx->traceMask, 1); opArg2 = std::to_string(ctx->lineNumber); break; } case POp::trace_enter: case POp::trace_exit: { - const auto* ctx = static_cast(stage.ctx); + const auto* ctx = static_cast(stage.ctx); opArg1 = this->ptrCtx(ctx->traceMask, 1); opArg2 = (fProgram.fDebugTrace && ctx->funcIdx >= 0 && @@ -3352,7 +3356,7 @@ void Program::Dumper::dump(SkWStream* out, bool writeInstructionCount) { break; } case POp::trace_scope: { - const auto* ctx = static_cast(stage.ctx); + const auto* ctx = static_cast(stage.ctx); opArg1 = this->ptrCtx(ctx->traceMask, 1); opArg2 = SkSL::String::printf("%+d", ctx->delta); break; diff --git a/gfx/skia/skia/src/sksl/codegen/SkSLSPIRVCodeGenerator.cpp b/gfx/skia/skia/src/sksl/codegen/SkSLSPIRVCodeGenerator.cpp index 6cc5982c0115..16a88de0e51f 100644 --- a/gfx/skia/skia/src/sksl/codegen/SkSLSPIRVCodeGenerator.cpp +++ b/gfx/skia/skia/src/sksl/codegen/SkSLSPIRVCodeGenerator.cpp @@ -5235,7 +5235,7 @@ void SPIRVCodeGenerator::addRTFlipUniform(Position pos) { std::string_view name = "sksl_synthetic_uniforms"; const Type* intfStruct = fSynthetics.takeOwnershipOfSymbol(Type::MakeStructType( fContext, Position(), name, std::move(fields), /*interfaceBlock=*/true)); - bool usePushConstants = fProgram.fConfig->fSettings.fUsePushConstants; + bool usePushConstants = fProgram.fConfig->fSettings.fUseVulkanPushConstantsForGaneshRTAdjust; int binding = -1, set = -1; if (!usePushConstants) { binding = fProgram.fConfig->fSettings.fRTFlipBinding; diff --git a/gfx/skia/skia/src/sksl/codegen/SkSLWGSLCodeGenerator.cpp b/gfx/skia/skia/src/sksl/codegen/SkSLWGSLCodeGenerator.cpp index 3ac71280c7cf..9cb0816c52d5 100644 --- a/gfx/skia/skia/src/sksl/codegen/SkSLWGSLCodeGenerator.cpp +++ b/gfx/skia/skia/src/sksl/codegen/SkSLWGSLCodeGenerator.cpp @@ -2285,7 +2285,9 @@ void WGSLCodeGenerator::writeEmulatedSwitchFallthroughCases(SkSpanwrite(fallthroughVar); this->write(" = true; "); } +#if defined(SK_DEBUG) this->writeLine("// fallthrough"); +#endif fIndentation--; this->writeLine("}"); diff --git a/gfx/skia/skia/src/sksl/generated/sksl_graphite_frag.minified.sksl b/gfx/skia/skia/src/sksl/generated/sksl_graphite_frag.minified.sksl index 47203c8638f1..6fbed5a4e5bd 100644 --- a/gfx/skia/skia/src/sksl/generated/sksl_graphite_frag.minified.sksl +++ b/gfx/skia/skia/src/sksl/generated/sksl_graphite_frag.minified.sksl @@ -2,162 +2,175 @@ static constexpr char SKSL_MINIFIED_sksl_graphite_frag[] = "$pure half4 sk_error(){return half4(1.,0.,0.,1.);}$pure half4 sk_passthrough" "(half4 a){return a;}$pure half4 sk_solid_shader(float4 a){return half4(a);}" "$pure half4 sk_rgb_opaque(float4 a){return half4(half3(a.xyz),1.);}$pure half4" -" sk_alpha_only(float4 a){return half4(0.,0.,0.,half(a.w));}$pure float $k(int" -" a,float b,half4[2]c){float d=float(c[0].x);float e=float(c[0].y);float f=float" -"(c[0].z);float g=float(c[0].w);float h=float(c[1].x);float i=float(c[1].y);" -"float j=float(c[1].z);float k=sign(b);b=abs(b);{float l;switch(a){case 1:b=" -"b0.)g=sign(g)*$k(abs(g),c,d.xyz);else" +" if(c.x<-1.)g=sign(g)*$l(abs(g),c.yzw,d.xyz);else if(c.x<0.)g=sign(g)*$m(abs" +"(g),c.yzw,d.xyz);g=float3x3(b)*g;if(e.x>0.)g=sign(g)*$k(abs(g),e,f.xyz);else" +" if(e.x<-1.)g=sign(g)*$l(abs(g),e.yzw,f.xyz);else if(e.x<0.)g=sign(g)*$n(abs" +"(g),e.yzw,f.xyz);half h=f.w;a.xyz=half3(g)*max(a.w,h);return a;}$pure half4" +" sk_color_space_transform_premul(half4 a,half2 b){if(b.x<0.)a=unpremul(a);else" +"{half c=b.x;half d=b.y;a.w=max(a.w,c);a.xyz=a.xyz*max(a.w,d);}return a;}$pure" +" half4 sk_color_space_transform_srgb(half4 a,half3x3 b,half4 c,half4 d,half4" +" e,half4 f){if(d.w<0.)a=unpremul(a);else{half g=1.-d.w;half h=d.w*f.w;half i" +"=d.w-h;a.w=dot(half3(a.wx,1.),half3(g,i,h));}float3 g=float3(a.xyz);g=sign(" +"g)*$k(abs(g),c,d.xyz);g=float3x3(b)*g;g=sign(g)*$k(abs(g),e,f.xyz);half h=f" +".w;a.xyz=half3(g)*max(a.w,h);return a;}$pure half4 sk_analytic_clip(float2 a" +",float4 b,float2 c,half4 d){float2 e=abs(c.x).xx;float2 f=float2(d.xy)*((b." +"xy+e)-a);float2 g=float2(d.zw)*(a-(b.zw-e));float2 h=max(max(f,g),0.);half i" +"=half(saturate(e.x*(1.-length(h*c.y))));half4 j=saturate(half4(half2(a-b.xy" +"),half2(b.zw-a)));j=mix(j,half4(1.),d);half k=(((i*j.x)*j.y)*j.z)*j.w;k=c.x" +"<0.?1.-k:k;return k.xxxx;}$pure half4 sk_analytic_and_atlas_clip(float2 a,float4" +" b,float2 c,half4 d,float2 e,float4 f,float2 g,sampler2D h){half4 i=sk_analytic_clip" +"(a,b,c,d);float2 j=a+e;float2 k=clamp(j,f.xy,f.zw);half l=sample(h,k*g).x;return" +" i*l;}$pure float $o(int a,float b,float c,float d){switch(a){case 0:return" " clamp(b,c,d);case 1:{float e=d-c;return mod(b-c,e)+c;}case 2:{float e=d-c;" "float g=2.*e;float h=mod(b-c,g);return mix(h,g-h,step(e,h))+c;}default:return" -" b;}}$pure half4 $m(float2 a,float2 b,sampler2D c){return sample(c,a*b);}$pure" -" half4 $n(float2 a,float2 b,float4 c,int d,int e,int f,float2 g,sampler2D h" +" b;}}$pure half4 $p(float2 a,float2 b,sampler2D c){return sample(c,a*b);}$pure" +" half4 $q(float2 a,float2 b,float4 c,int d,int e,int f,float2 g,sampler2D h" "){if(d==3&&f==0){float i=floor(a.x)+.5;if(ic.z)return half4(0.);}if" -"(e==3&&f==0){float i=floor(a.y)+.5;if(ic.w)return half4(0.);}a.x=$l" -"(d,a.x,c.x,c.z);a.y=$l(e,a.y,c.y,c.w);float4 i;if(f==0)i=float4(floor(c.xy)" +"(e==3&&f==0){float i=floor(a.y)+.5;if(ic.w)return half4(0.);}a.x=$o" +"(d,a.x,c.x,c.z);a.y=$o(e,a.y,c.y,c.w);float4 i;if(f==0)i=float4(floor(c.xy)" "+.50001,ceil(c.zw)-.50001);else i=float4(c.xy+g.x,c.zw-g.y);float2 j=clamp(" -"a,i.xy,i.zw);half4 k=$m(j,b,h);if(f==1){half2 l=half2(a-j);half2 m=abs(l);bool" +"a,i.xy,i.zw);half4 k=$p(j,b,h);if(f==1){half2 l=half2(a-j);half2 m=abs(l);bool" " n=d==1;bool o=e==1;if(n||o){float p;float q;half4 r;half4 t;if(n){p=l.x>0." -"?i.x:i.z;r=$m(float2(p,j.y),b,h);}if(o){q=l.y>0.?i.y:i.w;t=$m(float2(j.x,q)" -",b,h);}if(n&&o){half4 u=$m(float2(p,q),b,h);k=mix(mix(k,r,m.x),mix(t,u,m.x)" +"?i.x:i.z;r=$p(float2(p,j.y),b,h);}if(o){q=l.y>0.?i.y:i.w;t=$p(float2(j.x,q)" +",b,h);}if(n&&o){half4 u=$p(float2(p,q),b,h);k=mix(mix(k,r,m.x),mix(t,u,m.x)" ",m.y);}else if(n)k=mix(k,r,m.x);else if(o)k=mix(k,t,m.y);}if(d==3)k*=max(1." -"-m.x,0.);if(e==3)k*=max(1.-m.y,0.);}return k;}$pure half4 $o(float2 a,float2" +"-m.x,0.);if(e==3)k*=max(1.-m.y,0.);}return k;}$pure half4 $r(float2 a,float2" " b,float4 c,int d,int e,half4x4 g,sampler2D h){float2 i=fract(a-.5);a-=1.5;" "a=floor(a)+.5;half4 j=g*half4(1.,half(i.x),half(i.x*i.x),half((i.x*i.x)*i.x" "));half4 k=g*half4(1.,half(i.y),half(i.y*i.y),half((i.y*i.y)*i.y));half4 l=" "half4(0.);for(int m=0;m<4;++m){half4 n=half4(0.);for(int o=0;o<4;++o)n+=j[o" -"]*$n(a+float2(float(o),float(m)),b,c,d,e,0,.50001.xx,h);l+=k[m]*n;}l.w=saturate" +"]*$q(a+float2(float(o),float(m)),b,c,d,e,0,.50001.xx,h);l+=k[m]*n;}l.w=saturate" "(l.w);l.xyz=clamp(l.xyz,half3(0.),l.www);return l;}$pure half4 sk_image_shader" -"(float2 a,float2 b,float4 c,int d,int e,int f,sampler2D g){return $n(a,b,c," -"d,e,f,.50001.xx,g);}$pure half4 sk_cubic_image_shader(float2 a,float2 b,float4" -" c,int d,int e,half4x4 f,sampler2D g){return $o(a,b,c,d,e,f,g);}$pure half4" -" sk_hw_image_shader(float2 a,float2 b,sampler2D c){return $m(a,b,c);}$pure half4" -" $p(half a,half b,half c,half d,half3x3 e,half3 f){half3 g=half3(a,b,c);half4" -" h;h.xyz=saturate(e*g+f);h.w=d;return h;}$pure half4 $q(half4 a,half4 b,half4" -" c,half d,half4 e,half4 f,half4 g,half3x3 h,half3 i){half j=dot(e,a);half k" -"=dot(f,b);half l=dot(g,c);return $p(j,k,l,d,h,i);}$pure half4 sk_yuv_image_shader" -"(float2 a,float2 b,float2 c,float4 d,float2 e,int f,int g,int h,int i,half4" -" j,half4 k,half4 l,half4 m,half3x3 n,half3 o,sampler2D p,sampler2D q,sampler2D" -" r,sampler2D s){if(h!=i)a=floor(a)+.5;int t=f==3?0:f;int u=g==3?0:g;half4 v" -";half4 w;half4 x;v=$n(a,b,d,f,g,h,.50001.xx,p);w=$n(a,c,d,t,u,i,e,q);x=$n(a" -",c,d,t,u,i,e,r);half y;if(m==half4(1.))y=1.;else{half4 z=$n(a,b,d,f,g,h,.50001" -".xx,s);y=dot(m,z);}return $q(v,w,x,y,j,k,l,n,o);}$pure half4 sk_cubic_yuv_image_shader" -"(float2 a,float2 b,float2 c,float4 d,int e,int f,half4x4 g,half4 h,half4 i," -"half4 j,half4 k,half3x3 l,half3 m,sampler2D n,sampler2D o,sampler2D p,sampler2D" -" q){int r=e==3?0:e;int s=f==3?0:f;half4 t;half4 u;half4 v;t=$o(a,b,d,e,f,g," -"n);u=$o(a,c,d,r,s,g,o);v=$o(a,c,d,r,s,g,p);half w;if(k==half4(1.))w=1.;else" -"{half4 x=$o(a,b,d,e,f,g,q);w=dot(k,x);}return $q(t,u,v,w,h,i,j,l,m);}$pure half4" -" sk_hw_yuv_image_shader(float2 a,float2 b,float2 c,half4 d,half4 e,half4 f," -"half4 g,half3x3 h,half3 i,sampler2D j,sampler2D k,sampler2D l,sampler2D m){" -"half4 n;half4 o;half4 p;n=$m(a,b,j);o=$m(a,c,k);p=$m(a,c,l);half r;if(g==half4" -"(1.))r=1.;else{half4 s=$m(a,b,m);r=dot(g,s);}return $q(n,o,p,r,d,e,f,h,i);}" -"$pure half4 sk_hw_yuv_no_swizzle_image_shader(float2 a,float2 b,float2 c,half3x3" -" d,half4 e,sampler2D f,sampler2D g,sampler2D h,sampler2D i){half n=$m(a,b,f" -").x;half o=$m(a,c,g).x;half p=$m(a,c,h).x;half q=saturate($m(a,b,i).x+e.w);" -"return $p(n,o,p,q,d,e.xyz);}$pure half4 sk_dither(half4 a,half b,sampler2D c" -"){half e=sample(c,sk_FragCoord.xy*.125).x-.5;return half4(clamp(a.xyz+e*b,0." -",a.w),a.w);}$pure float2 $r(int a,float2 b){switch(a){case 0:b.x=saturate(b" -".x);break;case 1:b.x=fract(b.x);break;case 2:{float c=b.x-1.;b.x=(c-2.*floor" -"(c*.5))-1.;if(sk_Caps.mustDoOpBetweenFloorAndAbs)b.x=clamp(b.x,-1.,1.);b.x=" -"abs(b.x);break;}case 3:if(b.x<0.||b.x>1.)return float2(0.,-1.);break;}return" -" b;}$pure half4 $s(float4[4]a,float4 b,float2 c){if(c.y<0.)return half4(0.)" -";else if(c.x<=b.x)return half4(a[0]);else if(c.x1.)return float2" +"(0.,-1.);break;}return b;}$pure half4 $v(float4[4]a,float4 b,float2 c){if(c" +".y<0.)return half4(0.);else if(c.x<=b.x)return half4(a[0]);else if(c.x=.0001?e*inversesqrt(f):half2(.7071);float2x2" -" g=float2x2(dFdx(c),dFdy(c));half2 h=half2(g*float2(e));half i=.65*length(h" -");if(b.y>0.)return saturate((d+i)/(2.*i)).xxxx;else return smoothstep(-i,i," -"d).xxxx;}$pure half4 sdf_text_lcd_coverage_fn(float2 a,half2 b,half4 c,float2" -" d,float e,sampler2D f,sampler2D g,sampler2D h,sampler2D i){float2x2 j=float2x2" -"(dFdx(d),dFdy(d));half2 k=half2(j*float2(b));half3 l=$D(a,int(e),k,f,g,h,i)" -";half3 m=half3(7.96875)*(l-half3(.5019608));m-=c.xyz;half2 n=half2(dFdx(m.y" -"),dFdy(m.y));half o=dot(n,n);n=o>=.0001?n*inversesqrt(o):half2(.7071);half2" -" p=half2(j*float2(n));half3 q=(.65*length(p)).xxx;if(c.w>0.)return half4(saturate" -"(m+q/(2.*q)),1.);else return half4(smoothstep(-q,q,m),1.);}$pure float $E(float2" -" a,float2x2 b){float2 c=a*b;return inversesqrt(dot(c,c));}$pure float2 $F(float2" -" a,float2 b,float c,float2x2 d){float2 e=1./(b*b+c*c);float2 g=e*a;float h=" -"$E(g,d);float i=(.5*h)*(dot(a,g)-1.);float j=((b.x*c)*e.x)*h;return float2(" -"j-i,j+i);}void $G(inout float2 a,float2x2 b,float2 c,float2 d,float2 e,float2" -" f){float2 g=f-d;if(all(greaterThan(g,0..xx)))if(all(greaterThan(f,0..xx))||" -"c.x>0.&&c.y<0.){float2 h=$F(g*e,f,c.x,b);h.y=f.x-c.x<=0.?1.:-h.y;a=min(a,h)" -";}else if(c.y==0.){float h=((c.x-g.x)-g.y)*$E(e,b);a.x=min(a.x,h);}}void $H" -"(inout float2 a,float2x2 b,float2 c,float4 e,float4 f,float4 g){$G(a,b,c,e." -"xy,-1..xx,float2(f.x,g.x));$G(a,b,c,e.zy,float2(1.,-1.),float2(f.y,g.y));$G" -"(a,b,c,e.zw,1..xx,float2(f.z,g.z));$G(a,b,c,e.xw,float2(-1.,1.),float2(f.w," -"g.w));}$pure half4 analytic_rrect_coverage_fn(float4 a,float4 b,float4 c,float4" -" d,float4 e,float2 f,float2 g){if(g.x>0.)return half4(1.);else if(g.y>1.){float2" -" h=min(c.xy,c.zw);float i=min(h.x,h.y)*a.w;float j=(g.y-1.)*a.w;float k=coverage_bias" -"(j);return half(saturate(j*(i+k))).xxxx;}else{float2x2 h=float2x2(b)*(1./a." -"w);float2 i=float2($E(float2(1.,0.),h),$E(float2(0.,1.),h));float2 j=i*(f.x" -"+min(c.xy,c.zw));float2 k=float2(min(j.x,j.y),-1.);float l;float m;if(g.x>-" -".95){float2 n=i*((c.xy+c.zw)+2.*f.xx);l=min(min(n.x,n.y),1.);m=coverage_bias" -"(l);}else{float2 n=(2.*f.x)*i;float2 o=n-j;k.y=-max(o.x,o.y);if(f.x>0.){float" -" p=min(n.x,n.y);float2 q=mix(p.xx,n,greaterThanEqual(o,-.5.xx));l=saturate(" -"max(q.x,q.y));m=coverage_bias(l);}else l=(m=1.);}$H(k,h,f,c,d,e);float n=min" -"(g.y,0.)*a.w;float o=l*(min(k.x+n,-k.y)+m);return half(saturate(o)).xxxx;}}" -"$pure half4 per_edge_aa_quad_coverage_fn(float4 a,float4 b){float2 d=min(b." -"xy,b.zw);float e=min(d.x,d.y)*a.w;return half(saturate(e)).xxxx;}$pure half4" -" circular_arc_coverage_fn(float4 a,float3 b,float3 c,float3 e,float f,float4" -" g){float h=length(a.xy);half i=half(a.z*(1.-h));half j=saturate(i);half k=" -"half(a.z*(h-a.w));half l=saturate(k);j*=l;half m=half(saturate(a.z*dot(a.xy" -",b.xy)+b.z));m*=half(saturate(a.z*dot(a.xy,c.xy)+c.z));m=m+half(saturate(a." -"z*dot(a.xy,e.xy)+e.z));half n=half(a.z*(f-length(a.xy-g.xy)));half o=half(a" -".z*(f-length(a.xy-g.zw)));half p=max(n,0.)+max(o,0.);m=saturate(m+p);return" -"(m*j).xxxx;}$pure half4 $I(float2 a,float4 b,half c,half d,sampler2D e){half" -" f;half g;if(c!=0.){half2 h=max(half2(b.xy-a),half2(a-b.zw));f=sample(e,float2" -"(float(d*h.x),.5)).x;g=sample(e,float2(float(d*h.y),.5)).x;}else{half4 h=half4" -"(half2(b.xy-a),half2(a-b.zw));f=(1.-sample(e,float2(float(d*h.x),.5)).x)-sample" -"(e,float2(float(d*h.z),.5)).x;g=(1.-sample(e,float2(float(d*h.y),.5)).x)-sample" -"(e,float2(float(d*h.w),.5)).x;}return(f*g).xxxx;}$pure half4 $J(float2 a,float4" -" b,sampler2D c){float d=b.z;float e=b.w;half2 f=half2((a-b.xy)*d);float g=float" -"(length(f))-e;return sample(c,float2(g,.5)).xxxx;}$pure half4 $K(float2 a,float4" -" b,half c,sampler2D d){float2 e=a-b.xy;float2 f=(b.zw-b.xy)*.5;e-=f;half2 g" -"=half2(sign(e));e=abs(e);half2 h=half2(e-(f-float(c)));h=max(h,0.);h*=g;h+=" -"c.xx;half2 i=(2.*c).xx;half2 j=h/i;return sample(d,float2(j)).xxxx;}$pure half4" -" blur_coverage_fn(float2 a,float4 b,half2 c,int d,sampler2D e){switch(d){case" -" 0:{return $I(a,b,c.x,c.y,e);}case 2:{return $J(a,b,e);}case 1:{return $K(a" -",b,c.x,e);}}return half4(0.);}"; +" l=half4(0.);half2 m=half2(c);half n=1.;for(int o=0;o=.0001?e*inversesqrt" +"(f):half2(.7071);float2x2 g=float2x2(dFdx(c),dFdy(c));half2 h=half2(g*float2" +"(e));half i=.65*length(h);if(b.y>0.)return saturate((d+i)/(2.*i)).xxxx;else" +" return smoothstep(-i,i,d).xxxx;}$pure half4 sdf_text_lcd_coverage_fn(float2" +" a,half2 b,half4 c,float2 d,float e,sampler2D f,sampler2D g,sampler2D h,sampler2D" +" i){float2x2 j=float2x2(dFdx(d),dFdy(d));half2 k=half2(j*float2(b));half3 l" +"=$G(a,int(e),k,f,g,h,i);half3 m=half3(7.96875)*(l-half3(.5019608));m-=c.xyz" +";half2 n=half2(dFdx(m.y),dFdy(m.y));half o=dot(n,n);n=o>=.0001?n*inversesqrt" +"(o):half2(.7071);half2 p=half2(j*float2(n));half3 q=(.65*length(p)).xxx;if(" +"c.w>0.)return half4(saturate(m+q/(2.*q)),1.);else return half4(smoothstep(-" +"q,q,m),1.);}$pure float $H(float2 a,float2x2 b){float2 c=a*b;return inversesqrt" +"(dot(c,c));}$pure float2 $I(float2 a,float2 b,float c,float2x2 d){float2 e=" +"1./(b*b+c*c);float2 g=e*a;float h=$H(g,d);float i=(.5*h)*(dot(a,g)-1.);float" +" j=((b.x*c)*e.x)*h;return float2(j-i,j+i);}void $J(inout float2 a,float2x2 b" +",float2 c,float2 d,float2 e,float2 f){float2 g=f-d;if(all(greaterThan(g,0.." +"xx)))if(all(greaterThan(f,0..xx))||c.x>0.&&c.y<0.){float2 h=$I(g*e,f,c.x,b)" +";h.y=f.x-c.x<=0.?1.:-h.y;a=min(a,h);}else if(c.y==0.){float h=((c.x-g.x)-g." +"y)*$H(e,b);a.x=min(a.x,h);}}void $K(inout float2 a,float2x2 b,float2 c,float4" +" e,float4 f,float4 g){$J(a,b,c,e.xy,-1..xx,float2(f.x,g.x));$J(a,b,c,e.zy,float2" +"(1.,-1.),float2(f.y,g.y));$J(a,b,c,e.zw,1..xx,float2(f.z,g.z));$J(a,b,c,e.xw" +",float2(-1.,1.),float2(f.w,g.w));}$pure half4 analytic_rrect_coverage_fn(float4" +" a,float4 b,float4 c,float4 d,float4 e,float2 f,float2 g){if(g.x>0.)return half4" +"(1.);else if(g.y>1.){float2 h=min(c.xy,c.zw);float i=min(h.x,h.y)*a.w;float" +" j=(g.y-1.)*a.w;float k=coverage_bias(j);return half(saturate(j*(i+k))).xxxx" +";}else{float2x2 h=float2x2(b)*(1./a.w);float2 i=float2($H(float2(1.,0.),h)," +"$H(float2(0.,1.),h));float2 j=i*(f.x+min(c.xy,c.zw));float2 k=float2(min(j." +"x,j.y),-1.);float l;float m;if(g.x>-.95){float2 n=i*((c.xy+c.zw)+2.*f.xx);l" +"=min(min(n.x,n.y),1.);m=coverage_bias(l);}else{float2 n=(2.*f.x)*i;float2 o" +"=n-j;k.y=-max(o.x,o.y);if(f.x>0.){float p=min(n.x,n.y);float2 q=mix(p.xx,n," +"greaterThanEqual(o,-.5.xx));l=saturate(max(q.x,q.y));m=coverage_bias(l);}else" +" l=(m=1.);}$K(k,h,f,c,d,e);float n=min(g.y,0.)*a.w;float o=l*(min(k.x+n,-k." +"y)+m);return half(saturate(o)).xxxx;}}$pure half4 per_edge_aa_quad_coverage_fn" +"(float4 a,float4 b){float2 d=min(b.xy,b.zw);float e=min(d.x,d.y)*a.w;return" +" half(saturate(e)).xxxx;}$pure half4 circular_arc_coverage_fn(float4 a,float3" +" b,float3 c,float3 e,float f,float4 g){float h=length(a.xy);half i=half(a.z" +"*(1.-h));half j=saturate(i);half k=half(a.z*(h-a.w));half l=saturate(k);j*=" +"l;half m=half(saturate(a.z*dot(a.xy,b.xy)+b.z));m*=half(saturate(a.z*dot(a." +"xy,c.xy)+c.z));m=m+half(saturate(a.z*dot(a.xy,e.xy)+e.z));half n=half(a.z*(" +"f-length(a.xy-g.xy)));half o=half(a.z*(f-length(a.xy-g.zw)));half p=max(n,0." +")+max(o,0.);m=saturate(m+p);return(m*j).xxxx;}$pure half4 $L(float2 a,float4" +" b,half c,half d,sampler2D e){half f;half g;if(c!=0.){half2 h=max(half2(b.xy" +"-a),half2(a-b.zw));f=sample(e,float2(float(d*h.x),.5)).x;g=sample(e,float2(" +"float(d*h.y),.5)).x;}else{half4 h=half4(half2(b.xy-a),half2(a-b.zw));f=(1.-" +"sample(e,float2(float(d*h.x),.5)).x)-sample(e,float2(float(d*h.z),.5)).x;g=" +"(1.-sample(e,float2(float(d*h.y),.5)).x)-sample(e,float2(float(d*h.w),.5))." +"x;}return(f*g).xxxx;}$pure half4 $M(float2 a,float4 b,sampler2D c){float d=" +"b.z;float e=b.w;half2 f=half2((a-b.xy)*d);float g=float(length(f))-e;return" +" sample(c,float2(g,.5)).xxxx;}$pure half4 $N(float2 a,float4 b,half c,sampler2D" +" d){float2 e=a-b.xy;float2 f=(b.zw-b.xy)*.5;e-=f;half2 g=half2(sign(e));e=abs" +"(e);half2 h=half2(e-(f-float(c)));h=max(h,0.);h*=g;h+=c.xx;half2 i=(2.*c).xx" +";half2 j=h/i;return sample(d,float2(j)).xxxx;}$pure half4 blur_coverage_fn(" +"float2 a,float4 b,half2 c,int d,sampler2D e){switch(d){case 0:{return $L(a," +"b,c.x,c.y,e);}case 2:{return $M(a,b,e);}case 1:{return $N(a,b,c.x,e);}}return" +" half4(0.);}"; diff --git a/gfx/skia/skia/src/sksl/generated/sksl_graphite_frag.unoptimized.sksl b/gfx/skia/skia/src/sksl/generated/sksl_graphite_frag.unoptimized.sksl index fc308c199794..81007bd501d5 100644 --- a/gfx/skia/skia/src/sksl/generated/sksl_graphite_frag.unoptimized.sksl +++ b/gfx/skia/skia/src/sksl/generated/sksl_graphite_frag.unoptimized.sksl @@ -1,59 +1,77 @@ static constexpr char SKSL_MINIFIED_sksl_graphite_frag[] = "const int $kTileModeClamp=0;const int $kTileModeRepeat=1;const int $kTileModeDecal" "=3;const int $kFilterModeNearest=0;const int $kFilterModeLinear=1;const int" -" $kColorSpaceXformFlagUnpremul=1;const int $kColorSpaceXformFlagLinearize=2" -";const int $kColorSpaceXformFlagGamutTransform=4;const int $kColorSpaceXformFlagEncode" -"=8;const int $kColorSpaceXformFlagPremul=16;const int $kColorSpaceXformFlagAlphaSwizzle" -"=32;const int $kMaskFormatA8=0;const float $kLinearInset=.50001;$pure half4" -" sk_error(){return half4(1.,0.,0.,1.);}$pure half4 sk_passthrough(half4 color" -"){return color;}$pure half4 sk_solid_shader(float4 colorParam){return half4" -"(colorParam);}$pure half4 sk_rgb_opaque(float4 colorParam){return half4(half3" -"(colorParam.xyz),1.);}$pure half4 sk_alpha_only(float4 colorParam){return half4" -"(0.,0.,0.,half(colorParam.w));}$pure float $apply_xfer_fn(int kind,float x," -"half4[2]cs){float G=float(cs[0].x);float A=float(cs[0].y);float B=float(cs[" -"0].z);float C=float(cs[0].w);float D=float(cs[1].x);float E=float(cs[1].y);" -"float F=float(cs[1].z);float s=sign(x);x=abs(x);{float x_C;switch(kind){case" -" 1:x=x0.)colorF=sign(colorF)*$apply_srgb_xfer_fn" +"(abs(colorF),srcGABC,srcDEF_args.xyz);else if(srcGABC.x<-1.)colorF=sign(colorF" +")*$apply_pq_xfer_fn(abs(colorF),srcGABC.yzw,srcDEF_args.xyz);else if(srcGABC" +".x<0.)colorF=sign(colorF)*$apply_hlg_xfer_fn(abs(colorF),srcGABC.yzw,srcDEF_args" +".xyz);colorF=float3x3(gamut)*colorF;if(dstGABC.x>0.)colorF=sign(colorF)*$apply_srgb_xfer_fn" +"(abs(colorF),dstGABC,dstDEF_args.xyz);else if(dstGABC.x<-1.)colorF=sign(colorF" +")*$apply_pq_xfer_fn(abs(colorF),dstGABC.yzw,dstDEF_args.xyz);else if(dstGABC" +".x<0.)colorF=sign(colorF)*$apply_hlg_inv_xfer_fn(abs(colorF),dstGABC.yzw,dstDEF_args" +".xyz);half noPremul=dstDEF_args.w;color.xyz=half3(colorF)*max(color.w,noPremul" +");return color;}$pure half4 sk_color_space_transform_premul(half4 color,half2" +" args){if(args.x<0.)color=unpremul(color);else{half opaque=args.x;half noPremul" +"=args.y;color.w=max(color.w,opaque);color.xyz=color.xyz*max(color.w,noPremul" +");}return color;}$pure half4 sk_color_space_transform_srgb(half4 color,half3x3" +" gamut,half4 srcGABC,half4 srcDEF_args,half4 dstGABC,half4 dstDEF_args){if(" +"srcDEF_args.w<0.)color=unpremul(color);else{half alphaSwizzleA=1.-srcDEF_args" +".w;half alphaSwizzle1=srcDEF_args.w*dstDEF_args.w;half alphaSwizzleR=srcDEF_args" +".w-alphaSwizzle1;color.w=dot(half3(color.wx,1.),half3(alphaSwizzleA,alphaSwizzleR" +",alphaSwizzle1));}float3 colorF=float3(color.xyz);colorF=sign(colorF)*$apply_srgb_xfer_fn" +"(abs(colorF),srcGABC,srcDEF_args.xyz);colorF=float3x3(gamut)*colorF;colorF=" +"sign(colorF)*$apply_srgb_xfer_fn(abs(colorF),dstGABC,dstDEF_args.xyz);half noPremul" +"=dstDEF_args.w;color.xyz=half3(colorF)*max(color.w,noPremul);return color;}" +"$pure half4 sk_analytic_clip(float2 coords,float4 rect,float2 radiusPlusHalf" +",half4 edgeSelect){float2 radius=abs(radiusPlusHalf.x).xx;float2 dxy0=float2" +"(edgeSelect.xy)*((rect.xy+radius)-coords);float2 dxy1=float2(edgeSelect.zw)" +"*(coords-(rect.zw-radius));float2 dxy=max(max(dxy0,dxy1),0.);half circleCornerAlpha" "=half(saturate(radius.x*(1.-length(dxy*radiusPlusHalf.y))));half4 rectEdgeAlphas" -"=saturate(half4(half2(fragCoord-rect.xy),half2(rect.zw-fragCoord)));rectEdgeAlphas" +"=saturate(half4(half2(coords-rect.xy),half2(rect.zw-coords)));rectEdgeAlphas" "=mix(rectEdgeAlphas,half4(1.),edgeSelect);half alpha=(((circleCornerAlpha*rectEdgeAlphas" ".x)*rectEdgeAlphas.y)*rectEdgeAlphas.z)*rectEdgeAlphas.w;alpha=radiusPlusHalf" -".x<0.?1.-alpha:alpha;return alpha.xxxx;}$pure float $tile(int tileMode,float" -" f,float low,float high){switch(tileMode){case 0:return clamp(f,low,high);case" -" 1:{float length=high-low;return mod(f-low,length)+low;}case 2:{float length" -"=high-low;float length2=2.*length;float tmp=mod(f-low,length2);return mix(tmp" -",length2-tmp,step(length,tmp))+low;}default:return f;}}$pure half4 $sample_image" -"(float2 pos,float2 invImgSize,sampler2D s){return sample(s,pos*invImgSize);" -"}$pure half4 $sample_image_subset(float2 pos,float2 invImgSize,float4 subset" -",int tileModeX,int tileModeY,int filterMode,float2 linearFilterInset,sampler2D" -" s){if(tileModeX==$kTileModeDecal&&filterMode==$kFilterModeNearest){float snappedX" -"=floor(pos.x)+.5;if(snappedXsubset.z)return half4(0.);}" -"if(tileModeY==$kTileModeDecal&&filterMode==$kFilterModeNearest){float snappedY" -"=floor(pos.y)+.5;if(snappedYsubset.w)return half4(0.);}" -"pos.x=$tile(tileModeX,pos.x,subset.x,subset.z);pos.y=$tile(tileModeY,pos.y," -"subset.y,subset.w);float4 insetClamp;if(filterMode==$kFilterModeNearest)insetClamp" -"=float4(floor(subset.xy)+$kLinearInset,ceil(subset.zw)-$kLinearInset);else insetClamp" +".x<0.?1.-alpha:alpha;return alpha.xxxx;}$pure half4 sk_analytic_and_atlas_clip" +"(float2 coords,float4 rect,float2 radiusPlusHalf,half4 edgeSelect,float2 texCoordOffset" +",float4 maskBounds,float2 invAtlasSize,sampler2D atlasSampler){half4 analyticClip" +"=sk_analytic_clip(coords,rect,radiusPlusHalf,edgeSelect);float2 texCoord=coords" +"+texCoordOffset;float2 clampedTexCoord=clamp(texCoord,maskBounds.xy,maskBounds" +".zw);half atlasClip=sample(atlasSampler,clampedTexCoord*invAtlasSize).x;return" +" analyticClip*atlasClip;}$pure float $tile(int tileMode,float f,float low,float" +" high){switch(tileMode){case 0:return clamp(f,low,high);case 1:{float length" +"=high-low;return mod(f-low,length)+low;}case 2:{float length=high-low;float" +" length2=2.*length;float tmp=mod(f-low,length2);return mix(tmp,length2-tmp," +"step(length,tmp))+low;}default:return f;}}$pure half4 $sample_image(float2 pos" +",float2 invImgSize,sampler2D s){return sample(s,pos*invImgSize);}$pure half4" +" $sample_image_subset(float2 pos,float2 invImgSize,float4 subset,int tileModeX" +",int tileModeY,int filterMode,float2 linearFilterInset,sampler2D s){if(tileModeX" +"==$kTileModeDecal&&filterMode==$kFilterModeNearest){float snappedX=floor(pos" +".x)+.5;if(snappedXsubset.z)return half4(0.);}if(tileModeY" +"==$kTileModeDecal&&filterMode==$kFilterModeNearest){float snappedY=floor(pos" +".y)+.5;if(snappedYsubset.w)return half4(0.);}pos.x=$tile" +"(tileModeX,pos.x,subset.x,subset.z);pos.y=$tile(tileModeY,pos.y,subset.y,subset" +".w);float4 insetClamp;if(filterMode==$kFilterModeNearest)insetClamp=float4(" +"floor(subset.xy)+$kLinearInset,ceil(subset.zw)-$kLinearInset);else insetClamp" "=float4(subset.xy+linearFilterInset.x,subset.zw-linearFilterInset.y);float2" " clampedPos=clamp(pos,insetClamp.xy,insetClamp.zw);half4 color=$sample_image" "(clampedPos,invImgSize,s);if(filterMode==$kFilterModeLinear){half2 error=half2" @@ -81,104 +99,107 @@ static constexpr char SKSL_MINIFIED_sksl_graphite_frag[] = "return color;}$pure half4 sk_image_shader(float2 coords,float2 invImgSize,float4" " subset,int tileModeX,int tileModeY,int filterMode,sampler2D s){return $sample_image_subset" "(coords,invImgSize,subset,tileModeX,tileModeY,filterMode,.50001.xx,s);}$pure" -" half4 sk_cubic_image_shader(float2 coords,float2 invImgSize,float4 subset," -"int tileModeX,int tileModeY,half4x4 cubicCoeffs,sampler2D s){return $cubic_filter_image" -"(coords,invImgSize,subset,tileModeX,tileModeY,cubicCoeffs,s);}$pure half4 sk_hw_image_shader" -"(float2 coords,float2 invImgSize,sampler2D s){return $sample_image(coords,invImgSize" -",s);}$pure half4 $yuv_to_rgb_no_swizzle(half Y,half U,half V,half alpha,half3x3" -" yuvToRGBMatrix,half3 yuvToRGBTranslate){half3 preColor=half3(Y,U,V);half4 sampleColor" -";sampleColor.xyz=saturate(yuvToRGBMatrix*preColor+yuvToRGBTranslate);sampleColor" -".w=alpha;return sampleColor;}$pure half4 $yuv_to_rgb(half4 sampleColorY,half4" -" sampleColorU,half4 sampleColorV,half alpha,half4 channelSelectY,half4 channelSelectU" -",half4 channelSelectV,half3x3 yuvToRGBMatrix,half3 yuvToRGBTranslate){half Y" -"=dot(channelSelectY,sampleColorY);half U=dot(channelSelectU,sampleColorU);half" -" V=dot(channelSelectV,sampleColorV);return $yuv_to_rgb_no_swizzle(Y,U,V,alpha" -",yuvToRGBMatrix,yuvToRGBTranslate);}$pure half4 sk_yuv_image_shader(float2 coords" -",float2 invImgSizeY,float2 invImgSizeUV,float4 subset,float2 linearFilterUVInset" -",int tileModeX,int tileModeY,int filterModeY,int filterModeUV,half4 channelSelectY" -",half4 channelSelectU,half4 channelSelectV,half4 channelSelectA,half3x3 yuvToRGBMatrix" -",half3 yuvToRGBTranslate,sampler2D sY,sampler2D sU,sampler2D sV,sampler2D sA" -"){if(filterModeY!=filterModeUV)coords=floor(coords)+.5;int tileModeX_UV=tileModeX" +" half4 sk_image_shader_clamp(float2 coords,float2 invImgSize,float4 subsetInsetClamp" +",sampler2D s){return $sample_image(clamp(coords,subsetInsetClamp.xy,subsetInsetClamp" +".zw),invImgSize,s);}$pure half4 sk_cubic_image_shader(float2 coords,float2 invImgSize" +",float4 subset,int tileModeX,int tileModeY,half4x4 cubicCoeffs,sampler2D s)" +"{return $cubic_filter_image(coords,invImgSize,subset,tileModeX,tileModeY,cubicCoeffs" +",s);}$pure half4 sk_hw_image_shader(float2 coords,float2 invImgSize,sampler2D" +" s){return $sample_image(coords,invImgSize,s);}$pure half4 $yuv_to_rgb_no_swizzle" +"(half Y,half U,half V,half alpha,half3x3 yuvToRGBMatrix,half3 yuvToRGBTranslate" +"){half3 preColor=half3(Y,U,V);half4 sampleColor;sampleColor.xyz=saturate(yuvToRGBMatrix" +"*preColor+yuvToRGBTranslate);sampleColor.w=alpha;return sampleColor;}$pure half4" +" $yuv_to_rgb(half4 sampleColorY,half4 sampleColorU,half4 sampleColorV,half alpha" +",half4 channelSelectY,half4 channelSelectU,half4 channelSelectV,half3x3 yuvToRGBMatrix" +",half3 yuvToRGBTranslate){half Y=dot(channelSelectY,sampleColorY);half U=dot" +"(channelSelectU,sampleColorU);half V=dot(channelSelectV,sampleColorV);return" +" $yuv_to_rgb_no_swizzle(Y,U,V,alpha,yuvToRGBMatrix,yuvToRGBTranslate);}$pure" +" half4 sk_yuv_image_shader(float2 coords,float2 invImgSizeY,float2 invImgSizeUV" +",float4 subset,float2 linearFilterUVInset,int tileModeX,int tileModeY,int filterModeY" +",int filterModeUV,half4 channelSelectY,half4 channelSelectU,half4 channelSelectV" +",half4 channelSelectA,half3x3 yuvToRGBMatrix,half3 yuvToRGBTranslate,sampler2D" +" sY,sampler2D sU,sampler2D sV,sampler2D sA){if(filterModeY!=filterModeUV)coords" +"=floor(coords)+.5;int tileModeX_UV=tileModeX==$kTileModeDecal?$kTileModeClamp" +":tileModeX;int tileModeY_UV=tileModeY==$kTileModeDecal?$kTileModeClamp:tileModeY" +";half4 sampleColorY;half4 sampleColorU;half4 sampleColorV;sampleColorY=$sample_image_subset" +"(coords,invImgSizeY,subset,tileModeX,tileModeY,filterModeY,.50001.xx,sY);sampleColorU" +"=$sample_image_subset(coords,invImgSizeUV,subset,tileModeX_UV,tileModeY_UV," +"filterModeUV,linearFilterUVInset,sU);sampleColorV=$sample_image_subset(coords" +",invImgSizeUV,subset,tileModeX_UV,tileModeY_UV,filterModeUV,linearFilterUVInset" +",sV);half alpha;if(channelSelectA==half4(1.))alpha=1.;else{half4 sampleColorA" +"=$sample_image_subset(coords,invImgSizeY,subset,tileModeX,tileModeY,filterModeY" +",.50001.xx,sA);alpha=dot(channelSelectA,sampleColorA);}return $yuv_to_rgb(sampleColorY" +",sampleColorU,sampleColorV,alpha,channelSelectY,channelSelectU,channelSelectV" +",yuvToRGBMatrix,yuvToRGBTranslate);}$pure half4 sk_cubic_yuv_image_shader(float2" +" coords,float2 invImgSizeY,float2 invImgSizeUV,float4 subset,int tileModeX," +"int tileModeY,half4x4 cubicCoeffs,half4 channelSelectY,half4 channelSelectU" +",half4 channelSelectV,half4 channelSelectA,half3x3 yuvToRGBMatrix,half3 yuvToRGBTranslate" +",sampler2D sY,sampler2D sU,sampler2D sV,sampler2D sA){int tileModeX_UV=tileModeX" "==$kTileModeDecal?$kTileModeClamp:tileModeX;int tileModeY_UV=tileModeY==$kTileModeDecal" "?$kTileModeClamp:tileModeY;half4 sampleColorY;half4 sampleColorU;half4 sampleColorV" -";sampleColorY=$sample_image_subset(coords,invImgSizeY,subset,tileModeX,tileModeY" -",filterModeY,.50001.xx,sY);sampleColorU=$sample_image_subset(coords,invImgSizeUV" -",subset,tileModeX_UV,tileModeY_UV,filterModeUV,linearFilterUVInset,sU);sampleColorV" -"=$sample_image_subset(coords,invImgSizeUV,subset,tileModeX_UV,tileModeY_UV," -"filterModeUV,linearFilterUVInset,sV);half alpha;if(channelSelectA==half4(1." -"))alpha=1.;else{half4 sampleColorA=$sample_image_subset(coords,invImgSizeY," -"subset,tileModeX,tileModeY,filterModeY,.50001.xx,sA);alpha=dot(channelSelectA" +";sampleColorY=$cubic_filter_image(coords,invImgSizeY,subset,tileModeX,tileModeY" +",cubicCoeffs,sY);sampleColorU=$cubic_filter_image(coords,invImgSizeUV,subset" +",tileModeX_UV,tileModeY_UV,cubicCoeffs,sU);sampleColorV=$cubic_filter_image" +"(coords,invImgSizeUV,subset,tileModeX_UV,tileModeY_UV,cubicCoeffs,sV);half alpha" +";if(channelSelectA==half4(1.))alpha=1.;else{half4 sampleColorA=$cubic_filter_image" +"(coords,invImgSizeY,subset,tileModeX,tileModeY,cubicCoeffs,sA);alpha=dot(channelSelectA" ",sampleColorA);}return $yuv_to_rgb(sampleColorY,sampleColorU,sampleColorV,alpha" ",channelSelectY,channelSelectU,channelSelectV,yuvToRGBMatrix,yuvToRGBTranslate" -");}$pure half4 sk_cubic_yuv_image_shader(float2 coords,float2 invImgSizeY,float2" -" invImgSizeUV,float4 subset,int tileModeX,int tileModeY,half4x4 cubicCoeffs" -",half4 channelSelectY,half4 channelSelectU,half4 channelSelectV,half4 channelSelectA" -",half3x3 yuvToRGBMatrix,half3 yuvToRGBTranslate,sampler2D sY,sampler2D sU,sampler2D" -" sV,sampler2D sA){int tileModeX_UV=tileModeX==$kTileModeDecal?$kTileModeClamp" -":tileModeX;int tileModeY_UV=tileModeY==$kTileModeDecal?$kTileModeClamp:tileModeY" -";half4 sampleColorY;half4 sampleColorU;half4 sampleColorV;sampleColorY=$cubic_filter_image" -"(coords,invImgSizeY,subset,tileModeX,tileModeY,cubicCoeffs,sY);sampleColorU" -"=$cubic_filter_image(coords,invImgSizeUV,subset,tileModeX_UV,tileModeY_UV,cubicCoeffs" -",sU);sampleColorV=$cubic_filter_image(coords,invImgSizeUV,subset,tileModeX_UV" -",tileModeY_UV,cubicCoeffs,sV);half alpha;if(channelSelectA==half4(1.))alpha" -"=1.;else{half4 sampleColorA=$cubic_filter_image(coords,invImgSizeY,subset,tileModeX" -",tileModeY,cubicCoeffs,sA);alpha=dot(channelSelectA,sampleColorA);}return $yuv_to_rgb" -"(sampleColorY,sampleColorU,sampleColorV,alpha,channelSelectY,channelSelectU" -",channelSelectV,yuvToRGBMatrix,yuvToRGBTranslate);}$pure half4 sk_hw_yuv_image_shader" -"(float2 coords,float2 invImgSizeY,float2 invImgSizeUV,half4 channelSelectY," -"half4 channelSelectU,half4 channelSelectV,half4 channelSelectA,half3x3 yuvToRGBMatrix" -",half3 yuvToRGBTranslate,sampler2D sY,sampler2D sU,sampler2D sV,sampler2D sA" -"){half4 sampleColorY;half4 sampleColorU;half4 sampleColorV;sampleColorY=$sample_image" -"(coords,invImgSizeY,sY);sampleColorU=$sample_image(coords,invImgSizeUV,sU);" -"sampleColorV=$sample_image(coords,invImgSizeUV,sV);half alpha;if(channelSelectA" -"==half4(1.))alpha=1.;else{half4 sampleColorA=$sample_image(coords,invImgSizeY" -",sA);alpha=dot(channelSelectA,sampleColorA);}return $yuv_to_rgb(sampleColorY" -",sampleColorU,sampleColorV,alpha,channelSelectY,channelSelectU,channelSelectV" -",yuvToRGBMatrix,yuvToRGBTranslate);}$pure half4 sk_hw_yuv_no_swizzle_image_shader" -"(float2 coords,float2 invImgSizeY,float2 invImgSizeUV,half3x3 yuvToRGBMatrix" -",half4 yuvToRGBXlateAlphaParam,sampler2D sY,sampler2D sU,sampler2D sV,sampler2D" -" sA){half Y=$sample_image(coords,invImgSizeY,sY).x;half U=$sample_image(coords" -",invImgSizeUV,sU).x;half V=$sample_image(coords,invImgSizeUV,sV).x;half alpha" -"=saturate($sample_image(coords,invImgSizeY,sA).x+yuvToRGBXlateAlphaParam.w)" -";return $yuv_to_rgb_no_swizzle(Y,U,V,alpha,yuvToRGBMatrix,yuvToRGBXlateAlphaParam" -".xyz);}$pure half4 sk_dither(half4 colorIn,half range,sampler2D lut){half value" -"=sample(lut,sk_FragCoord.xy*.125).x-.5;return half4(clamp(colorIn.xyz+value" -"*range,0.,colorIn.w),colorIn.w);}$pure float2 $tile_grad(int tileMode,float2" -" t){switch(tileMode){case 0:t.x=saturate(t.x);break;case 1:t.x=fract(t.x);break" -";case 2:{float t_1=t.x-1.;t.x=(t_1-2.*floor(t_1*.5))-1.;if(sk_Caps.mustDoOpBetweenFloorAndAbs" -")t.x=clamp(t.x,-1.,1.);t.x=abs(t.x);break;}case 3:if(t.x<0.||t.x>1.)return float2" -"(0.,-1.);break;}return t;}$pure half4 $colorize_grad_4(float4[4]colorsParam" -",float4 offsetsParam,float2 t){if(t.y<0.)return half4(0.);else if(t.x<=offsetsParam" -".x)return half4(colorsParam[0]);else if(t.x1.)return float2(0.,-1.);break;}return t;}$pure" +" half4 $colorize_grad_4(float4[4]colorsParam,float4 offsetsParam,float2 t){" +"if(t.y<0.)return half4(0.);else if(t.x<=offsetsParam.x)return half4(colorsParam" +"[0]);else if(t.x2.5;float4 N=M?G.yzwx:H.yzwx;float4" -" O=M?H.yzwx:-G.yzwx;G=mix(N,G,J);H=mix(O,H,J);I=mix(I.yzwx,I,J);B=mix(B.yzwx" -",B,J);if(!M&&w==0.){L*=float2(J[D],J.yzwx[D]);K=(J-1.)*o.x;o.y=1.;w=1.;}}float4" -" M=inversesqrt(I);G*=M;H*=M;float2 N=-float2(G.yzwx[D],H.yzwx[D]);float2 O=" -"float2(G[D],H[D]);float2 P;bool Q=false;if(c<0.)if(h.w<0.||d*h.z!=0.)Q=true" -";else{float R=h.w;float2 S=E+(x?-L:L);if(w==1.||any(lessThanEqual(S,R.xx)))" -"P=S-R;else P=S*a-R*b;}else P=(E+L)*(a+w*a.yx);if(Q)P=h.xy;else{P-=E;P=(float2" -"(z[D],A[D])+N*P.x)+O*P.y;}l=(H*(z-P.x)-G*(A-P.y))+K;float3x3 R=inverse(j);float3" -" S=j*float3(P,1.);k=float4(R[0].xy-R[0].z*P,R[1].xy-R[1].z*P);if(y){float4 T" -"=-H*(R[0].x-R[0].z*z)+G*(R[0].y-R[0].z*A);float4 U=-H*(R[1].x-R[1].z*z)+G*(" -"R[1].y-R[1].z*A);l*=inversesqrt(T*T+U*U);l+=(1.-B)*abs(S.z);bool V=B==1..xxxx" -"&&dot(abs(G*G.yzwx+H*H.yzwx),1..xxxx)<.00024;if(V){float2 W=l.xy+l.zw;p.y=1." -"+min(min(W.x,W.y),abs(S.z));}else p.y=1.+abs(S.z);}if(c>0.&&S.z>0.){float2x2" -" T=float2x2(k);float2 U=float2(B[D],B.yzwx[D])*b;float2 V=((F.x*U.x)*perp(-" -"O))*T;float2 W=((F.y*U.y)*perp(N))*T;bool X=all(notEqual(U,0..xx));if(w==1." -"&&X){V=normalize(V);W=normalize(W);if(dot(V,W)<-.8){float Y=sign(cross_length_2d" -"(V,W));V=Y*perp(V);W=-Y*perp(W);}}S.xy+=S.z*normalize(V+W);if(y)l-=S.z;else" -" p.y=-S.z;}else if(!y)p.y=0.;p.x=float(d!=0.?1.:(x?-1.:0.));if(C)k=float4(float2x2" -"(H.x,-H.y,-G.x,G.y)*float2x2(k));q=P;return float4(S.xy,S.z*i,S.z);}float4 per_edge_aa_quad_vertex_fn" +".xx))){w=.414213568;F=E.yx;}float4 G=z-z.wxyz;float4 H=A-A.wxyz;float4 I=1." +"/max(abs(G),max(abs(H),1..xxxx));G*=I;H*=I;float4 J=G*G+H*H;float4 K=sign(J" +");float4 L=0..xxxx;float2 M=o.x.xx;if(any(equal(K,0..xxxx)))if(all(equal(K," +"0..xxxx))){G=float4(0.,1.,0.,-1.);H=float4(-1.,0.,1.,0.);J=1..xxxx;}else{bool" +" N=((K.x+K.y)+K.z)+K.w>2.5;float4 O=N?G.yzwx:H.yzwx;float4 P=N?H.yzwx:-G.yzwx" +";G=mix(O,G,K);H=mix(P,H,K);J=mix(J.yzwx,J,K);B=mix(B.yzwx,B,K);if(!N&&w==0." +"){M*=float2(K[D],K.yzwx[D]);L=(K-1.)*o.x;o.y=1.;w=1.;}}float4 N=inversesqrt" +"(J);G*=N;H*=N;float2 O=-float2(G.yzwx[D],H.yzwx[D]);float2 P=float2(G[D],H[" +"D]);float2 Q;bool R=false;if(c<0.)if(h.w<0.||d*h.z!=0.)R=true;else{float S=" +"h.w;float2 T=E+(x?-M:M);if(w==1.||any(lessThanEqual(T,S.xx)))Q=T-S;else Q=T" +"*a-S*b;}else Q=(E+M)*(a+w*a.yx);if(R)Q=h.xy;else{Q-=E;Q=(float2(z[D],A[D])+" +"O*Q.x)+P*Q.y;}l=(H*(z-Q.x)-G*(A-Q.y))+L;float3x3 S=inverse(j);float3 T=j*float3" +"(Q,1.);k=float4(S[0].xy-S[0].z*Q,S[1].xy-S[1].z*Q);if(y){float4 U=-H*(S[0]." +"x-S[0].z*z)+G*(S[0].y-S[0].z*A);float4 V=-H*(S[1].x-S[1].z*z)+G*(S[1].y-S[1" +"].z*A);l*=inversesqrt(U*U+V*V);l+=(1.-B)*abs(T.z);bool W=B==1..xxxx&&dot(abs" +"(G*G.yzwx+H*H.yzwx),1..xxxx)<.00024;if(W){float2 X=l.xy+l.zw;p.y=1.+min(min" +"(X.x,X.y),abs(T.z));}else p.y=1.+abs(T.z);}if(c>0.&&T.z>0.){float2x2 U=float2x2" +"(k);float2 V=float2(B[D],B.yzwx[D])*b;float2 W=((F.x*V.x)*perp(-P))*U;float2" +" X=((F.y*V.y)*perp(O))*U;bool Y=all(notEqual(V,0..xx));if(w==1.&&Y){W=normalize" +"(W);X=normalize(X);if(dot(W,X)<-.8){float Z=sign(cross_length_2d(W,X));W=Z*" +"perp(W);X=-Z*perp(X);}}T.xy+=T.z*normalize(W+X);if(y)l-=T.z;else p.y=-T.z;}" +"else if(!y)p.y=0.;p.x=float(d!=0.?1.:(x?-1.:0.));if(C)k=float4(float2x2(H.x" +",-H.y,-G.x,G.y)*float2x2(k));q=Q;return float4(T.xy,T.z*i,T.z);}float4 per_edge_aa_quad_vertex_fn" "(float2 a,float4 b,float4 c,float4 d,float e,float3x3 f,out float4 g,out float2" -" h){float4 k=c-c.wxyz;float4 l=d-d.wxyz;float4 m=k*k+l*l;float4 n=sign(m);if" -"(any(equal(n,0..xxxx)))if(all(equal(n,0..xxxx))){k=float4(0.,1.,0.,-1.);l=float4" -"(-1.,0.,1.,0.);m=1..xxxx;}else{bool o=((n.x+n.y)+n.z)+n.w>2.5;float4 p=o?k." -"yzwx:l.yzwx;float4 q=o?l.yzwx:-k.yzwx;k=mix(p,k,n);l=mix(q,l,n);m=mix(m.yzwx" -",m,n);b=mix(b.yzwx,b,n);}float4 o=inversesqrt(m);k*=o;l*=o;uint p=uint(sk_VertexID" -")/4;float2 q=-float2(k.yzwx[p],l.yzwx[p]);float2 r=float2(k[p],l[p]);float2" -" s=float2(c[p],d[p]);g=l*(c-s.x)-k*(d-s.y);float3x3 t=inverse(f);float3 u=f" -"*float3(s,1.);float4 v=-l*(t[0].x-t[0].z*c)+k*(t[0].y-t[0].z*d);float4 w=-l" -"*(t[1].x-t[1].z*c)+k*(t[1].y-t[1].z*d);g*=inversesqrt(v*v+w*w);g+=(1.5-b)*abs" -"(u.z);if(any(notEqual(a,0..xx))&&u.z>0.){float2x2 x=float2x2(t[0].xy-t[0].z" -"*s,t[1].xy-t[1].z*s);float2 y=float2(b[p],b.yzwx[p])*a;float2 z=(y.x*perp(-" -"r))*x;float2 A=(y.y*perp(q))*x;bool B=all(notEqual(y,0..xx));if(B){z=normalize" -"(z);A=normalize(A);if(dot(z,A)<-.8){float C=sign(cross_length_2d(z,A));z=C*" -"perp(z);A=-C*perp(A);}}u.xy+=u.z*normalize(z+A);g-=u.z;}h=s;return float4(u" -".xy,u.z*e,u.z);}float4 circular_arc_vertex_fn(float3 a,float4 b,float3 c,float3" -" d,float3 e,float3 f,float4 g,float h,float3x3 i,out float4 j,out float3 k," -"out float3 l,out float3 m,out float n,out float4 o,out float2 p){float2 q=b" -".xy;float2 r=q;float s=min(dot(a.xy,d.xy)+d.z,0.);a.xy-=d.xy*s;float t=length" -"(a.xy);if(a.z>0.)r+=a.xy*b.z;else r+=a.xy*b.w;float3 u=i*float3(r,1.);float3" -" v=i*float3(q,1.);float2 w=u.xy-v.xy;if(w!=0..xx){w=normalize(w);u.xy+=a.z*" -"w;if(a.z>0.)w*=t;else w*=t*c.y;}j=float4(w,c.xy);if(c.z>0.){k=e;l=f;m=0..xxx" -";}else{k=e;l=float3(0.,0.,1.);m=f;}if(abs(c.z)>1.)n=(1.-c.y)*.5;else n=0.;o" -"=g;p=r;return float4(u.xy,h,1.);}float4 text_vertex_fn(float2 a,float4x4 b," -"float4x4 c,float2 d,float2 e,float2 f,float2 g,float h,float i,out float2 j" -",out float2 k,out float2 l){a*=e;float2 m=h*a+g;float4 n=b*float4(m,0.,1.);" -"l=(c*n).xy;k=a+f;j=k*d;return float4(n.xy,i*n.w,n.w);}float4 coverage_mask_vertex_fn" -"(float2 a,float3x3 b,float4 c,float4 d,float2 e,float f,float3x3 g,out float4" -" h,out float2 i,out half j,out float2 k){i=mix(c.xy,c.zw,a);float3 l=b*float3" -"(i+e,1.);float3 m=g*l;k=m.xy/m.z;if(all(lessThanEqual(d.xy,d.zw))){h=d;j=0." -";}else{h=d.zwxy;j=1.;}return float4(l.xy,f*l.z,l.z);}float4 cover_bounds_vertex_fn" -"(float2 a,float4 b,float c,float3x3 d,out float2 e){if(all(lessThanEqual(b." -"xy,b.zw))){a=mix(b.xy,b.zw,a);float3 f=d*float3(a,1.);e=a;return float4(f.xy" -",c*f.z,f.z);}else{a=mix(b.zw,b.xy,a);float3 f=inverse(d)*float3(a,1.);float" -" g=1./f.z;e=f.xy*g;return float4(a*g,c*g,g);}}"; +" h){float4 k=c-c.wxyz;float4 l=d-d.wxyz;float4 m=1./max(abs(k),max(abs(l),1." +".xxxx));k*=m;l*=m;float4 n=k*k+l*l;float4 o=sign(n);if(any(equal(o,0..xxxx)" +"))if(all(equal(o,0..xxxx))){k=float4(0.,1.,0.,-1.);l=float4(-1.,0.,1.,0.);n" +"=1..xxxx;}else{bool p=((o.x+o.y)+o.z)+o.w>2.5;float4 q=p?k.yzwx:l.yzwx;float4" +" r=p?l.yzwx:-k.yzwx;k=mix(q,k,o);l=mix(r,l,o);n=mix(n.yzwx,n,o);b=mix(b.yzwx" +",b,o);}float4 p=inversesqrt(n);k*=p;l*=p;uint q=uint(sk_VertexID)/4;float2 r" +"=-float2(k.yzwx[q],l.yzwx[q]);float2 s=float2(k[q],l[q]);float2 t=float2(c[" +"q],d[q]);g=l*(c-t.x)-k*(d-t.y);float3x3 u=inverse(f);float3 v=f*float3(t,1." +");float4 w=-l*(u[0].x-u[0].z*c)+k*(u[0].y-u[0].z*d);float4 x=-l*(u[1].x-u[1" +"].z*c)+k*(u[1].y-u[1].z*d);g*=inversesqrt(w*w+x*x);g+=(1.5-b)*abs(v.z);if(any" +"(notEqual(a,0..xx))&&v.z>0.){float2x2 y=float2x2(u[0].xy-u[0].z*t,u[1].xy-u" +"[1].z*t);float2 z=float2(b[q],b.yzwx[q])*a;float2 A=(z.x*perp(-s))*y;float2" +" B=(z.y*perp(r))*y;bool C=all(notEqual(z,0..xx));if(C){A=normalize(A);B=normalize" +"(B);if(dot(A,B)<-.8){float D=sign(cross_length_2d(A,B));A=D*perp(A);B=-D*perp" +"(B);}}v.xy+=v.z*normalize(A+B);g-=v.z;}h=t;return float4(v.xy,v.z*e,v.z);}float4" +" circular_arc_vertex_fn(float3 a,float4 b,float3 c,float3 d,float3 e,float3" +" f,float4 g,float h,float3x3 i,out float4 j,out float3 k,out float3 l,out float3" +" m,out float n,out float4 o,out float2 p){float2 q=b.xy;float2 r=q;float s=" +"min(dot(a.xy,d.xy)+d.z,0.);a.xy-=d.xy*s;float t=length(a.xy);if(a.z>0.)r+=a" +".xy*b.z;else r+=a.xy*b.w;float3 u=i*float3(r,1.);float3 v=i*float3(q,1.);float2" +" w=u.xy-v.xy;if(w!=0..xx){w=normalize(w);u.xy+=a.z*w;if(a.z>0.)w*=t;else w*=" +"t*c.y;}j=float4(w,c.xy);if(c.z>0.){k=e;l=f;m=0..xxx;}else{k=e;l=float3(0.,0." +",1.);m=f;}if(abs(c.z)>1.)n=(1.-c.y)*.5;else n=0.;o=g;p=r;return float4(u.xy" +",h,1.);}float4 text_vertex_fn(float2 a,float4x4 b,float4x4 c,float2 d,float2" +" e,float2 f,float2 g,float h,float i,out float2 j,out float2 k,out float2 l" +"){a*=e;float2 m=h*a+g;float4 n=b*float4(m,0.,1.);l=(c*n).xy;k=a+f;j=k*d;return" +" float4(n.xy,i*n.w,n.w);}float4 coverage_mask_vertex_fn(float2 a,float3x3 b" +",float4 c,float4 d,float2 e,float f,float3x3 g,out float4 h,out float2 i,out" +" half j,out float2 k){i=mix(c.xy,c.zw,a);float3 l=b*float3(i+e,1.);float3 m" +"=g*l;k=m.xy/m.z;if(all(lessThanEqual(d.xy,d.zw))){h=d;j=0.;}else{h=d.zwxy;j" +"=1.;}return float4(l.xy,f*l.z,l.z);}float4 cover_bounds_vertex_fn(float2 a," +"float4 b,float c,float3x3 d,out float2 e){if(all(lessThanEqual(b.xy,b.zw)))" +"{a=mix(b.xy,b.zw,a);float3 f=d*float3(a,1.);e=a;return float4(f.xy,c*f.z,f." +"z);}else{a=mix(b.zw,b.xy,a);float3 f=inverse(d)*float3(a,1.);float g=1./f.z" +";e=f.xy*g;return float4(a*g,c*g,g);}}"; diff --git a/gfx/skia/skia/src/sksl/generated/sksl_graphite_vert.unoptimized.sksl b/gfx/skia/skia/src/sksl/generated/sksl_graphite_vert.unoptimized.sksl index 1e9c5aa450b1..5f6354f69a46 100644 --- a/gfx/skia/skia/src/sksl/generated/sksl_graphite_vert.unoptimized.sksl +++ b/gfx/skia/skia/src/sksl/generated/sksl_graphite_vert.unoptimized.sksl @@ -135,7 +135,8 @@ static constexpr char SKSL_MINIFIED_sksl_graphite_vert[] = "=uint(sk_VertexID)/kCornerVertexCount;float2 cornerRadii=float2(xRadii[cornerID" "],yRadii[cornerID]);if(cornerID%2!=0)cornerRadii=cornerRadii.yx;float2 cornerAspectRatio" "=1..xx;if(all(greaterThan(cornerRadii,0..xx))){joinScale=kRoundScale;cornerAspectRatio" -"=cornerRadii.yx;}float4 dx=xs-xs.wxyz;float4 dy=ys-ys.wxyz;float4 edgeSquaredLen" +"=cornerRadii.yx;}float4 dx=xs-xs.wxyz;float4 dy=ys-ys.wxyz;float4 invMag=1." +"/max(abs(dx),max(abs(dy),1..xxxx));dx*=invMag;dy*=invMag;float4 edgeSquaredLen" "=dx*dx+dy*dy;float4 edgeMask=sign(edgeSquaredLen);float4 edgeBias=0..xxxx;float2" " strokeRadius=strokeParams.x.xx;if(any(equal(edgeMask,0..xxxx)))if(all(equal" "(edgeMask,0..xxxx))){dx=float4(0.,1.,0.,-1.);dy=float4(-1.,0.,1.,0.);edgeSquaredLen" @@ -180,8 +181,9 @@ static constexpr char SKSL_MINIFIED_sksl_graphite_vert[] = ",devPos.z);}float4 per_edge_aa_quad_vertex_fn(float2 normal,float4 edgeAA,float4" " xs,float4 ys,float depth,float3x3 localToDevice,out float4 edgeDistances,out" " float2 stepLocalCoords){const uint kCornerVertexCount=4;float4 dx=xs-xs.wxyz" -";float4 dy=ys-ys.wxyz;float4 edgeSquaredLen=dx*dx+dy*dy;float4 edgeMask=sign" -"(edgeSquaredLen);if(any(equal(edgeMask,0..xxxx)))if(all(equal(edgeMask,0..xxxx" +";float4 dy=ys-ys.wxyz;float4 invMag=1./max(abs(dx),max(abs(dy),1..xxxx));dx" +"*=invMag;dy*=invMag;float4 edgeSquaredLen=dx*dx+dy*dy;float4 edgeMask=sign(" +"edgeSquaredLen);if(any(equal(edgeMask,0..xxxx)))if(all(equal(edgeMask,0..xxxx" "))){dx=float4(0.,1.,0.,-1.);dy=float4(-1.,0.,1.,0.);edgeSquaredLen=1..xxxx;" "}else{bool triangle=((edgeMask.x+edgeMask.y)+edgeMask.z)+edgeMask.w>2.5;float4" " edgeX=triangle?dx.yzwx:dy.yzwx;float4 edgeY=triangle?dy.yzwx:-dx.yzwx;dx=mix" diff --git a/gfx/skia/skia/src/sksl/ir/SkSLType.cpp b/gfx/skia/skia/src/sksl/ir/SkSLType.cpp index cd469a608432..342e68307d45 100644 --- a/gfx/skia/skia/src/sksl/ir/SkSLType.cpp +++ b/gfx/skia/skia/src/sksl/ir/SkSLType.cpp @@ -1306,7 +1306,8 @@ bool Type::checkForOutOfRangeLiteral(const Context& context, const Expression& e if (baseType.isNumber()) { // Replace constant expressions with their corresponding values. const Expression* valueExpr = ConstantFolder::GetConstantValueForVariable(expr); - if (valueExpr->supportsConstantValues()) { + // Unsized arrays can't have constants and fails to get a slotCount. + if (valueExpr->supportsConstantValues() && !valueExpr->type().isUnsizedArray()) { // Iterate over every constant subexpression in the value. int numSlots = valueExpr->type().slotCount(); for (int slot = 0; slot < numSlots; ++slot) { diff --git a/gfx/skia/skia/src/sksl/sksl_graphite_frag.sksl b/gfx/skia/skia/src/sksl/sksl_graphite_frag.sksl index c1b6a75b4e45..f59641708b4f 100644 --- a/gfx/skia/skia/src/sksl/sksl_graphite_frag.sksl +++ b/gfx/skia/skia/src/sksl/sksl_graphite_frag.sksl @@ -8,18 +8,6 @@ const int $kTileModeDecal = 3; const int $kFilterModeNearest = 0; const int $kFilterModeLinear = 1; -const int $kTFTypeSRGB = 1; -const int $kTFTypePQ = 2; -const int $kTFTypeHLG = 3; -const int $kTFTypeHLGinv = 4; - -const int $kColorSpaceXformFlagUnpremul = 0x1; -const int $kColorSpaceXformFlagLinearize = 0x2; -const int $kColorSpaceXformFlagGamutTransform = 0x4; -const int $kColorSpaceXformFlagEncode = 0x8; -const int $kColorSpaceXformFlagPremul = 0x10; -const int $kColorSpaceXformFlagAlphaSwizzle = 0x20; - const int $kMaskFormatA8 = 0; const int $kMaskFormatA565 = 1; const int $kMaskFormatARGB = 2; @@ -52,93 +40,196 @@ $pure half4 sk_alpha_only(float4 colorParam) { return half4(0.0, 0.0, 0.0, colorParam.a); } -$pure float $apply_xfer_fn(int kind, float x, half4 cs[2]) { - float G = cs[0][0], A = cs[0][1], B = cs[0][2], C = cs[0][3], - D = cs[1][0], E = cs[1][1], F = cs[1][2]; - float s = sign(x); - x = abs(x); - switch (kind) { - case $kTFTypeSRGB: - x = (x < D) ? (C * x) + F - : pow(A * x + B, G) + E; - break; - case $kTFTypePQ: - float x_C = pow(x, C); - x = pow(max(A + B * x_C, 0) / (D + E * x_C), F); - break; - case $kTFTypeHLG: - x = (x * A <= 1) ? pow(x * A, B) - : exp((x - E) * C) + D; - x *= (F + 1); - break; - case $kTFTypeHLGinv: - x /= (F + 1); - x = (x <= 1) ? A * pow(x, B) - : C * log(x - D) + E; - break; +$pure float3 $apply_srgb_xfer_fn(float3 x, half4 gabc, half3 def) { + return mix(pow(gabc[1] * x + gabc[2], float3(gabc[0])) + def[1], + (gabc[3] * x) + def[2], + lessThan(x, float3(def[0]))); +} + +$pure float3 $apply_pq_xfer_fn(float3 x, half3 abc, half3 def) { + float3 x_C = pow(x, float3(abc[2])); + return pow(max(abc[0] + abc[1] * x_C, 0) / (def[0] + def[1] * x_C), float3(def[2])); +} + +$pure float3 $apply_hlg_xfer_fn(float3 x, half3 abc, half3 def) { + return (def[2] + 1) * mix(exp((x - def[1]) * abc[2]) + def[0], + pow(x * abc[0], float3(abc[1])), + lessThanEqual(x * abc[0], float3(1))); +} + +$pure float3 $apply_hlg_inv_xfer_fn(float3 x, half3 abc, half3 def) { + x /= (def[2] + 1); + return mix(abc[2] * log(x - def[0]) + def[1], + abc[0] * pow(x, float3(abc[1])), + lessThanEqual(x, float3(1))); +} + +$pure half4 sk_color_space_transform(half4 color, + half3x3 gamut, + half4 srcGABC, + half4 srcDEF_args, + half4 dstGABC, + half4 dstDEF_args) { + // To encode whether to do premul/unpremul or make the output opaque, we use + // srcDEF_args.w and dstDEF_args.w: + // - identity: {0, 1} + // - do unpremul: {-1, 1} + // - do premul: {0, 0} + // - do both: {-1, 0} + // - alpha swizzle 1: {1, 1} + // - alpha swizzle r: {1, 0} + // + // This depends on the assumption that if we're doing an alpha swizzle, we're not premultiplying + // or unpremultiplying, so the alpha swizzle cases are mutually exclusive with the premul cases. + + if (srcDEF_args.w < 0) { + color = unpremul(color); + + } else { + // Alpha swizzle cases; we set alpha = {a, r, 1} . {1 - x, x - x * y, x * y}. + // Given the encoded inputs described above, and ignoring the do unpremul case, we have: + // 1. No-op ({x, y} = {0, 1} or {0, 0}) -> {a, r, 1} . {1, 0, 0} = a + // 2. Use R ({x, y} = {1, 0}) -> {a, r, 1} . {0, 1, 0} = r + // 3. Use 1 ({x, y} = {1, 1}) -> {a, r, 1} . {0, 0, 1} = 1 + half alphaSwizzleA = 1.0 - srcDEF_args.w; + half alphaSwizzle1 = srcDEF_args.w * dstDEF_args.w; + half alphaSwizzleR = srcDEF_args.w - alphaSwizzle1; + color.a = dot(color.ar1, half3(alphaSwizzleA, alphaSwizzleR, alphaSwizzle1)); } - return s * x; -} -$pure half4 sk_premul_alpha(float4 color) { - return half4(color.rgb*color.a, color.a); -} - -$pure half4 sk_color_space_transform(half4 halfColor, - int flags, - int srcKind, - half3x3 gamutTransform, - int dstKind, - half4x4 coeffs) { - if (flags != 0) { - float4 color = float4(halfColor); - - if (bool(flags & $kColorSpaceXformFlagAlphaSwizzle)) { - color.a = dot(color.r1, float2(coeffs[1][3], coeffs[3][3])); - } - if (bool(flags & $kColorSpaceXformFlagUnpremul)) { - color = unpremul(color); - } - if (bool(flags & $kColorSpaceXformFlagLinearize)) { - half4 srcCoeffs[2]; - srcCoeffs[0] = coeffs[0]; - srcCoeffs[1] = coeffs[1]; - color.r = $apply_xfer_fn(srcKind, color.r, srcCoeffs); - color.g = $apply_xfer_fn(srcKind, color.g, srcCoeffs); - color.b = $apply_xfer_fn(srcKind, color.b, srcCoeffs); - } - if (bool(flags & $kColorSpaceXformFlagGamutTransform)) { - color.rgb = gamutTransform * color.rgb; - } - if (bool(flags & $kColorSpaceXformFlagEncode)) { - half4 dstCoeffs[2]; - dstCoeffs[0] = coeffs[2]; - dstCoeffs[1] = coeffs[3]; - color.r = $apply_xfer_fn(dstKind, color.r, dstCoeffs); - color.g = $apply_xfer_fn(dstKind, color.g, dstCoeffs); - color.b = $apply_xfer_fn(dstKind, color.b, dstCoeffs); - } - - halfColor = bool(flags & $kColorSpaceXformFlagPremul) ? sk_premul_alpha(color) - : half4(color); + // To encode which transfer function to apply, we use the src and dst gamma values: + // - identity: 0 + // - sRGB: g > 0 + // - PQ: -2 + // - HLG: -1 + float3 colorF = float3(color.rgb); + if (srcGABC.x > 0) { + colorF = sign(colorF) * $apply_srgb_xfer_fn(abs(colorF), srcGABC, srcDEF_args.xyz); + } else if (srcGABC.x < -1 ) { + colorF = sign(colorF) * $apply_pq_xfer_fn(abs(colorF), srcGABC.yzw, srcDEF_args.xyz); + } else if (srcGABC.x < 0) { + colorF = sign(colorF) * $apply_hlg_xfer_fn(abs(colorF), srcGABC.yzw, srcDEF_args.xyz); } - return halfColor; + + colorF = gamut * colorF; + + if (dstGABC.x > 0) { + colorF = sign(colorF) * $apply_srgb_xfer_fn(abs(colorF), dstGABC, dstDEF_args.xyz); + } else if (dstGABC.x < -1 ) { + colorF = sign(colorF) * $apply_pq_xfer_fn(abs(colorF), dstGABC.yzw, dstDEF_args.xyz); + } else if (dstGABC.x < 0) { + colorF = sign(colorF) * $apply_hlg_inv_xfer_fn(abs(colorF), dstGABC.yzw, dstDEF_args.xyz); + } + + // Premul cases: + // 1. No-op (noPremul = 1) -> max(...) = 1.0 -> identity function + // 2. Premultiply (noPremul = 0) -> max(...) = color.a -> premul + // + // "noPremul" is 0 in the premul case, but also in the alpha-swizzle-r case, but the only time + // we swizzle r to alpha is for a read swizzle of 000R, in which case the RGB channels are zero + // and it doesn't matter what we multiply them by here. + half noPremul = dstDEF_args.w; + color.rgb = half3(colorF.rgb) * max(color.a, noPremul); + + return color; } -$pure half4 sk_circular_rrect_clip(float4 rect, - float2 radiusPlusHalf, - half4 edgeSelect) { - float2 fragCoord = sk_FragCoord.xy; +$pure half4 sk_color_space_transform_premul(half4 color, half2 args) { + // This shader can either do nothing, or perform one of three actions. These four possibilities + // are encoded in a half2 argument as: + // - identity: {0, 1} + // - do unpremul: {-1, 1} + // - do premul: {0, 0} + // - make opaque: {1, 1} + if (args.x < 0) { + // Unpremul case (mutually exclusive with other cases, and costly). + color = unpremul(color); + } else { + // The other cases (identity, premul, make opaque) are branchless. + half opaque = args.x; + half noPremul = args.y; + + // Opaque cases: + // 1. No-op (opaque = 0) -> max(...) = color.a -> identity function + // 2. Make opaque (opaque = 1) -> max(...) = 1.0 -> make opaque + color.a = max(color.a, opaque); + + // Premul cases: + // 1. No-op (noPremul = 1) -> max(...) = 1.0 -> identity function + // 2. Premultiply (noPremul = 0) -> max(...) = color.a -> premul + color.rgb = color.rgb * max(color.a, noPremul); + } + + return color; +} + +$pure half4 sk_color_space_transform_srgb(half4 color, + half3x3 gamut, + half4 srcGABC, + half4 srcDEF_args, + half4 dstGABC, + half4 dstDEF_args) { + // To encode whether to do premul/unpremul or make the output opaque, we use + // srcDEF_args.w and dstDEF_args.w: + // - identity: {0, 1} + // - do unpremul: {-1, 1} + // - do premul: {0, 0} + // - do both: {-1, 0} + // - alpha swizzle 1: {1, 1} + // - alpha swizzle r: {1, 0} + // + // This depends on the assumption that if we're doing an alpha swizzle, we're not premultiplying + // or unpremultiplying, so the alpha swizzle cases are mutually exclusive with the premul cases. + + if (srcDEF_args.w < 0) { + color = unpremul(color); + + } else { + // Alpha swizzle cases; we set alpha = {a, r, 1} . {1 - x, x - x * y, x * y}. + // Given the encoded inputs described above, and ignoring the do unpremul cases, we have: + // 1. No-op ({x, y} = {0, 1} or {0, 0}) -> {a, r, 1} . {1, 0, 0} = a + // 2. Use R ({x, y} = {1, 0}) -> {a, r, 1} . {0, 1, 0} = r + // 3. Use 1 ({x, y} = {1, 1}) -> {a, r, 1} . {0, 0, 1} = 1 + half alphaSwizzleA = 1.0 - srcDEF_args.w; + half alphaSwizzle1 = srcDEF_args.w * dstDEF_args.w; + half alphaSwizzleR = srcDEF_args.w - alphaSwizzle1; + color.a = dot(color.ar1, half3(alphaSwizzleA, alphaSwizzleR, alphaSwizzle1)); + } + + float3 colorF = float3(color.rgb); + colorF = sign(colorF) * $apply_srgb_xfer_fn(abs(colorF), srcGABC, srcDEF_args.xyz); + + colorF = gamut * colorF; + + colorF = sign(colorF) * $apply_srgb_xfer_fn(abs(colorF), dstGABC, dstDEF_args.xyz); + + // Premul cases: + // 1. No-op (noPremul = 1) -> max(...) = 1.0 -> identity function + // 2. Premultiply (noPremul = 0) -> max(...) = color.a -> premul + // + // "noPremul" is 0 in the premul case, but also in the alpha-swizzle-r case, but the only time + // we swizzle r to alpha is for a read swizzle of 000R, in which case the RGB channels are zero + // and it doesn't matter what we multiply them by here. + half noPremul = dstDEF_args.w; + color.rgb = half3(colorF.rgb) * max(color.a, noPremul); + + return color; +} + +$pure half4 sk_analytic_clip(float2 coords, + float4 rect, + float2 radiusPlusHalf, + half4 edgeSelect) { // Negative x indicates inverse fill float2 radius = float2(abs(radiusPlusHalf.x)); - float2 dxy0 = edgeSelect.LT*((rect.LT + radius) - fragCoord); - float2 dxy1 = edgeSelect.RB*(fragCoord - (rect.RB - radius)); + float2 dxy0 = edgeSelect.LT*((rect.LT + radius) - coords); + float2 dxy1 = edgeSelect.RB*(coords - (rect.RB - radius)); float2 dxy = max(max(dxy0, dxy1), 0.0); // Use more complex length calculation to manage precision issues half circleCornerAlpha = half(saturate(radius.x*(1.0 - length(dxy*radiusPlusHalf.y)))); - half4 rectEdgeAlphas = saturate(half4(fragCoord - rect.LT, rect.RB - fragCoord)); + half4 rectEdgeAlphas = saturate(half4(coords - rect.LT, rect.RB - coords)); rectEdgeAlphas = mix(rectEdgeAlphas, half4(1), edgeSelect); half alpha = circleCornerAlpha * rectEdgeAlphas.x * rectEdgeAlphas.y * @@ -149,6 +240,24 @@ $pure half4 sk_circular_rrect_clip(float4 rect, return half4(alpha); } +$pure half4 sk_analytic_and_atlas_clip(float2 coords, + float4 rect, + float2 radiusPlusHalf, + half4 edgeSelect, + float2 texCoordOffset, + float4 maskBounds, + float2 invAtlasSize, + sampler2D atlasSampler) { + half4 analyticClip = sk_analytic_clip(coords, rect, radiusPlusHalf, edgeSelect); + + float2 texCoord = coords + texCoordOffset; + float2 clampedTexCoord = clamp(texCoord, maskBounds.LT, maskBounds.RB); + + half atlasClip = sample(atlasSampler, clampedTexCoord * invAtlasSize).r; + + return analyticClip * atlasClip; +} + $pure float $tile(int tileMode, float f, float low, float high) { switch (tileMode) { case $kTileModeClamp: @@ -307,6 +416,13 @@ $pure half4 sk_image_shader(float2 coords, filterMode, float2($kLinearInset), s); } +$pure half4 sk_image_shader_clamp(float2 coords, + float2 invImgSize, + float4 subsetInsetClamp, + sampler2D s) { + return $sample_image(clamp(coords, subsetInsetClamp.xy, subsetInsetClamp.zw), invImgSize, s); +} + $pure half4 sk_cubic_image_shader(float2 coords, float2 invImgSize, float4 subset, @@ -1175,7 +1291,7 @@ $pure half4 sk_perlin_noise_shader(float2 coords, color = saturate(color); // Pre-multiply the result - return sk_premul_alpha(color); + return half4(color.rgb * color.a, color.a); } $pure half4 sk_porter_duff_blend(half4 src, half4 dst, half4 coeffs) { diff --git a/gfx/skia/skia/src/sksl/sksl_graphite_vert.sksl b/gfx/skia/skia/src/sksl/sksl_graphite_vert.sksl index e11b79053ad9..05b32d8f683c 100644 --- a/gfx/skia/skia/src/sksl/sksl_graphite_vert.sksl +++ b/gfx/skia/skia/src/sksl/sksl_graphite_vert.sksl @@ -641,6 +641,12 @@ float4 analytic_rrect_vertex_fn(// Vertex Attributes // the +X/+Y normalized vertex template for each corner. float4 dx = xs - xs.wxyz; float4 dy = ys - ys.wxyz; + // This is a specialized application of `robust_normalized_diff` for a quad, with an extra + // max against 1.0 to not scale small edges. This is to avoid overflows for extremely large + // coordinates when squaring dx or dy. + float4 invMag = 1.0 / max(abs(dx), max(abs(dy), float4(1.0))); + dx *= invMag; + dy *= invMag; float4 edgeSquaredLen = dx*dx + dy*dy; float4 edgeMask = sign(edgeSquaredLen); // 0 for zero-length edge, 1 for non-zero edge. @@ -857,6 +863,12 @@ float4 per_edge_aa_quad_vertex_fn(// Vertex Attributes // the +X/+Y normalized vertex template for each corner. float4 dx = xs - xs.wxyz; float4 dy = ys - ys.wxyz; + // This is a specialized application of `robust_normalized_diff` for a quad, with an extra + // max against 1.0 to not scale small edges. This is to avoid overflows for extremely large + // coordinates when squaring dx or dy. + float4 invMag = 1.0 / max(abs(dx), max(abs(dy), float4(1.0))); + dx *= invMag; + dy *= invMag; float4 edgeSquaredLen = dx*dx + dy*dy; float4 edgeMask = sign(edgeSquaredLen); // 0 for zero-length edge, 1 for non-zero edge. diff --git a/gfx/skia/skia/src/sksl/tracing/SkSLDebugTracePriv.cpp b/gfx/skia/skia/src/sksl/tracing/SkSLDebugTracePriv.cpp index 12b929522c60..7f1ba6c01040 100644 --- a/gfx/skia/skia/src/sksl/tracing/SkSLDebugTracePriv.cpp +++ b/gfx/skia/skia/src/sksl/tracing/SkSLDebugTracePriv.cpp @@ -4,23 +4,16 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ - #include "src/sksl/tracing/SkSLDebugTracePriv.h" -#include "include/core/SkData.h" -#include "include/core/SkRefCnt.h" #include "include/core/SkStream.h" #include "include/core/SkTypes.h" -#include "src/core/SkStreamPriv.h" #include "src/sksl/ir/SkSLType.h" #include #include #include #include -#include - -static constexpr char kTraceVersion[] = "20220209"; namespace SkSL { @@ -197,10 +190,4 @@ void DebugTracePriv::dump(SkWStream* o) const { } } -void DebugTracePriv::writeTrace(SkWStream* w) const { -} - -bool DebugTracePriv::readTrace(SkStream* r) { -} - } // namespace SkSL diff --git a/gfx/skia/skia/src/sksl/tracing/SkSLDebugTracePriv.h b/gfx/skia/skia/src/sksl/tracing/SkSLDebugTracePriv.h index 0d407b755603..d5fe9ff95c55 100644 --- a/gfx/skia/skia/src/sksl/tracing/SkSLDebugTracePriv.h +++ b/gfx/skia/skia/src/sksl/tracing/SkSLDebugTracePriv.h @@ -19,7 +19,6 @@ #include #include -class SkStream; class SkWStream; namespace SkSL { @@ -69,10 +68,6 @@ public: /** Attaches the SkSL source to be debugged. */ void setSource(const std::string& source); - /** Serializes a debug trace to JSON which can be parsed by our debugger. */ - bool readTrace(SkStream* r); - void writeTrace(SkWStream* w) const override; - /** Generates a human-readable dump of the debug trace. */ void dump(SkWStream* o) const override; diff --git a/gfx/skia/skia/src/text/gpu/StrikeCache.cpp b/gfx/skia/skia/src/text/gpu/StrikeCache.cpp index 4caf3e113764..add3127c92fd 100644 --- a/gfx/skia/skia/src/text/gpu/StrikeCache.cpp +++ b/gfx/skia/skia/src/text/gpu/StrikeCache.cpp @@ -14,6 +14,7 @@ #include "src/core/SkReadBuffer.h" #include "src/core/SkStrikeCache.h" #include "src/core/SkStrikeSpec.h" +#include "src/core/SkTraceEvent.h" #include "src/text/StrikeForGPU.h" #include "src/text/gpu/Glyph.h" @@ -97,6 +98,9 @@ size_t StrikeCache::internalPurge(size_t minBytesNeeded) { return 0; } + TRACE_EVENT2_ALWAYS("skia.gpu.cache", "StrikeCache::internalPurge", + "totalMemoryUsed", fTotalMemoryUsed, "cacheCount", fCacheCount); + size_t bytesFreed = 0; int countFreed = 0; diff --git a/gfx/skia/skia/src/text/gpu/SubRunContainer.cpp b/gfx/skia/skia/src/text/gpu/SubRunContainer.cpp index c3a9cddd633d..0f0104362396 100644 --- a/gfx/skia/skia/src/text/gpu/SubRunContainer.cpp +++ b/gfx/skia/skia/src/text/gpu/SubRunContainer.cpp @@ -62,8 +62,8 @@ #if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) #include "include/core/SkRRect.h" -#include "include/private/SkColorData.h" #include "include/private/gpu/ganesh/GrTypesPriv.h" +#include "src/core/SkColorData.h" #include "src/core/SkPaintPriv.h" #include "src/gpu/ganesh/GrClip.h" #include "src/gpu/ganesh/GrColorInfo.h" diff --git a/gfx/skia/skia/src/text/gpu/TextBlob.cpp b/gfx/skia/skia/src/text/gpu/TextBlob.cpp index 8ec4e36740aa..143210b65c54 100644 --- a/gfx/skia/skia/src/text/gpu/TextBlob.cpp +++ b/gfx/skia/skia/src/text/gpu/TextBlob.cpp @@ -11,9 +11,9 @@ #include "include/core/SkPoint.h" #include "include/core/SkRect.h" #include "include/core/SkScalar.h" -#include "include/private/SkColorData.h" #include "include/private/base/SkAssert.h" #include "include/private/base/SkCPUTypes.h" +#include "src/core/SkColorData.h" #include "src/core/SkDevice.h" #include "src/core/SkFontPriv.h" #include "src/core/SkMaskFilterBase.h" diff --git a/gfx/skia/skia/src/utils/SkBitSet.h b/gfx/skia/skia/src/utils/SkBitSet.h index ddaa56a3f593..24291ead843f 100644 --- a/gfx/skia/skia/src/utils/SkBitSet.h +++ b/gfx/skia/skia/src/utils/SkBitSet.h @@ -38,7 +38,7 @@ public: ~SkBitSet() = default; /** Basic equality checks. */ - bool operator==(const SkBitSet& that) { + bool operator==(const SkBitSet& that) const { if (fSize != that.fSize) { return false; } @@ -46,7 +46,7 @@ public: return 0 == memcmp(fChunks.get(), that.fChunks.get(), sizeof(Chunk) * numChunks); } - bool operator!=(const SkBitSet& that) { + bool operator!=(const SkBitSet& that) const { return !this->operator==(that); } diff --git a/gfx/skia/skia/src/utils/SkCharToGlyphCache.cpp b/gfx/skia/skia/src/utils/SkCharToGlyphCache.cpp index 6d6ecc1376e7..0636d5825803 100644 --- a/gfx/skia/skia/src/utils/SkCharToGlyphCache.cpp +++ b/gfx/skia/skia/src/utils/SkCharToGlyphCache.cpp @@ -14,14 +14,14 @@ SkCharToGlyphCache::SkCharToGlyphCache() { SkCharToGlyphCache::~SkCharToGlyphCache() {} void SkCharToGlyphCache::reset() { - fK32.reset(); - fV16.reset(); + fKUnichar.reset(); + fVGlyph.reset(); // Add sentinels so we can always rely on these to stop linear searches (in either direction) // Neither is a legal unichar, so we don't care what glyphID we use. // - *fK32.append() = 0x80000000; *fV16.append() = 0; - *fK32.append() = 0x7FFFFFFF; *fV16.append() = 0; + *fKUnichar.append() = 0x80000000; *fVGlyph.append() = 0; + *fKUnichar.append() = 0x7FFFFFFF; *fVGlyph.append() = 0; fDenom = 0; } @@ -93,37 +93,37 @@ static int find_with_slope(const SkUnichar base[], int count, SkUnichar value, d } int SkCharToGlyphCache::findGlyphIndex(SkUnichar unichar) const { - const int count = fK32.size(); + const int count = fKUnichar.size(); int index; if (count <= kSmallCountLimit) { - index = find_simple(fK32.begin(), count, unichar); + index = find_simple(fKUnichar.begin(), count, unichar); } else { - index = find_with_slope(fK32.begin(), count, unichar, fDenom); + index = find_with_slope(fKUnichar.begin(), count, unichar, fDenom); } if (index >= 0) { - return fV16[index]; + return fVGlyph[index]; } return index; } void SkCharToGlyphCache::insertCharAndGlyph(int index, SkUnichar unichar, SkGlyphID glyph) { - SkASSERT(fK32.size() == fV16.size()); - SkASSERT(index < fK32.size()); - SkASSERT(unichar < fK32[index]); + SkASSERT(fKUnichar.size() == fVGlyph.size()); + SkASSERT(index < fKUnichar.size()); + SkASSERT(unichar < fKUnichar[index]); - *fK32.insert(index) = unichar; - *fV16.insert(index) = glyph; + *fKUnichar.insert(index) = unichar; + *fVGlyph.insert(index) = glyph; // if we've changed the first [1] or last [count-2] entry, recompute our slope - const int count = fK32.size(); + const int count = fKUnichar.size(); if (count >= kMinCountForSlope && (index == 1 || index == count - 2)) { SkASSERT(index >= 1 && index <= count - 2); - fDenom = 1.0 / ((double)fK32[count - 2] - fK32[1]); + fDenom = 1.0 / ((double)fKUnichar[count - 2] - fKUnichar[1]); } #ifdef SK_DEBUG - for (int i = 1; i < fK32.size(); ++i) { - SkASSERT(fK32[i-1] < fK32[i]); + for (int i = 1; i < fKUnichar.size(); ++i) { + SkASSERT(fKUnichar[i-1] < fKUnichar[i]); } #endif } diff --git a/gfx/skia/skia/src/utils/SkCharToGlyphCache.h b/gfx/skia/skia/src/utils/SkCharToGlyphCache.h index 81371315875d..280122928ef9 100644 --- a/gfx/skia/skia/src/utils/SkCharToGlyphCache.h +++ b/gfx/skia/skia/src/utils/SkCharToGlyphCache.h @@ -12,8 +12,6 @@ #include "include/private/base/SkTDArray.h" #include "include/private/base/SkTo.h" -#include - class SkCharToGlyphCache { public: SkCharToGlyphCache(); @@ -21,7 +19,7 @@ public: // return number of unichars cached int count() const { - return fK32.size(); + return fKUnichar.size(); } void reset(); // forget all cache entries (to save memory) @@ -57,8 +55,8 @@ public: } private: - SkTDArray fK32; - SkTDArray fV16; + SkTDArray fKUnichar; + SkTDArray fVGlyph; double fDenom; }; diff --git a/gfx/skia/skia/src/utils/SkCustomTypeface.cpp b/gfx/skia/skia/src/utils/SkCustomTypeface.cpp index f03fc13602a7..1354ceb10cc9 100644 --- a/gfx/skia/skia/src/utils/SkCustomTypeface.cpp +++ b/gfx/skia/skia/src/utils/SkCustomTypeface.cpp @@ -242,10 +242,10 @@ SkTypeface::LocalizedStrings* SkUserTypeface::onCreateFamilyNameIterator() const class SkUserScalerContext : public SkScalerContext { public: - SkUserScalerContext(sk_sp face, + SkUserScalerContext(SkUserTypeface& face, const SkScalerContextEffects& effects, - const SkDescriptor* desc) - : SkScalerContext(std::move(face), effects, desc) { + const SkDescriptor* desc) + : SkScalerContext(face, effects, desc) { fRec.getSingleMatrix(&fMatrix); this->forceGenerateImageFromPath(); } @@ -360,8 +360,7 @@ private: std::unique_ptr SkUserTypeface::onCreateScalerContext( const SkScalerContextEffects& effects, const SkDescriptor* desc) const { - return std::make_unique( - sk_ref_sp(const_cast(this)), effects, desc); + return std::make_unique(*const_cast(this), effects, desc); } /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/gfx/skia/skia/src/utils/SkFloatUtils.h b/gfx/skia/skia/src/utils/SkFloatUtils.h index f89a77254ec8..15b90c72cdf8 100644 --- a/gfx/skia/skia/src/utils/SkFloatUtils.h +++ b/gfx/skia/skia/src/utils/SkFloatUtils.h @@ -116,7 +116,7 @@ public: const Bits dist = DistanceBetweenSignAndMagnitudeNumbers(fU.bits, rhs.fU.bits); - //SkDEBUGF("(%f, %f, %d) ", u_.value_, rhs.u_.value_, dist); + //SkDEBUGF("(%f, %f, %d) ", fU.value, rhs.fU.value, dist); return dist <= kMaxUlps; } diff --git a/gfx/skia/skia/src/utils/SkJSONWriter.cpp b/gfx/skia/skia/src/utils/SkJSONWriter.cpp new file mode 100644 index 000000000000..3038f2e7cf38 --- /dev/null +++ b/gfx/skia/skia/src/utils/SkJSONWriter.cpp @@ -0,0 +1,47 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +// Make sure that the PRI format string macros are defined +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif + +#include "src/utils/SkJSONWriter.h" + +#include +#include +#include + +void SkJSONWriter::appendS64(int64_t value) { + this->beginValue(); + this->appendf("%" PRId64, value); +} + +void SkJSONWriter::appendU64(uint64_t value) { + this->beginValue(); + this->appendf("%" PRIu64, value); +} + +void SkJSONWriter::appendHexU64(uint64_t value) { + this->beginValue(); + this->appendf("\"0x%" PRIx64 "\"", value); +} + +void SkJSONWriter::appendf(const char* fmt, ...) { + const int kBufferSize = 1024; + char buffer[kBufferSize]; + va_list argp; + va_start(argp, fmt); +#ifdef SK_BUILD_FOR_WIN + int length = _vsnprintf_s(buffer, kBufferSize, _TRUNCATE, fmt, argp); +#else + int length = vsnprintf(buffer, kBufferSize, fmt, argp); +#endif + SkASSERT(length >= 0 && length < kBufferSize); + va_end(argp); + this->write(buffer, length); +} diff --git a/gfx/skia/skia/src/utils/SkJSONWriter.h b/gfx/skia/skia/src/utils/SkJSONWriter.h new file mode 100644 index 000000000000..0bc46ef073c0 --- /dev/null +++ b/gfx/skia/skia/src/utils/SkJSONWriter.h @@ -0,0 +1,419 @@ +/* + * 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 SkJSONWriter_DEFINED +#define SkJSONWriter_DEFINED + +#include "include/core/SkStream.h" +#include "include/core/SkString.h" +#include "include/core/SkTypes.h" +#include "include/private/base/SkNoncopyable.h" +#include "include/private/base/SkTArray.h" +#include "src/base/SkUTF.h" + +#include +#include +#include +#include + +/** + * Lightweight class for writing properly structured JSON data. No random-access, everything must + * be generated in-order. The resulting JSON is written directly to the SkWStream supplied at + * construction time. Output is buffered, so writing to disk (via an SkFILEWStream) is ideal. + * + * There is a basic state machine to ensure that JSON is structured correctly, and to allow for + * (optional) pretty formatting. + * + * This class adheres to the RFC-4627 usage of JSON (not ECMA-404). In other words, all JSON + * created with this class must have a top-level object or array. Free-floating values of other + * types are not considered valid. + * + * Note that all error checking is in the form of asserts - invalid usage in a non-debug build + * will simply produce invalid JSON. + */ +class SkJSONWriter : SkNoncopyable { +public: + enum class Mode { + /** + * Output the minimal amount of text. No additional whitespace (including newlines) is + * generated. The resulting JSON is suitable for fast parsing and machine consumption. + */ + kFast, + + /** + * Output human-readable JSON, with indented objects and arrays, and one value per line. + * Slightly slower than kFast, and produces data that is somewhat larger. + */ + kPretty + }; + + /** + * Construct a JSON writer that will serialize all the generated JSON to 'stream'. + */ + SkJSONWriter(SkWStream* stream, Mode mode = Mode::kFast) + : fBlock(new char[kBlockSize]) + , fWrite(fBlock) + , fBlockEnd(fBlock + kBlockSize) + , fStream(stream) + , fMode(mode) + , fState(State::kStart) { + fScopeStack.push_back(Scope::kNone); + fNewlineStack.push_back(true); + } + + ~SkJSONWriter() { + this->flush(); + delete[] fBlock; + SkASSERT(fScopeStack.size() == 1); + SkASSERT(fNewlineStack.size() == 1); + } + + /** + * Force all buffered output to be flushed to the underlying stream. + */ + void flush() { + if (fWrite != fBlock) { + fStream->write(fBlock, fWrite - fBlock); + fWrite = fBlock; + } + } + + /** + * Append the name (key) portion of an object member. Must be called between beginObject() and + * endObject(). If you have both the name and value of an object member, you can simply call + * the two argument versions of the other append functions. + */ + void appendName(const char* name) { + if (!name) { + return; + } + SkASSERT(Scope::kObject == this->scope()); + SkASSERT(State::kObjectBegin == fState || State::kObjectValue == fState); + if (State::kObjectValue == fState) { + this->write(",", 1); + } + this->separator(this->multiline()); + this->write("\"", 1); + this->write(name, strlen(name)); + this->write("\":", 2); + fState = State::kObjectName; + } + + /** + * Adds a new object. A name must be supplied when called between beginObject() and + * endObject(). Calls to beginObject() must be balanced by corresponding calls to endObject(). + * By default, objects are written out with one named value per line (when in kPretty mode). + * This can be overridden for a particular object by passing false for multiline, this will + * keep the entire object on a single line. This can help with readability in some situations. + * In kFast mode, this parameter is ignored. + */ + void beginObject(const char* name = nullptr, bool multiline = true) { + this->appendName(name); + this->beginValue(true); + this->write("{", 1); + fScopeStack.push_back(Scope::kObject); + fNewlineStack.push_back(multiline); + fState = State::kObjectBegin; + } + + /** + * Ends an object that was previously started with beginObject(). + */ + void endObject() { + SkASSERT(Scope::kObject == this->scope()); + SkASSERT(State::kObjectBegin == fState || State::kObjectValue == fState); + bool emptyObject = State::kObjectBegin == fState; + bool wasMultiline = this->multiline(); + this->popScope(); + if (!emptyObject) { + this->separator(wasMultiline); + } + this->write("}", 1); + } + + /** + * Adds a new array. A name must be supplied when called between beginObject() and + * endObject(). Calls to beginArray() must be balanced by corresponding calls to endArray(). + * By default, arrays are written out with one value per line (when in kPretty mode). + * This can be overridden for a particular array by passing false for multiline, this will + * keep the entire array on a single line. This can help with readability in some situations. + * In kFast mode, this parameter is ignored. + */ + void beginArray(const char* name = nullptr, bool multiline = true) { + this->appendName(name); + this->beginValue(true); + this->write("[", 1); + fScopeStack.push_back(Scope::kArray); + fNewlineStack.push_back(multiline); + fState = State::kArrayBegin; + } + + /** + * Ends an array that was previous started with beginArray(). + */ + void endArray() { + SkASSERT(Scope::kArray == this->scope()); + SkASSERT(State::kArrayBegin == fState || State::kArrayValue == fState); + bool emptyArray = State::kArrayBegin == fState; + bool wasMultiline = this->multiline(); + this->popScope(); + if (!emptyArray) { + this->separator(wasMultiline); + } + this->write("]", 1); + } + + /** + * Functions for adding values of various types. The single argument versions add un-named + * values, so must be called either + * - Between beginArray() and endArray() -or- + * - Between beginObject() and endObject(), after calling appendName() + */ + void appendString(const char* value, size_t size) { + this->beginValue(); + this->write("\"", 1); + if (value) { + char const * const end = value + size; + while (value < end) { + char const * next = value; + SkUnichar u = SkUTF::NextUTF8(&next, end); + switch (u) { + case '"': this->write("\\\"", 2); break; + case '\\': this->write("\\\\", 2); break; + case '\b': this->write("\\b", 2); break; + case '\f': this->write("\\f", 2); break; + case '\n': this->write("\\n", 2); break; + case '\r': this->write("\\r", 2); break; + case '\t': this->write("\\t", 2); break; + default: { + if (u < 0) { + next = value + 1; + SkString s("\\u"); + s.appendHex((unsigned char)*value, 4); + this->write(s.c_str(), s.size()); + } else if (u < 0x20) { + SkString s("\\u"); + s.appendHex(u, 4); + this->write(s.c_str(), s.size()); + } else { + this->write(value, next - value); + } + } break; + } + value = next; + } + } + this->write("\"", 1); + } + void appendString(const SkString& value) { + this->appendString(value.c_str(), value.size()); + } + // Avoid the non-explicit converting constructor from char* + template ,bool> = false> + void appendString(const T& value) { + this->appendString(value.data(), value.size()); + } + template inline void appendNString(char const (&value)[N]) { + static_assert(N > 0); + this->appendString(value, N-1); + } + void appendCString(const char* value) { + this->appendString(value, value ? strlen(value) : 0); + } + + void appendPointer(const void* value) { this->beginValue(); this->appendf("\"%p\"", value); } + void appendBool(bool value) { + this->beginValue(); + if (value) { + this->write("true", 4); + } else { + this->write("false", 5); + } + } + void appendS32(int32_t value) { this->beginValue(); this->appendf("%d", value); } + void appendS64(int64_t value); + void appendU32(uint32_t value) { this->beginValue(); this->appendf("%u", value); } + void appendU64(uint64_t value); + void appendFloat(float value) { this->beginValue(); this->appendf("%g", value); } + void appendDouble(double value) { this->beginValue(); this->appendf("%g", value); } + void appendFloatDigits(float value, int digits) { + this->beginValue(); + this->appendf("%.*g", digits, value); + } + void appendDoubleDigits(double value, int digits) { + this->beginValue(); + this->appendf("%.*g", digits, value); + } + void appendHexU32(uint32_t value) { this->beginValue(); this->appendf("\"0x%x\"", value); } + void appendHexU64(uint64_t value); + + void appendString(const char* name, const char* value, size_t size) { + this->appendName(name); + this->appendString(value, size); + } + void appendString(const char* name, const SkString& value) { + this->appendName(name); + this->appendString(value.c_str(), value.size()); + } + // Avoid the non-explicit converting constructor from char* + template ,bool> = false> + void appendString(const char* name, const T& value) { + this->appendName(name); + this->appendString(value.data(), value.size()); + } + template inline void appendNString(const char* name, char const (&value)[N]) { + static_assert(N > 0); + this->appendName(name); + this->appendString(value, N-1); + } + void appendCString(const char* name, const char* value) { + this->appendName(name); + this->appendString(value, value ? strlen(value) : 0); + } +#define DEFINE_NAMED_APPEND(function, type) \ + void function(const char* name, type value) { this->appendName(name); this->function(value); } + + /** + * Functions for adding named values of various types. These add a name field, so must be + * called between beginObject() and endObject(). + */ + DEFINE_NAMED_APPEND(appendPointer, const void *) + DEFINE_NAMED_APPEND(appendBool, bool) + DEFINE_NAMED_APPEND(appendS32, int32_t) + DEFINE_NAMED_APPEND(appendS64, int64_t) + DEFINE_NAMED_APPEND(appendU32, uint32_t) + DEFINE_NAMED_APPEND(appendU64, uint64_t) + DEFINE_NAMED_APPEND(appendFloat, float) + DEFINE_NAMED_APPEND(appendDouble, double) + DEFINE_NAMED_APPEND(appendHexU32, uint32_t) + DEFINE_NAMED_APPEND(appendHexU64, uint64_t) + +#undef DEFINE_NAMED_APPEND + + void appendFloatDigits(const char* name, float value, int digits) { + this->appendName(name); + this->appendFloatDigits(value, digits); + } + void appendDoubleDigits(const char* name, double value, int digits) { + this->appendName(name); + this->appendDoubleDigits(value, digits); + } + +private: + enum { + // Using a 32k scratch block gives big performance wins, but we diminishing returns going + // any larger. Even with a 1MB block, time to write a large (~300 MB) JSON file only drops + // another ~10%. + kBlockSize = 32 * 1024, + }; + + enum class Scope { + kNone, + kObject, + kArray + }; + + enum class State { + kStart, + kEnd, + kObjectBegin, + kObjectName, + kObjectValue, + kArrayBegin, + kArrayValue, + }; + + void appendf(const char* fmt, ...) SK_PRINTF_LIKE(2, 3); + + void beginValue(bool structure = false) { + SkASSERT(State::kObjectName == fState || + State::kArrayBegin == fState || + State::kArrayValue == fState || + (structure && State::kStart == fState)); + if (State::kArrayValue == fState) { + this->write(",", 1); + } + if (Scope::kArray == this->scope()) { + this->separator(this->multiline()); + } else if (Scope::kObject == this->scope() && Mode::kPretty == fMode) { + this->write(" ", 1); + } + // We haven't added the value yet, but all (non-structure) callers emit something + // immediately, so transition state, to simplify the calling code. + if (!structure) { + fState = Scope::kArray == this->scope() ? State::kArrayValue : State::kObjectValue; + } + } + + void separator(bool multiline) { + if (Mode::kPretty == fMode) { + if (multiline) { + this->write("\n", 1); + for (int i = 0; i < fScopeStack.size() - 1; ++i) { + this->write(" ", 3); + } + } else { + this->write(" ", 1); + } + } + } + + void write(const char* buf, size_t length) { + if (static_cast(fBlockEnd - fWrite) < length) { + // Don't worry about splitting writes that overflow our block. + this->flush(); + } + if (length > kBlockSize) { + // Send particularly large writes straight through to the stream (unbuffered). + fStream->write(buf, length); + } else { + memcpy(fWrite, buf, length); + fWrite += length; + } + } + + Scope scope() const { + SkASSERT(!fScopeStack.empty()); + return fScopeStack.back(); + } + + bool multiline() const { + SkASSERT(!fNewlineStack.empty()); + return fNewlineStack.back(); + } + + void popScope() { + fScopeStack.pop_back(); + fNewlineStack.pop_back(); + switch (this->scope()) { + case Scope::kNone: + fState = State::kEnd; + break; + case Scope::kObject: + fState = State::kObjectValue; + break; + case Scope::kArray: + fState = State::kArrayValue; + break; + default: + SkDEBUGFAIL("Invalid scope"); + break; + } + } + + char* fBlock; + char* fWrite; + char* fBlockEnd; + + SkWStream* fStream; + Mode fMode; + State fState; + skia_private::STArray<16, Scope, true> fScopeStack; + skia_private::STArray<16, bool, true> fNewlineStack; +}; + +#endif diff --git a/gfx/skia/skia/src/utils/SkPatchUtils.cpp b/gfx/skia/skia/src/utils/SkPatchUtils.cpp index efc2d44b8fdc..6611c0c48696 100644 --- a/gfx/skia/skia/src/utils/SkPatchUtils.cpp +++ b/gfx/skia/skia/src/utils/SkPatchUtils.cpp @@ -17,13 +17,13 @@ #include "include/core/SkSize.h" #include "include/core/SkTypes.h" #include "include/core/SkVertices.h" -#include "include/private/SkColorData.h" #include "include/private/base/SkFloatingPoint.h" #include "include/private/base/SkMath.h" #include "include/private/base/SkTPin.h" #include "include/private/base/SkTo.h" #include "src/base/SkArenaAlloc.h" #include "src/base/SkVx.h" +#include "src/core/SkColorData.h" #include "src/core/SkColorSpacePriv.h" #include "src/core/SkConvertPixels.h" #include "src/core/SkGeometry.h" diff --git a/gfx/skia/skia/src/utils/SkShadowTessellator.cpp b/gfx/skia/skia/src/utils/SkShadowTessellator.cpp index 26bb874ac4e2..69e772f7fc77 100644 --- a/gfx/skia/skia/src/utils/SkShadowTessellator.cpp +++ b/gfx/skia/skia/src/utils/SkShadowTessellator.cpp @@ -16,10 +16,10 @@ #include "include/core/SkRect.h" #include "include/core/SkTypes.h" #include "include/core/SkVertices.h" -#include "include/private/SkColorData.h" #include "include/private/base/SkFloatingPoint.h" #include "include/private/base/SkTDArray.h" #include "include/private/base/SkTemplates.h" +#include "src/core/SkColorData.h" #include "src/core/SkDrawShadowInfo.h" #include "src/core/SkGeometry.h" #include "src/core/SkPointPriv.h" diff --git a/gfx/skia/skia/src/utils/SkShadowUtils.cpp b/gfx/skia/skia/src/utils/SkShadowUtils.cpp index e55f74bc22c6..42ee95a353cf 100644 --- a/gfx/skia/skia/src/utils/SkShadowUtils.cpp +++ b/gfx/skia/skia/src/utils/SkShadowUtils.cpp @@ -12,6 +12,7 @@ #include "include/core/SkBlurTypes.h" #include "include/core/SkCanvas.h" #include "include/core/SkColorFilter.h" +#include "include/core/SkM44.h" #include "include/core/SkMaskFilter.h" #include "include/core/SkMatrix.h" #include "include/core/SkPaint.h" @@ -415,12 +416,17 @@ bool draw_shadow(const FACTORY& factory, FindContext context(&path.viewMatrix(), &factory); SkResourceCache::Key* key = nullptr; - AutoSTArray<32 * 4, uint8_t> keyStorage; + constexpr int kMinBytes = 128; + // We need to make this array be of the cache's Key so the memory we create the Key in + // is properly aligned. + AutoSTArray keyStorage; int keyDataBytes = path.keyBytes(); if (keyDataBytes >= 0) { + // Store the key... keyStorage.reset(keyDataBytes + sizeof(SkResourceCache::Key)); key = new (keyStorage.begin()) SkResourceCache::Key(); - path.writeKey((uint32_t*)(keyStorage.begin() + sizeof(*key))); + // ... followed by the bytes from path. + path.writeKey((uint32_t*)(((uint8_t*)keyStorage.begin()) + sizeof(SkResourceCache::Key))); key->init(&kNamespace, resource_cache_shared_id(), keyDataBytes); SkResourceCache::Find(*key, FindVisitor, &context); } @@ -596,7 +602,7 @@ void SkDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) { } SkMatrix viewMatrix = this->localToDevice(); - SkAutoDeviceTransformRestore adr(this, SkMatrix::I()); + SkAutoDeviceTransformRestore adr(this, SkM44()); #if !defined(SK_ENABLE_OPTIMIZE_SIZE) auto drawVertsProc = [this](const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint, @@ -607,8 +613,8 @@ void SkDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) { // change in translation from the cached version. SkAutoDeviceTransformRestore adr( this, - hasPerspective ? SkMatrix::I() - : this->localToDevice() * SkMatrix::Translate(tx, ty)); + hasPerspective ? SkM44() + : this->localToDevice44() * SkM44::Translate(tx, ty)); // The vertex colors for a tesselated shadow polygon are always either opaque black // or transparent and their real contribution to the final blended color is via // their alpha. We can skip expensive per-vertex color conversion for this. @@ -720,7 +726,7 @@ void SkDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) { SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(blurRadius); bool respectCTM = false; paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma, respectCTM)); - this->drawPath(devSpacePath, paint); + this->drawPath(devSpacePath, paint, true); } } @@ -832,14 +838,14 @@ void SkDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) { &shadowMatrix, &radius)) { return; } - SkAutoDeviceTransformRestore adr2(this, shadowMatrix); + SkAutoDeviceTransformRestore adr2(this, SkM44(shadowMatrix)); SkPaint paint; paint.setColor(rec.fSpotColor); SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius); bool respectCTM = false; paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma, respectCTM)); - this->drawPath(path, paint); + this->drawPath(path, paint, false); } } } diff --git a/gfx/skia/skia/src/utils/mac/SkCTFont.cpp b/gfx/skia/skia/src/utils/mac/SkCTFont.cpp index d678374fdd16..4b48787142a3 100644 --- a/gfx/skia/skia/src/utils/mac/SkCTFont.cpp +++ b/gfx/skia/skia/src/utils/mac/SkCTFont.cpp @@ -350,9 +350,17 @@ SkCTFontWeightMapping& SkCTFontGetDataFontWeightMapping() { SkOTTableOS2_V0* os2Table = SkTAddOffset(data->writable_data(), os2TableOffset); + // On macOS 15.0 and later, CoreText will pin a usWeightClass of 0 to 1. + // Instead, get the value at 11 and then when finished project back to 0. + // Cannot use 1-10 as CoreText considers these as 100-1000 as in OS/2 version A. + constexpr int kLowestUsefulWeightClassValue = 11; CGFloat previousWeight = -CGFLOAT_MAX; for (int i = 0; i < 11; ++i) { - os2Table->usWeightClass.value = SkEndian_SwapBE16(i * 100); + if (i == 0) { + os2Table->usWeightClass.value = SkEndian_SwapBE16(kLowestUsefulWeightClassValue); + } else { + os2Table->usWeightClass.value = SkEndian_SwapBE16(i * 100); + } // On macOS 10.14 and earlier it appears that the CFDataGetBytePtr is used somehow in // font caching. Creating a slightly modified font with data at the same address seems @@ -417,6 +425,9 @@ SkCTFontWeightMapping& SkCTFontGetDataFontWeightMapping() { previousWeight = weight; dataFontWeights[i] = weight; } + CGFloat slope = (dataFontWeights[1] - dataFontWeights[0]) / + (100 - kLowestUsefulWeightClassValue); + dataFontWeights[0] = dataFontWeights[1] - (slope * (100 - 0)); selectedDataFontWeights = &dataFontWeights; }); return *selectedDataFontWeights; diff --git a/gfx/skia/skia/src/utils/mac/SkCreateCGImageRef.cpp b/gfx/skia/skia/src/utils/mac/SkCreateCGImageRef.cpp index 903751426ff5..c7d91d8889d6 100644 --- a/gfx/skia/skia/src/utils/mac/SkCreateCGImageRef.cpp +++ b/gfx/skia/skia/src/utils/mac/SkCreateCGImageRef.cpp @@ -12,10 +12,10 @@ #include "include/core/SkColorSpace.h" #include "include/core/SkData.h" #include "include/encode/SkICC.h" -#include "include/private/SkColorData.h" #include "include/private/base/SkMacros.h" #include "include/private/base/SkTo.h" #include "include/utils/mac/SkCGUtils.h" +#include "src/core/SkColorData.h" #include "src/utils/mac/SkUniqueCFRef.h" #include diff --git a/image/test/gtest/TestBlendAnimationFilter.cpp b/image/test/gtest/TestBlendAnimationFilter.cpp index 7291fbc3f640..71e4ca5202e6 100644 --- a/image/test/gtest/TestBlendAnimationFilter.cpp +++ b/image/test/gtest/TestBlendAnimationFilter.cpp @@ -7,7 +7,7 @@ #include "gtest/gtest.h" #include "mozilla/gfx/2D.h" -#include "skia/include/core/SkColorPriv.h" // for SkPMSrcOver +#include "skia/src/core/SkColorPriv.h" // for SkPMSrcOver #include "Common.h" #include "Decoder.h" #include "DecoderFactory.h" diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index c604f64f512e..e24315cb414e 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -2111,7 +2111,7 @@ pref(image.downscale-during-decode.enabled,true) == 1744468-1.html 1744468-1-ref == 1747272-1.html 1747272-1-ref.html == 1750146-1.html 1750146-1-ref.html == 1735265-1.html 1735265-1-ref.html -fuzzy(0-1,0-1000) == 1769082-1.html 1769082-1-ref.html +fuzzy(0-1,0-2000) == 1769082-1.html 1769082-1-ref.html == 1773484.html 1773484-ref.html # The following tests are skipped on Android since Android doesn't deal very @@ -2154,14 +2154,14 @@ skip-if(!cocoaWidget) != 1836024-1.html 1836024-1-notref.html # 1840511. (It passes on macOS and Android. It also passes with software WR, # or when using drawSnapshot.) fuzzy-if(!cocoaWidget&&!Android&&!swgl&&!useDrawSnapshot,1-1,142-142) == 1840511-1.html 1840511-1-ref.html -fuzzy-if(!cocoaWidget&&!Android&&!swgl&&!useDrawSnapshot,1-1,284-309) fuzzy-if(useDrawSnapshot,1-1,4-4) == 1840511-2.html 1840511-1-ref.html +fuzzy-if(!cocoaWidget&&!Android&&!swgl&&!useDrawSnapshot,1-1,284-309) fuzzy-if(useDrawSnapshot,0-1,0-4) == 1840511-2.html 1840511-1-ref.html # This test should render blank, but is reliably fuzzy, per bug 1840747. fuzzy-if(!useDrawSnapshot,18-19,294-322) == 1840747-1.html about:blank # This test has some fine/expected AA-related fuzziness along the edge of a # covered-up red shape. But there's a single bright-red pixel that shines # through, which is unexpected and is responsible for the 255-255 difference # here. That's tracked in bug 1840747. -fuzzy-if(!useDrawSnapshot&&!swgl,254-255,110-121) fuzzy-if(useDrawSnapshot,18-18,93-93) fuzzy-if(swgl,19-19,58-58) == 1841355-1.html about:blank +fuzzy-if(!useDrawSnapshot&&!swgl,254-255,110-121) fuzzy-if(useDrawSnapshot,18-18,86-93) fuzzy-if(swgl,19-19,58-58) == 1841355-1.html about:blank skip-if(((AddressSanitizer||ThreadSanitizer)&>kWidget)||(isDebugBuild&&Android)) fuzzy(0-123,0-1425) == 1878294-1.html 1878294-1-ref.html == 1888941-text-transform-emergency-wrap.html 1888941-text-transform-emergency-wrap-ref.html == 1936259.html 1936259-ref.html